Práctica recomendada para iniciar AsyncTask desde la vista personalizada

Es bastante común generar un hilo de cálculo que consume tiempo. Posteriormente, necesitamos actualizar Activity o Fragment con resultado computacional.

Todo el tiempo, estoy siguiendo las siguientes pautas. Funciona bien para mí hasta ahora.

AsyncTask necesita un fragmento de UI onPostExecute

  1. Use setRetainInstance(true) fragmento sin UI.
  2. Utilice la técnica setTargetFragment y getTargetFragment
  3. Consulte https://stackoverflow.com/a/12303649/72437

AsyncTask necesita ejecutar OnPostExecute Actividad de la interfaz de usuario

  1. Use setRetainInstance(true) fragmento sin UI.
  2. Utilice onAttach y onDetach para almacenar referencia a Activity . Google parece no alentar el uso de getActivity . Http://developer.android.com/guide/components/fragments.html
  3. Consulte https://stackoverflow.com/a/16305029/72437

Sin embargo, ¿qué tal el caso de una clase derivada de View ? Planeo lanzar AsyncTask desde la View personalizada. Sin embargo, ¿cómo puedo onPostExecute volver a la View ?

La razón por la que lo estoy pidiendo es, en mi vista personalizada, cierto evento táctil lo activará para volver a dibujarse con un nuevo mapa de bits. Generar el nuevo mapa de bits requiere mucho tiempo. Por lo tanto, planeo lanzar un AsyncTask, para generar dicho mapa de bits, y pasar de nuevo a la vista personalizada. Sin embargo, el cambio de configuración puede provocar que se vuelva a crear la vista personalizada. Por lo tanto, necesito asegurar mi AsyncTask puede tener la referencia correcta de la visión sobre onPostExecute .

Suponiendo que está utilizando AsyncTask sólo para las operaciones relacionadas con el dibujo (de lo contrario debería revisar su lógica – como sugieren los comentarios), puede crear AsyncTask directamente en su clase de View personalizada:

 class MyView extends View { private MyAsyncTask currentTask = null; // View details @Override public void onAttachedToWindow() { currentTask = new MyAsyncTask(this); currentTask.execute(); } @Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); if (currentTask != null) { currentTask.cancel(true); currentTask = null; } } static class MyAsyncTask extends AsyncTask<Void, Void, Bitmap> { private WeakReference<MyView> viewRef; MyAsyncTask(MyView view) { viewRef = new WeakReference<>(view); } // background task implementation @Override public void onPostExecute(Bitmap bitmap) { MyView view = viewRef.get(); if (view == null) { return; } // you now can safely update your view - we're on UI thread } } } 

Así es como se vería la implementación segura. Tiene algunas desventajas y partes importantes:

  • En ningún momento su AsyncTask debería mantener una referencia fuerte a View (por eso la clase es declarada como static y tiene WeakReference en View )
  • Cuando ya no te interesa el resultado de AsyncTask , cancela
  • Esta implementación simplemente AsyncTask resultado posiblemente útil de AsyncTask cancelado. Si ese es el problema, sugeriría eliminar AsyncTask de View completamente y buscar otras soluciones ( Executor separado o HandlerThread ).

También onPostExecute de AsyncTask será llamado desde el mismo thread looper que lo lanzó (en su caso, que es el hilo principal, por lo que no importa si se inicia desde la Activity o View , o en cualquier otro lugar, todo depende de lo difícil que sería Para gestionar esas tareas).

Te daré una idea general de que puedes seguir adelante aplicándote en cualquier lugar que necesites (incluyendo desde Thread o ThreadExecutor (en lugar de confiar sólo en AsyncTask);

Puede usar directamente una biblioteca para manejar events , donde los eventos se distribuyen en un "bus" común y cualquier clase que desee puede registrarse en el bus y escuchar esos eventos:

Y para eso me referiré a Otto, funciona bien y es muy potente: http://square.github.io/otto/

Alternativamente puede implementarlo usted mismo con el LocalBroadcastManager https://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html como este:

LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context) <que es para obtener una referencia a la misma.

OnEventComplete: (sucede en su hilo o AsyncTask

 Intent i = new Intent("DOWNLOAD_COMPLETE"); // ps. feel free to attach extras to the intent, in order to pass data back to your activity/fragment/view lbm.sendBroadcast(i); 

Luego en tu actividad / fragmento / vista creas un receptor

 BroadcastReceiver receiver = new BroadcastReceiver(){ @Override public void onReceive (Context context, Intent intent){ // feel free to read the extras from the intent with data here and update your view } }; 

OnStartListening:

 lbm.registerReceiver(receiver, new IntentFilter("DOWNLOAD_COMPLETE")); 

OnStopListening:

 lbm.unregisterReceiver(receiver); 

Entonces DEBE comenzar y dejar de escuchar durante onStart / onStop o onResume / onPause o onAttachedToWindow / onDetachedFromWindow (para vistas)

  • ¿Cómo puedo hacer que WRAP_CONTENT funcione en un RecyclerView
  • ¿Es posible incluir un Fragmento en un diseño de método de entrada?
  • Arriba Navegación (flecha de la barra de acción flecha) no funciona para fragmentos
  • Android Studio obtener "Debe implementar OnFragmentInteractionListener"
  • Cómo cambiar la orientación de fragmentos en android
  • ¿Por qué TabActivity está obsoleta (razón)?
  • Cómo agregar un objeto de encabezado y preferencia a PreferenceActivity no utilizar un archivo XML, pero Java?
  • Diseño con fragmento y framelayout sustituido por otro fragmento y framelayout
  • Reemplazo de TabActivity con FragmentActivity y Fragments
  • ¿Cómo mantener el estado del fragmento sin backstack en la pestaña?
  • ¿Cómo manejar correctamente la rotación de la pantalla con un ViewPager y fragmentos anidados?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.