Es bien sabido que Android coloca nuestros componentes de aplicaciones (actividades, servicios) bajo amenaza de ser asesinados en cualquier momento. Esto hace las cosas realmente complicadas si desea proporcionar una solución robusta y sin fugas, mientras que al mismo tiempo mantiene el código limpio y aborda la separación de las preocupaciones.Buen diseño: subprocesos de trabajo y reinicios de actividades
Problema:
Una actividad inicia una tarea que consume tiempo (en forma de una Runnable
, AsyncTask
o cualquier otro mecanismo). Se debe mostrar un diálogo de progreso. La tarea abrirá varias conexiones, pero debería tener un medio para actualizar una barra de progreso, y podría necesitar mostrar un diálogo de elección hasta la mitad. También debería poder cerrar el cuadro de diálogo y mostrar un Toast cuando ocurre un error o cuando se completa.
cuando una actividad se mató y luego se vuelve a crear una nueva instancia, que se puede pensar en dos opciones:
tratar de matar a la tarea en ejecución, así, y generar otra instancia de tarea cuando un nuevo sustituto instancia de actividad se crea. Esta no es una opción para una tarea de larga ejecución, ya que desde el punto de vista del usuario es inaceptable iniciar la tarea y ver que el indicador de la barra de progreso va del 80% al 0% sin razón aparente. Sin embargo, este es el enfoque seguido en el Shelves example by Romain Guy. Aparentemente este es el enfoque recomendado en la Guía oficial para desarrolladores.
Mantenga la tarea en ejecución: para que pudiéramos tener una actividad suscribirse a (y posiblemente de partida) la tarea de actualizaciones en
onResume
, y darse de baja (y, posiblemente, hacer una pausa) al .
La segunda opción significa que debemos evitar la tarea de recopilación en caso de que la actividad se destruya temporalmente. Por ejemplo, podríamos declararlo dentro de una clase de aplicación personalizada, o incluso ofrecerlo como un servicio para el resto de la aplicación (utilizando un servicio Singleton? An Android?). No soy fanático de esto, sin embargo, ya que la tarea es utilizado solo por la actividad, por lo que no tiene sentido hacerlo disponible para otras clases. Los servicios de Android, por otro lado, también tienen que lidiar con el ciclo de vida.
La opción n. ° 2 también implica asegurar que no se filtre la actividad anterior. No deberíamos intentar actualizar la GUI si no hay actividad presente. Todo el pensamiento debería ser seguro para subprocesos. Finalmente, la tarea no debería permanecer en la memoria después de estar seguros de que ya no es necesaria. Pero al mismo tiempo, si se destruye la actividad inicial, la tarea sigue ejecutándose y se inicia una nueva actividad sustituta, la tarea debe estar actualizada para actualizar la GUI de forma inmediata y mostrar el estado actual de la tarea (o resultado en caso de que se complete))
El hecho de que la tarea se ejecute cuando no se muestra ninguna actividad es otro problema a tratar, ya que podríamos necesitar la interacción del usuario (diálogo de elección) de forma síncrona en algún momento. Esto podría resolverse si la actividad tuviera la capacidad de pausar inmediatamente la tarea al llegar al , pero la tarea podría demorar un tiempo o incluso no poder hacerlo.
Se han formulado preguntas similares anteriormente, pero no estoy preguntando específicamente sobre cómo resolver el problema, sino sobre qué diseño recomendaría para lograr un acoplamiento flexible y aislar la actividad de la tarea tanto como posible.
En resumen:
- Este es un problema recurrente en el desarrollo y alguien Android probablemente ha llegado con un diseño inteligente antes. ¿Hay un patrón de diseño o una biblioteca bien probada que simplifique esto?
- Si implementara el # 2, ¿qué clase elegiría para la tarea (Ejecutable, Servicio, etc.) y cómo se comunicaría con la actividad?
P.S. Por favor absténgase de publicar soluciones basadas en la "orientación | keyboardHidden" hackear XD. Aparte de esto, cualquier ayuda sería apreciada.
ACTUALIZACIÓN:
Mi primer intento ha conseguido un poco desordenado. La aplicación es una utilidad de impresión bluetooth, que implica detectar el estado de BT (y pedirle al usuario que lo habilite si estuviera deshabilitado), recuperar los dispositivos emparejados (y pedirle al usuario que elija uno si había más de uno), y luego enviar los datos y finalmente informar al usuario sobre el resultado. Como esta tarea tenía un 80% de código IO, un 20% de operaciones relacionadas con la GUI, tuve que agrupar el código de la GUI al principio y al final de la tarea, luego lo saqué de la tarea para volver a la actividad. Tengo un IntentService
que hace el trabajo pesado, luego informa a la actividad a través del BroadcastReceiver
. Esto resuelve la mayoría de los problemas, pero hay algunos problemas con dicho diseño. En primer lugar, existen todas las cadenas constantes utilizadas como claves para colocar y recuperar campos de los Intents de entrada y salida, que están introduciendo el acoplamiento semántico. Hubiera preferido la seguridad de tipo en la actividad < -> comunicación de servicio. Y aún tengo que resolver cómo pasar objetos personalizados complejos al servicio en el Intento, ahora estoy atascado con parcelas y parámetros primitivos. Finalmente, tanto la actividad como el servicio usan la misma API (bluetooth), hubiera preferido tener todo el código relacionado con BT en una sola clase. Creo que descartaré este diseño y volveré a intentar con una cola de impresión personalizada basada en hilos, a pesar de que dificultará el tratamiento de las actividades muertas.
AsyncTask se detiene mientras se recrea la actividad. Los mensajes que la tarea usa para el progreso/resultado no se entregan mientras se destruye la actividad (puede suceder después de la pausa/antes del currículum). Utilizar la instancia de no configuración para pasar AsyncTask entre instancias de actividad debería funcionar bastante bien. – zapl