2012-01-21 16 views
28

Estoy desarrollando una pequeña aplicación que lee en páginas html específicas, las reformatea y luego las muestra en una WebView. Si ejecuto mi código en el hilo de la GUI, el impacto en el rendimiento es casi despreciable comparado con simplemente dejar que WebView muestre la página html original. Pero si soy un buen chico y hago lo que me dicen, se supone que debo usar un AsyncTask para ejecutar el código en segundo plano, para no congelar la GUI durante esos 3-5 segundos mi código hace su trabajo . El problema es ... si lo hago, el código tarda más de 10 veces en terminar. Una página demora más de 60 segundos en mostrarse, lo cual es inaceptable.AsyncTask, ¿debe tomarse un castigo de penalización de rendimiento ...?

Rastreando el problema, TraceView me muestra que mi AsyncTask (en prioridad predeterminada) se ejecuta en trozos de aproximadamente 10 ms, alrededor de 4 veces por segundo. Necesito establecer la prioridad de mi subproceso en MAX_PRIORITY para acercarme a los tiempos de carga aceptables, pero aun así toma 3-4 veces más tiempo que cuando ejecuto el hilo de la GUI.

¿Estoy haciendo algo mal o es así como funciona? ¿Y debe funcionar de esta manera ...?

Aquí es código compilables conforme a lo solicitado:

package my.ownpackage.athome; 

import android.app.Activity; 
import android.os.AsyncTask; 
import android.os.Bundle; 
import android.os.StrictMode; 
import android.webkit.WebView; 
import android.webkit.WebViewClient; 

public class AndroidTestActivity extends Activity 
{ 
    WebView webview; 
    //... 

    private class HelloWebViewClient extends WebViewClient 
    { 
     @Override 
     public boolean shouldOverrideUrlLoading(WebView view, String url) 
     { 
      AndroidTestActivity.this.fetch(view, url); 
      return true; 
     } 
    } 

    public void onCreate(Bundle savedInstanceState) 
    { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 

     // To allow to connect to the web and pull down html-files, reset strict mode 
     // see http://stackoverflow.com/questions/8706464/defaulthttpclient-to-androidhttpclient 
     if (android.os.Build.VERSION.SDK_INT > 9) 
     { 
      StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); 
      StrictMode.setThreadPolicy(policy); 
     } 

     // webview init etc... 

     fetch(webview, "http://www.example.com"); 
    } 

    // This one calls either the AsyncTask or does it all manually in the GUI thread 
    public void fetch(WebView view, String url) 
    { 
     //** Use these when run as AsyncTask in background - SLOW! 
     //** Takes 30+ seconds at default thread priority, with MAX_PRIORITY 15+ seconds 
     // AsyncTask<Void, String, String> fx = new FilterX(url, view, this); 
     // fx.execute(); // running as AsyncTask takes roughly ten times longer than just plain load!  

     //** Use these when not running as AsyncTask - FAST! takes ~5 seconds 
     FilterX fx = new FilterX(url, view, this); 
     fx.onPreExecute(); 
     final String str = fx.doInBackground(); 
     fx.onPostExecute(str); 
    } 
} 

class FilterX extends AsyncTask<Void, String, String> 
{ 
    WebView the_view = null; 
    // other stuff... 

    FilterX(final String url, final WebView view, final Activity activity) 
    { 
     the_view = view; 
     // other initialization 
     // same code in both cases 
    } 

    protected void onPreExecute() 
    { 
     // same code in both cases 
    } 

    protected String doInBackground(Void... v) 
    { 
     // same in both cases... 

     return new String(); // just to make it compile 
    } 

    protected void onPostExecute(final String string) 
    { 
     the_view.loadUrl(string); 
     // same in both cases... 
    } 
} 

Para ejecutar exactamente el mismo código en mi clase FiltroX cuando se ejecuta como AsyncTask como cuando se ejecuta en el hilo GUI, me desnudé todas las cosas ProgressBar, y luego obtener los siguientes tiempos:

  • de 30 segundos para cargar una página en la prioridad de subprocesos predeterminado
  • 15+ segundos en cargar una página en MAX_PRIORITY
  • 5+ segundos en cargar una página cuando se ejecuta en el hilo GUI
+3

dónde dice TraceView que está gastando la mayor parte de su tiempo al usar la tarea asíncrona? Pasar de 5 segundos a 60 hace que suene como si algo hubiera salido terriblemente mal al volver a diseñar su código. (publique los fragmentos relevantes según sea necesario) – smith324

+0

No hay un verdadero "cambio de factor" en marcha. Simplemente elimino la llamada para ejecutar() y en su lugar llamo a doInBackground() desde el hilo de la GUI. Cortaré las partes relevantes y te mostraré sus trivialidades y muchas dudas de que sea un problema. – OppfinnarJocke

+0

Nunca respondió mi primera pregunta: ¿dónde dice TraceView que está usando su tiempo? – smith324

Respuesta

36

No es el único que observa este comportamiento. La ralentización por el factor 10 es probablemente el resultado de que Android use un grupo de programas de Linux (clase de programación) para los hilos de prioridad ANTECEDENTES o inferiores. Todos estos hilos tienen que vivir con un 10% de tiempo de CPU en total.

La buena noticia es que no tiene que vivir con la configuración de prioridad de subprocesos de java.lang.Thread. Puede asignar su prioridad Thread a pthread (hilo de Linux) de las definiciones en android.os.Process. Allí, no solo tiene Process.THREAD_PRIORITY_BACKGROUND, sino también constantes para ajustar la prioridad un poco.

Actualmente, Android utiliza el subgrupo de subprocesos cgroup para todos los subprocesos con prioridad THREAD_PRIORITY_BACKGROUND o peor, y THREAD_PRIORITY_BACKGROUND es 10 mientras que THREAD_PRIORITY_DEFAULT es 0 y THREAD_PRIORITY_FOREGROUND es -2.

Si elige THREAD_PRIORITY_BACKGROUND + THREAD_PRIORITY_MORE_FAVORABLE (también conocido como 9) su hilo se eliminará del grupo cgroup de fondo con la limitación del 10%, sin ser lo suficientemente importante como para interrumpir sus hilos de interfaz de usuario con demasiada frecuencia.

Creo que hay tareas en segundo plano que necesitan un poco de poder de cómputo pero que al mismo tiempo no son lo suficientemente importantes como para bloquear de hecho la IU (consumiendo demasiada CPU en un hilo separado) y Android no tiene obvia prioridad para asignarlos, por lo que en mi opinión, esta es una de las mejores prioridades que puede asignar a dicha tarea.

Si puede utilizar un HandlerThread que es fácil de lograr:

ht = new HandlerThread("thread name", THREAD_PRIORITY_BACKGROUND + THREAD_PRIORITY_MORE_FAVORABLE); 
ht.start(); 
h = new Handler(ht.getLooper()); 

Si quieres ir con AsyncTask, todavía se puede hacer

protected final YourResult doInBackground(YourInputs... yis) { 
    Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND + THREAD_PRIORITY_MORE_FAVORABLE); 
    ... 
} 

pero tenga en cuenta que la aplicación subyacente puede reutilizar el mismo objeto Thread para diferentes tareas, para la próxima AsyncTask, o lo que sea. Parece que Android simplemente restablece la prioridad después de que doInBackground() regrese.

Por supuesto, si su UI realmente consume CPU y desea más potencia para su tarea al mismo tiempo, quitándola de la interfaz de usuario, puede establecer otra prioridad, tal vez hasta Process.THREAD_PRIORITY_FOREGROUND.

+2

Enredar con la prioridad de un hilo que no has creado es de mala calidad. Si desea controlar las prioridades de subprocesos, es genial, pero luego no use 'AsyncTask', sino bifurque su propio' Thread', o use un 'ExecutorService' para un grupo de subprocesos que cree.O bien, si * realmente * quiere la semántica 'AsyncTask', cree su propio' ExecutorService' y use 'executeOnExecutor()' para habilitarlo en API Level 11+, y establezca su propia prioridad de subprocesos en esos hilos. – CommonsWare

+2

@CommonsWare No he encontrado ningún otro objeto Thread-ish aparte de 'HandlerThread' que admite establecer' Process.THREAD_PRIORITY' explícitamente, y 'Thread's también tiene mágicamente prio de fondo por defecto. Como lo veo, ni siquiera puedo estar seguro de que Android no cambie la prioridad de un subproceso en el camino de acuerdo con sus necesidades. Así que cuando no encontré otra forma documentada que para 'HandlerThread', tomé la libertad de ver todos los otros métodos de manipulación prioritarios como igualmente no oficiales. Pero tienes un punto allí. –

+0

Mis disculpas, estaba malinterpretando algo en su respuesta. Aunque tenga en cuenta que 'HandlerThread' no tiene nada que ver con la prioridad, aparte de lo que hereda de la clase Java' Thread' estándar. – CommonsWare

18

AsyncTask se ejecuta con una prioridad más baja para ayudar a asegurarse de que el hilo de interfaz de usuario seguirá siendo sensible.

1

A pesar del rendimiento alcanzado, do desea hacer esto en el fondo. Juega bien, y los demás jugarán bien contigo.

Como no sé para qué sirve esto, no puedo sugerir una alternativa. Mi primera reacción fue que es extraño que estés tratando de formatear HTML en un dispositivo telefónico. Es un teléfono , no es un núcleo cuádruple con montones de RAM. ¿Es posible hacer el reformateo en un servicio web y mostrar el resultado en el teléfono?

+0

Bueno ... este es el código de un servidor de foro, que no tiene tapatalk ni ningún otro tipo de soporte, por lo que la única forma en que puedo pensar es en reformatearlo en el teléfono. Me saca de quicio para leer ese foro en un navegador (que BTW no parece hacer su búsqueda y representación en segundo plano), constantemente tengo que acercar y desplazar cada página manualmente, por lo que mi código elimina - material esencial (encabezados, la mayoría de las columnas en las tablas, la mayoría de las imágenes, etc.) y luego muestra ese formato muy bien en mi WebView. Es solo para mí ¿Usando un servicio web ...? No tengo la menor idea ... – OppfinnarJocke

0

u necesidad de llamar a última cadena str = fx.execute. no deberías invocar doinbackground directamente desde el hilo ui.

Cuestiones relacionadas