He investigado este problema durante meses, se me ocurrieron diferentes soluciones, que no estoy contento, ya que todos son hacks masivos. Todavía no puedo creer que una clase con un diseño defectuoso haya llegado al marco y nadie esté hablando de eso, así que supongo que me falta algo.
El problema es con AsyncTask
. De acuerdo con la documentación
"permite realizar operaciones en segundo plano y publicar resultados en el subproceso de la interfaz de usuario sin tener que manipular subprocesos y / o controladores".
El ejemplo continúa mostrando cómo showDialog()
se llama a algún método ejemplar onPostExecute()
. Esto, sin embargo, me parece totalmente artificial , porque mostrar un diálogo siempre necesita una referencia a una válida Context
, y una AsyncTask nunca debe contener una referencia fuerte a un objeto de contexto .
La razón es obvia: ¿qué pasa si la actividad se destruye, lo que desencadenó la tarea? Esto puede suceder todo el tiempo, por ejemplo, porque volteó la pantalla. Si la tarea tuviera una referencia al contexto que la creó, no solo se está aferrando a un objeto de contexto inútil (la ventana se habrá destruido y cualquier interacción de la IU fallará con una excepción), incluso se arriesga a crear un pérdida de memoria.
A menos que mi lógica sea defectuosa aquí, esto se traduce en: onPostExecute()
es completamente inútil, porque ¿de qué sirve que este método se ejecute en el hilo de la interfaz de usuario si no tiene acceso a ningún contexto? No puedes hacer nada significativo aquí.
Una solución alternativa sería no pasar instancias de contexto a una AsyncTask, sino una Handler
instancia. Eso funciona: dado que un controlador vincula libremente el contexto y la tarea, puede intercambiar mensajes entre ellos sin correr el riesgo de una fuga (¿verdad?). Pero eso significaría que la premisa de AsyncTask, es decir, que no necesita molestarse con los controladores, está mal. También parece abusar de Handler, ya que está enviando y recibiendo mensajes en el mismo hilo (lo crea en el hilo de la interfaz de usuario y lo envía a través de onPostExecute (), que también se ejecuta en el hilo de la interfaz de usuario).
Para colmo, incluso con esa solución alternativa, todavía tiene el problema de que cuando se destruye el contexto, no tiene registro de las tareas que realizó. Eso significa que debe reiniciar cualquier tarea al volver a crear el contexto, por ejemplo, después de un cambio de orientación de la pantalla. Esto es lento y derrochador.
Mi solución a esto (como se implementa en la biblioteca Droid-Fu ) es mantener una asignación de WeakReference
s de los nombres de los componentes a sus instancias actuales en el objeto de aplicación único. Cada vez que se inicia una AsyncTask, registra el contexto de llamada en ese mapa, y en cada devolución de llamada, obtendrá la instancia de contexto actual de esa asignación. Esto garantiza que nunca hará referencia a una instancia de contexto obsoleto y que siempre tendrá acceso a un contexto válido en las devoluciones de llamada para que pueda realizar un trabajo de interfaz de usuario significativo allí. Tampoco tiene fugas, porque las referencias son débiles y se borran cuando ya no existe una instancia de un componente dado.
Aún así, es una solución alternativa compleja y requiere subclasificar algunas de las clases de la biblioteca Droid-Fu, lo que lo convierte en un enfoque bastante intrusivo.
Ahora simplemente quiero saber: ¿Me estoy perdiendo algo masivamente o AsyncTask es realmente completamente defectuoso? ¿Cómo están tus experiencias trabajando con él? ¿Cómo resolviste este problema?
Gracias por tu contribución.