programacion - Comportamiento de contexto Android AsyncTask
manual de android en pdf (4)
He estado trabajando con AsyncTasks en Android y estoy lidiando con un problema.
Tome un ejemplo simple, una Actividad con una AsyncTask. La tarea en el fondo no hace nada espectacular, simplemente duerme durante 8 segundos.
Al final de AsyncTask en el método onPostExecute () solo estoy configurando el estado de visibilidad de un botón en View.VISIBLE, solo para verificar mis resultados.
Ahora, esto funciona de maravilla hasta que el usuario decida cambiar la orientación de su teléfono mientras está funcionando la AsyncTask (dentro de la ventana de espera 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 aparece el problema. AsyncTask se refiere a un botón y aparentemente contiene una referencia al contexto que inició la AsyncTask en primer lugar.
Esperaría que este contexto anterior (ya que el usuario causó un cambio de orientación) se vuelva nulo y AsyncTask arroje un NPE para la referencia al botón que está tratando de hacer visible.
En su lugar, no se lanza ningún NPE, AsyncTask piensa que la referencia del botón no es nula, lo establece en visible. ¿El resultado? ¡No pasa nada en la pantalla!
Actualización: He abordado esto manteniendo una WeakReference
a la actividad y cambiando cuando ocurre 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 demolida y reiniciada. El objeto Handler interno se vuelve obsoleto, tal como lo indicó. En el ejemplo de Shelves de Romain Guy, él simplemente cancela cualquier AsyncTask actualmente en ejecución y luego reinicia los cambios de posorientación nuevos.
Es posible transferir el hilo a la nueva actividad, pero agrega mucha plomería. No existe una forma generalmente aceptada de hacerlo, pero puedes leer sobre mi método aquí: http://foo.jasonhudgins.com/2010/03/simple-progressbar-tutorial.html
Este es el tipo de cosas que me llevan a evitar siempre que mi Actividad sea destruida / recreada en el cambio de orientación.
Para hacerlo, agregue 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 usar la respuesta dada aquí: https://.com/a/2124731/327011
Pero si necesita destruir la actividad (diferentes diseños para retrato y paisaje) puede hacer que AsyncTask sea una clase pública (Lea aquí por qué no debería ser Android privado : recomendaciones de AsyncTask: ¿clase privada o clase pública? ) Y luego cree una método setActivity para establecer la referencia a la actividad actual cada vez que se destruye / crea.
Puede ver un ejemplo aquí: Android AsyncTask en clase externa
Si solo necesita un contexto y no lo va a usar para cosas ui, simplemente puede pasar el ApplicationContext a su AsyncTask. A menudo necesita el contexto para los recursos del sistema, por ejemplo.
No intente actualizar la interfaz de usuario desde una AsyncTask y trate de evitar el manejo de los cambios de configuración, ya que puede complicarse. Para actualizar la interfaz de usuario, puede registrar un receptor de difusión y enviar una transmisión.
También debe tener AsyncTask como una clase pública separada de la actividad mencionada anteriormente, hace que las pruebas sean mucho más fáciles. Lamentablemente, la programación de Android a menudo refuerza las malas prácticas y los ejemplos oficiales no ayudan.