java - asynctask weakreference activity
WeakReference/AsyncTask patrĂ³n en Android (3)
¿Cómo resuelve esto la situación?
La WeakReference
permite que la Activity
se recoja, por lo que no tiene una pérdida de memoria.
Una referencia nula significa que AsyncTask
no puede intentar actualizar a ciegas una interfaz de usuario que ya no está conectada, lo que generaría excepciones (por ejemplo, la vista no está vinculada al administrador de ventanas). Por supuesto, hay que comprobar si hay null para evitar NPE.
si mi asynctask está descargando diez archivos, y al finalizar 5 la actividad se reinicia (debido a un cambio de orientación), ¿se volverá a invocar mi FileDownloadingTask?
Depende de su implementación, pero probablemente sí, si no hace deliberadamente algo para que la descarga repetida sea innecesaria, como el almacenamiento en caché de los resultados en algún lugar.
¿Qué pasaría con la
AsyncTask
anterior que se invocó inicialmente?
En versiones anteriores de Android se ejecutaría hasta completarse, descargando todos los archivos solo para desecharlos (o tal vez almacenarlos en caché, dependiendo de su implementación).
En las nuevas AsyncTask
Android sospecho que AsyncTask
se está matando junto con la Activity
que las inició, pero mi base de sospecha es que la demostración de pérdida de memoria para RoboSpice (ver a continuación) no se RoboSpice en mis dispositivos JellyBean.
Si puedo ofrecerle algún consejo: AsyncTask
no es adecuado para realizar tareas potencialmente largas como la red.
IntentService
es un IntentService
mejor (y aún relativamente simple), si un solo hilo de trabajo es aceptable para usted. Utilice un servicio (local) si desea controlar el conjunto de subprocesos, ¡y tenga cuidado de no trabajar en el subproceso principal!
RoboSpice parece bueno si está buscando una manera de realizar redes de manera confiable en segundo plano (descargo de responsabilidad: no lo he probado, no estoy afiliado). Hay una aplicación de demostración de RoboSpice Motivations en la tienda de juegos que explica por qué debería usarla demostrando todas las cosas que pueden salir mal con AsyncTask
, incluida la solución WeakReference.
Vea también este hilo: ¿AsyncTask es realmente defectuoso conceptualmente o simplemente me estoy perdiendo algo?
Actualizar:
Creé un proyecto github con un ejemplo de descarga usando IntentService
para otra pregunta SO ( ¿Cómo arreglar android.os.NetworkOnMainThreadException? ), Pero también es relevante aquí, creo. Tiene la ventaja adicional de que, al devolver el resultado a través de onActivityResult
, una descarga que está en vuelo cuando se gira el dispositivo se entregará a la Activity
reiniciada.
Tengo una pregunta con respecto a esta simple situación frecuente en Android.
Tenemos una actividad principal, invocamos una AsyncTask junto con la referencia de mainactivity, para que AsyncTask pueda actualizar las vistas en MainActivity.
Voy a desglosar el evento en pasos.
- MainActivity crea una AyncTask, le pasa su referencia.
- AysncTask, comienza a funcionar, descargando diez archivos, por ejemplo
- El usuario cambió la orientación del dispositivo. Esto da como resultado un puntero huérfano en la AsyncTask
- Cuando se completa el AsyncTask, e intenta acceder a la actividad para actualizar el estado, se bloquea, debido al puntero nulo.
La solución para lo anterior es mantener una WeakReference en AsyncTask como lo recomienda el libro "Pro Android 4".
WeakReference<Activity> weakActivity;
in method onPostExecute
Activity activity = weakActivity.get();
if (activity != null) {
// do your stuff with activity here
}
¿Cómo resuelve esto la situación?
Mi pregunta es, si mi asynctask está descargando diez archivos, y al completar 5 la actividad se reinicia (debido a un cambio de orientación), ¿se volverá a invocar mi FileDownloadingTask?
¿Qué pasaría con la AsyncTask anterior que se invocó inicialmente?
Gracias, y me disculpo por la longitud de la pregunta.
¿Cómo resuelve esto la situación?
No lo hace
El referente de un WeakReference
se establece en null
cuando el recolector de basura determina que el referente es débilmente accesible. Esto no ocurre cuando se detiene una actividad, y no necesariamente ocurre inmediatamente cuando se destruye la actividad y el marco descarta todas las referencias a ella. Si el GC no se ha ejecutado, es completamente posible que la AsyncTask
complete mientras que su WeakReference
todavía contiene una referencia a una actividad muerta.
No solo eso, sino que este enfoque no hace nada para evitar que AsyncTask
consuma CPU inútilmente.
Un mejor enfoque es hacer que la Activity
mantenga una fuerte referencia a la AsyncTask
y cancel(...)
en el método de ciclo de vida de desmontaje apropiado. La AsyncTask
debe monitorear isCancelled()
y dejar de funcionar si ya no es necesario.
Si desea que una AsyncTask
sobreviva a los cambios de configuración (pero no a otras formas de destrucción de la actividad), puede alojarla en un fragmento retenido.
La clase WeakReference
básicamente evita que el JRE aumente el contador de referencia para la instancia dada.
No entraré en la administración de la memoria de Java y responderé a su pregunta directamente: WeakReference
resuelve la situación al proporcionarle a AsyncTask
una forma de saber si su actividad principal sigue siendo válida.
El cambio de orientación en sí no reiniciará automáticamente la AsyncTask
. onCreate
codificar el comportamiento deseado con los mecanismos conocidos ( onCreate
/ onDestroy
, onSave/RestoreInstanceState
).
Con respecto a la AsyncTask
original, no estoy 100% seguro de cuál de estas opciones sucederá:
- O bien Java detiene el hilo y desecha la
AsyncTask
, porque el único objeto que contiene una referencia (laActivity
original) se destruye - O algún objeto Java interno mantiene una referencia al objeto
AsyncTask
, bloqueando su recolección de basura, dejando efectivamente queAsyncTask
termine en segundo plano
De cualquier manera, sería una buena práctica abortar / pausar y reiniciar / reanudar la AsyncTask
manualmente (o entregarla a la nueva Activity
), o usar un Service
lugar.