AsyncTask
Es una gran cosa para ejecutar tareas complejas en otro hilo.
Pero cuando hay un cambio de orientación u otro cambio de configuración mientras AsyncTask
todavía se está ejecutando, la corriente Activity
se destruye y se reinicia. Y como la instancia de AsyncTask
está conectada a esa actividad, falla y provoca una ventana de mensaje de "forzar cierre".
Por lo tanto, estoy buscando algún tipo de "práctica recomendada" para evitar estos errores y evitar que AsyncTask falle.
Lo que he visto hasta ahora es:
- Deshabilite los cambios de orientación (seguro que no es la forma en que debe manejar esto).
- Permitir que la tarea sobreviva y actualizarla con la nueva instancia de actividad a través de
onRetainNonConfigurationInstance
- Simplemente cancele la tarea cuando
Activity
se destruya y reinicie cuandoActivity
se vuelva a crear. - Enlace de la tarea a la clase de aplicación en lugar de la instancia de actividad.
- Algún método utilizado en el proyecto "estantes" (a través de onRestoreInstanceState)
Algunos ejemplos de código:
Android AsyncTasks durante una rotación de pantalla, Parte I y Parte II
¿Me pueden ayudar a encontrar el mejor enfoque que resuelva mejor el problema y que también sea fácil de implementar? El código en sí también es importante ya que no sé cómo resolver esto correctamente.
fuente
Respuestas:
No NO utilizar
android:configChanges
para tratar este tema. Esta es una muy mala práctica.No NO utilizar
Activity#onRetainNonConfigurationInstance()
cualquiera de los dos. Esto es menos modular y no es adecuado paraFragment
aplicaciones basadas.Puede leer mi artículo que describe cómo manejar los cambios de configuración utilizando
Fragment
s retenidos . Resuelve muy bien el problema de retener unAsyncTask
cambio transversal. Básicamente, necesita alojar suAsyncTask
interiorFragment
, llamarsetRetainInstance(true)
alFragment
e informar elAsyncTask
progreso / resultados del mismo aActivity
través de lo retenidoFragment
.fuente
TabActivity
. Para ser honesto, no estoy seguro de por qué estamos hablando de esto ... todos están de acuerdo en que eseFragment
es el camino a seguir. :)Por lo general, resuelvo esto haciendo que mis AsyncTasks activen Intentos de transmisión en la devolución de llamada .onPostExecute (), para que no modifiquen la Actividad que los inició directamente. Las Actividades escuchan estas transmisiones con BroadcastReceivers dinámicos y actúan en consecuencia.
De esta manera, las AsyncTasks no tienen que preocuparse por la instancia específica de la Actividad que maneja su resultado. Simplemente "gritan" cuando terminan, y si una Actividad es alrededor de ese tiempo (está activa y enfocada / está en estado reanudado) que está interesada en los resultados de la tarea, entonces será manejada.
Esto implica un poco más de sobrecarga, ya que el tiempo de ejecución necesita manejar la transmisión, pero generalmente no me importa. Creo que usar LocalBroadcastManager en lugar del sistema predeterminado en todo el mundo acelera un poco las cosas.
fuente
Aquí hay otro ejemplo de una AsyncTask que utiliza un
Fragment
para manejar los cambios de configuración en tiempo de ejecución (como cuando el usuario gira la pantalla) consetRetainInstance(true)
. También se demuestra una barra de progreso determinada (actualizada regularmente).El ejemplo se basa en parte en los documentos oficiales, Retener un objeto durante un cambio de configuración .
En este ejemplo, el trabajo que requiere un subproceso de fondo es la mera carga de una imagen de Internet en la interfaz de usuario.
Alex Lockwood parece tener razón en que cuando se trata de manejar cambios de configuración de tiempo de ejecución con AsyncTasks usando un "Fragmento Retenido" es la mejor práctica.
onRetainNonConfigurationInstance()
queda en desuso en Lint, en Android Studio. Los documentos oficiales nos advierten que usemosandroid:configChanges
, desde Manejando la configuración, cambie usted mismo , ...Luego está la cuestión de si uno debería usar AsyncTask para el hilo de fondo.
La referencia oficial de AsyncTask advierte ...
Alternativamente, uno podría usar un servicio, un cargador (usando un CursorLoader o AsyncTaskLoader) o un proveedor de contenido para realizar operaciones asincrónicas.
Rompo el resto de la publicación en:
El procedimiento
Comience con una AsyncTask básica como una clase interna de una actividad (no es necesario que sea una clase interna pero probablemente sea conveniente). En esta etapa, AsyncTask no maneja los cambios de configuración en tiempo de ejecución.
Agregue una clase anidada RetainedFragment que extienda la clase Fragement y no tenga su propia interfaz de usuario. Agregue setRetainInstance (true) al evento onCreate de este Fragmento. Proporcione procedimientos para configurar y obtener sus datos.
En onCreate () de la clase de actividad más externa, maneje el RetainedFragment: haga referencia a él si ya existe (en caso de que la actividad se reinicie); crear y agregarlo si no existe; Luego, si ya existía, obtenga datos del RetainedFragment y configure su IU con esos datos.
Inicie AsyncTask desde la IU
Agregue y codifique una barra de progreso determinada:
Todo el código para el procedimiento anterior.
Diseño de actividad.
La actividad con: clase interna AsyncTask subclasificada; clase interna RetainedFragment subclase que maneja los cambios de configuración en tiempo de ejecución (por ejemplo, cuando el usuario gira la pantalla); y una barra de progreso determinada que se actualiza a intervalos regulares. ...
En este ejemplo, la función de biblioteca (mencionada anteriormente con el prefijo de paquete explícito com.example.standardapplibrary.android.Network) que hace un trabajo real ...
Agregue todos los permisos que requiera su tarea en segundo plano al AndroidManifest.xml ...
Agregue su actividad a AndroidManifest.xml ...
fuente
Recientemente, he encontrado una buena solución aquí . Se basa en guardar un objeto de tarea a través de RetainConfiguration. Desde mi punto de vista, la solución es muy elegante y en cuanto a mí, he comenzado a usarla. Solo necesita anidar su asinctask de la tarea base y eso es todo.
fuente
Basado en la respuesta de @Alex Lockwood y en las respuestas de @William y @quickdraw mcgraw en esta publicación: Cómo manejar los mensajes del controlador cuando la actividad / fragmento está en pausa , escribí una solución genérica.
De esta forma se maneja la rotación, y si la actividad pasa a segundo plano durante la ejecución de la tarea asíncrona, la actividad recibirá las devoluciones de llamada (onPreExecute, onProgressUpdate, onPostExecute & onCancelled) una vez que se reanude, por lo que no se lanzará IllegalStateException (consulte Cómo manejar el controlador) mensajes cuando la actividad / fragmento está en pausa ).
Sería genial tener lo mismo pero con tipos de argumentos genéricos, como AsyncTask (por ejemplo: AsyncTaskFragment <Parámetros, Progreso, Resultado>), pero no pude hacerlo rápidamente y no tengo tiempo en este momento. Si alguien quiere mejorar, ¡no dude en hacerlo!
El código:
Necesitarás el PauseHandler:
Uso de la muestra:
fuente
Puedes usar cargadores para esto. Compruebe Doc aquí
fuente
Para aquellos que quieran esquivar Fragmentos, pueden retener la AsyncTask ejecutándose en los cambios de orientación usando onRetainCustomNonConfigurationInstance () y algunos cables.
(Tenga en cuenta que este método es la alternativa al obsoleto onRetainNonConfigurationInstance () ).
Sin embargo, parece que esta solución no se menciona con frecuencia. Escribí un ejemplo simple para ilustrar.
¡Salud!
fuente
He implementado una biblioteca que puede resolver problemas con la pausa de actividad y recreación mientras se ejecuta su tarea.
Debe implementar
AsmykPleaseWaitTask
yAsmykBasicPleaseWaitActivity
. Su actividad y tarea en segundo plano funcionarán bien, incluso si usted rota la pantalla y cambia entre aplicacionesfuente
SOLUCIÓN RÁPIDA (no recomendado)
Para evitar que una actividad se destruya y se cree es declarar su actividad en el archivo de manifiesto: android: configChanges = "oriente | keyboardHidden | screenSize
Como se menciona en los documentos
fuente