weakreference asynctask activity java android android-asynctask weak-references

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 (la Activity original) se destruye
  • O algún objeto Java interno mantiene una referencia al objeto AsyncTask , bloqueando su recolección de basura, dejando efectivamente que AsyncTask 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.