2012-09-12 11 views
6

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:

  1. 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.

  2. 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.

+0

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

Respuesta

8

¿por qué no hacer que su actividad inicie un servicio para alojar la tarea de larga ejecución? un servicio es su mejor apuesta para mantener las cosas a largo plazo. puede proporcionar sugerencias al sistema operativo para dejarlo en ejecución. ver startForeground() y START_STICKY.

puede comunicarse con la actividad transmitiendo desde el servicio. Haga que su actividad registre programáticamente un broadcast receiver para escuchar esos intentos. si la actividad está en pausa, anule el registro de su receptor, de esa manera no responderá cuando no esté en primer plano.

si el sistema operativo destruye su servicio, entonces está matando el proceso, por lo que su tarea está condenada de todos modos. lo mejor que puedes hacer es reiniciarlo. de todos modos, esto no sucederá excepto en las condiciones más extremas si considera lo anterior.

EDITAR: resumiendo algunos pensamientos capturados en los comentarios ... mantener un proceso de trabajo de larga duración y reiniciarlo automáticamente es casi siempre lo incorrecto. los dispositivos móviles no tienen la fuente de alimentación para que esto sea factible. el autor declaró que tiene una condición especial que niega esta preocupación ... eso solo sería cierto si el dispositivo está conectado a una fuente de alimentación casi siempre.

el modelo de Android es proporcionar un conjunto robusto de intenciones (notificaciones) para que su aplicación pueda escuchar. estos intentos despiertan su dispositivo/aplicación cuando es hora de hacer algo. su aplicación debe manejar la notificación y luego permitir que el dispositivo vuelva a dormirse inmediatamente.

si no hay intención de stock que se corresponda con su evento, puede usar Google Cloud Messaging para activar dispositivos desde un servidor.en otras palabras, permita que los procesos de larga ejecución se ejecuten en el servidor y active el dispositivo cuando sea necesario.

si puede hacer eso, una alternativa sería usar AlarmManager para activar periódicamente y verificar algún estado. incluso si hicieras esto con frecuencia (por ejemplo, cada 5 minutos, que también es un no-no), sería mucho mejor que mantener el dispositivo siempre activo como se sugiere. también, haga uso de intervalos de activación inexactos (consulte la documentación vinculada anteriormente).

+0

Sí, ejecutar como servicio, y hacer como algunas aplicaciones, agregar un icono en la barra de prueba también ayudará a evitar que el servicio salga, puede obtener el evento mientras se detendrá una aplicación de Android. –

+0

Es sorprendente cómo un simple hilo de trabajo que habría codificado como una clase Runnable anidada en cualquier otra plataforma, en Android implica servicios de FG, receptores y tal vez bloqueos de activación. –

+0

de larga ejecución, los procesos en segundo plano son un caso especial en cualquier plataforma móvil. simplemente no desea hacer eso en la mayoría de los casos porque los dispositivos móviles no tienen la fuente de energía para que esto sea factible. –

Cuestiones relacionadas