Comportamiento del contexto de Android AsyncTask

He estado trabajando con AsyncTasks en Android y estoy lidiando con un problema.

Tome un ejemplo simple, una actividad con un AsyncTask. La tarea en el fondo no hace nada espectacular, sólo duerme durante 8 segundos.

Al final de AsyncTask en el método onPostExecute () sólo estoy estableciendo un estado de visibilidad de botón a View.VISIBLE, sólo para verificar mis resultados.

Ahora, esto funciona muy bien hasta que el usuario decide cambiar su orientación de teléfonos mientras el AsyncTask está trabajando (dentro de la ventana de sueño de 8 segundos).

Entiendo el ciclo de vida de la actividad de Android y sé que la actividad se destruye y se vuelve a crear.

Aquí es donde entra el problema. El AsyncTask hace referencia a un botón y al parecer contiene una referencia al contexto que inició AsyncTask en primer lugar.

Espero que este contexto antiguo (ya que el usuario provocó un cambio de orientación) se convierta en nulo y AsyncTask para lanzar un NPE para la referencia al botón que está intentando hacer visible.

En su lugar, no se produce NPE, AsyncTask piensa que la referencia de botón no es nulo, lo establece a visible. ¿El resultado? ¡Nada está sucediendo en la pantalla!

Actualización: He abordado esto por mantener una WeakReference a la actividad y cambiar cuando se produce un cambio de configuración. Esto es engorroso.

Aquí está el código:

 public class Main extends Activity { private Button mButton = null; private Button mTestButton = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mButton = (Button) findViewById(R.id.btnStart); mButton.setOnClickListener(new OnClickListener () { @Override public void onClick(View v) { new taskDoSomething().execute(0l); } }); mTestButton = (Button) findViewById(R.id.btnTest); } private class TaskDoSomething extends AsyncTask<Long, Integer, Integer> { @Override protected Integer doInBackground(Long... params) { Log.i("LOGGER", "Starting..."); try { Thread.sleep(8000); } catch (InterruptedException e) { e.printStackTrace(); } return 0; } @Override protected void onPostExecute(Integer result) { Log.i("LOGGER", "...Done"); mTestButton.setVisibility(View.VISIBLE); } } } 

Intente ejecutarlo y mientras el AsyncTask esté funcionando, cambie la orientación de su teléfono.

AsyncTask no está diseñado para ser reutilizado una vez que una Actividad ha sido eliminada y reiniciada. El objeto de controlador interno se convierte en obsoleto, como lo ha indicado. En el ejemplo de Shelves por Romain Guy, el sencillo cancela cualquier AsyncTask actualmente en ejecución y luego reinicia nuevos cambios posteriores a la orientación.

Es posible entregar tu hilo a la nueva actividad, pero añade un montón de fontanería. No existe un acuerdo general sobre la forma de hacerlo, pero puede leer sobre mi método aquí: http://foo.jasonhudgins.com/2010/03/simple-progressbar-tutorial.html

Si sólo necesita un contexto y no lo usará para ui cosas, simplemente puede pasar el ApplicationContext a su AsyncTask.You a menudo necesita el contexto de los recursos del sistema, por ejemplo.

No intente actualizar la interfaz de usuario desde una AsyncTask e intente evitar manipular los cambios de configuración usted mismo, ya que puede resultar desordenado. Para actualizar la interfaz de usuario, puede registrar un receptor de difusión y enviar una difusión.

También debe tener el AsyncTask como una clase pública separada de la actividad como se mencionó anteriormente, que hace que la prueba mucho más fácil. Lamentablemente, la programación de Android a menudo refuerza las malas prácticas y los ejemplos oficiales no ayudan.

Este es el tipo de cosa que me lleva a evitar siempre que mi Actividad sea destruida / recreada en el cambio de orientación.

Para ello, añada esto a su etiqueta <Activity> en su archivo de manifiesto:

 android:configChanges="orientation|keyboardHidden" 

Y anula onConfigurationChanged en tu clase de actividad:

 @Override public void onConfigurationChanged(final Configuration newConfig) { // Ignore orientation change to keep activity from restarting super.onConfigurationChanged(newConfig); } 

Para evitar esto, puede utilizar la respuesta aquí: https://stackoverflow.com/a/2124731/327011

Pero si necesitas destruir la actividad (diferentes diseños para el retrato y el paisaje) puedes hacer que el AsyncTask sea una clase pública (Lee aquí por qué no debería ser privado Android: AsyncTask recomendaciones: private class o public class? Method setActivity para establecer la referencia a la actividad actual cada vez que se destruye / crea.

Puedes ver un ejemplo aquí: Android AsyncTask en clase externa

  • Seguridad de subprocesos al realizar iteraciones a través de un ArrayList utilizando foreach
  • ¿Qué pasa con multithreading en Android SQLite?
  • Bloqueo de hilo principal de Android que bloquea hilo de WebView
  • Android AsyncTask vs hilo + controlador vs rxjava
  • Android ProgressDialog con el problema de roscado
  • ¿Cómo ejecutar el servicio no en el hilo principal?
  • AsyncTask hilo sigue allí después de ejecutar, ¿es normal?
  • Android de larga duración 'servicio'
  • Show Dialog in Juego con lienzo
  • ¿Cuál es la forma correcta de iniciar una comunicación de red basada en recibir una intención de difusión?
  • Tacto largo en una superficieView (android)
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.