AsyncTask, ¿debe tomarse un golpe de penalización de rendimiento …?

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.

Rastreando el problema, TraceView me muestra que mi AsyncTask (en prioridad predeterminada) se ejecuta en aproximadamente 10 trozos de 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í está el código comstackble según 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 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 { 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 FilterX cuando se ejecuta como AsyncTask que cuando se ejecuta en el hilo de la GUI, eliminé todo el material de ProgressBar, y luego obtengo los siguientes tiempos:

  • Más de 30 segundos para cargar una página con prioridad predeterminada de subprocesos
  • Más de 15 segundos para cargar una página en MAX_PRIORITY
  • 5+ segundos para cargar una página cuando se ejecuta en el hilo de GUI

No eres el único que observa este comportamiento. La ralentización por el factor 10 es probablemente el resultado de que Android use un grupo de progtwigs de Linux (clase de progtwigció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 usa el grupo de temas de fondo para todos los hilos 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 computacional pero que al mismo tiempo no son lo suficientemente importantes como para bloquear de hecho la IU (al consumir demasiada CPU en un hilo separado) y Android no tiene ninguna prioridad obvia para asignar a estos, por lo que en mi opinión, esta es una de las mejores prioridades que puede asignar a dicha tarea.

Si puede usar HandlerThread, 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, aún puedes hacerlo

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

pero tenga en cuenta que la implementació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.

AsyncTask se ejecuta con una prioridad menor para ayudar a garantizar que el subproceso de la interfaz de usuario siga siendo receptivo.

A pesar del éxito en el rendimiento, quieres hacer esto en segundo plano. 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 un quad-core con montones de RAM. ¿Es posible hacer el reformateo en un servicio web y mostrar el resultado en el teléfono?

necesita llamar a String str = fx.execute final. no deberías invocar doinbackground directamente desde el hilo ui.