Implementación de un grupo de subprocesos dentro de un servicio

Actualmente estoy trabajando en la implementación de un Service que cuando se solicite realizará algún trabajo en varios hilos paralelos.

Mi implementación se basa en la clase ThreadPoolExecutor junto con LinkedBlockingQueue .

Como regla básica, una vez que todas las tareas están terminadas y no hay tareas pendientes en la cola, me gustaría detener el servicio (aunque el servicio puede volver a iniciarse y seguir la misma lógica).

He podido alcanzar el resultado deseado usando el código abajo, con todo no estoy seguro si este acercamiento es correcto.

 public class TestService extends Service { // Sets the initial threadpool size to 3 private static final int CORE_POOL_SIZE = 3; // Sets the maximum threadpool size to 3 private static final int MAXIMUM_POOL_SIZE = 3; // Sets the amount of time an idle thread will wait for a task before terminating private static final int KEEP_ALIVE_TIME = 1; // Sets the Time Unit to seconds private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS; // A queue of Runnables for the uploading pool private final LinkedBlockingQueue<Runnable> uploadQueue = new LinkedBlockingQueue<Runnable>(); // A managed pool of background upload threads private final ThreadPoolExecutor uploadThreadPool = new ThreadPoolExecutor( CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, uploadQueue) { @Override protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); if (getActiveCount() == 1 && getQueue().size() == 0) { // we're the last Runnable around + queue is empty, service can be // safely stopped. TestService.this.stopSelf(); } } }; @Override public IBinder onBind(Intent intent) { return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { // execute a new Runnable uploadThreadPool.execute(new TestRunnable()); /** * Indicating that if Android has to kill off this service (ie low memory), * it should not restart it once conditions improve. */ return START_NOT_STICKY; } @Override public void onDestroy() { uploadThreadPool.shutdownNow(); uploadQueue.clear(); super.onDestroy(); } } 

Así que tengo algunas cosas que todavía no estoy seguro.

  1. Suponiendo que onDestroy fue llamado, ¿es seguro asumir que mi implementación interrumpirá TODOS los subprocesos en ejecución y borrará de forma segura las tareas pendientes sin interrumpir de alguna manera con la implementación de clase ThreadPoolExecutor ? La razón que estoy pidiendo es porque la cola se asocia con el ejecutor, y quizá shutdownNow es asíncrono y dependiendo del estado de la cola. ¿Hay una mejor manera de hacer esto?

  2. ¿Es correcto implementar esta lógica dentro de onDestroy ? De mi experiencia hay algunos casos donde el servicio se mata (es decir, memoria baja) y esta llamada de retorno no se llama. ¿Debo realizar un enfoque similar en otros lugares también?

  3. ¿Sería mejor declarar mis miembros de la clase queue & executor como estáticos? – Según lo declarado por @TheTwo "Excecutor cannot be re-used once shutdown is called" .

  4. ThreadPoolExecutor clase espera un BlockingQueue , ¿cuáles son los pros / contras de utilizar otros tipos de implementaciones BlockingQueue (es decir, ArrayBlockingQueue )?

  5. Respecto a la forma en que actualmente detecto cuando la cola está vacía y no hay más tareas pendientes (específicamente dentro de afterExecute callback) – ¿Es esta la mejor manera de hacer esto? ¿O puedo obtener una indicación de que la cola está vacía y las tareas se terminan de otra manera?

Apreciar cualquier ayuda!

Creo que está intentando implementar un servicio, que introduce muchos problemas, pero no resuelve ninguno. Efectivamente, se reduce el código de llamada en una línea, la creación del Ejecutor, pero se quita la capacidad de controlarla con precisión. No hay ninguna ganancia en la programación de muchas tareas, ya que esto ya está resuelto por el OS 'thread scheduler. Además, un llamante malicioso puede romper varios otros programas simplemente añadiendo suficiente while(true) sleep(100); Bucles

Sobre sus preguntas:

  1. No puede asegurarse de que todos los subprocesos se interrumpan correctamente nunca, porque no hay manera de interrumpir un subproceso que no está correctamente viendo el indicador de interrupción. Un while(true) ; No se puede interrumpir excepto por System.exit() . Teóricamente podría detener un subproceso, pero esta característica está obsoleta por una razón, ya que puede dejar la tarea real en un estado incompleto / inacabado (iE dejando las conexiones TCP entreabiertas).

  2. No, usted no está aplicando correctamente eso. Por una vez las tareas dejadas int la cola simplemente desaparecería en el vacío, y luego un Excecutor no se puede volver a utilizar una vez que se llama a la parada. Por lo tanto, al menos necesitas crear una nueva instancia de Excecutor en el inicio del servicio, y realmente deberías averiguar qué hacer con las tareas restantes.

  3. No, debido a 2.

  4. Los pros / contras de los tipos de lista dependen de su caso de uso. Un ArrayList tiene un costo más alto al crecer / encogerse, pero un menor costo al indexar un elemento específico (indexOf), mientras que una lista vinculada es casi lo contrario. Dado que su cola siempre se agrega a la cola, no se preocupa por ningún otro elemento, excepto el primero, y que está creciendo / encogiendo con frecuencia, una lista vinculada es la mejor opción.

  5. No debe detener la tarea de esta manera en absoluto, porque el orden de ejecución con subprocesos no está definido. En el peor de los casos su programa de llamadas se interrumpe cada vez hasta que se ejecute el servicio, lo que hará que el servicio para iniciar y detener sin ninguna razón en particular, mientras que perder mucho tiempo de procesamiento para nada. ¿Por qué siquiera quieres detener el servicio? Si no tiene nada que hacer, no hará nada más que usar unos pocos bytes de memoria.

Hace varios años escribí este artículo sobre cómo gestionar subprocesos en un servicio. Usted puede utilizar el software en sí o simplemente obtener algunas ideas sobre cómo hacerlo usted mismo.

  1. No. shutdownNow intenta interrumpir las tareas actualmente activas. No hay garantías de que será capaz de hacerlo
  2. Sí lo es. De la documentación:

Una vez que ninguna de estas situaciones se mantiene, el método onDestroy () del servicio es llamado y el servicio se termina de manera efectiva. Todas las operaciones de limpieza (detención de subprocesos, cancelación de registros de receptores) deben completarse al regresar de onDestroy ().

  • No hay razón para que esos miembros sean declarados como estáticos. static están asociados con la clase en lugar de con cualquier objeto, y el uso común es compartir el mismo miembro estático entre diferentes (servicios en su caso) instancias.
  • Debe leer atentamente la documentación sobre todas las posibles implementaciones de la interfaz BlockingQueue<E> , pero dudo que, para casos normales de uso, verá las diferencias desde la perspectiva de rendimiento.
  • Hilos de aplicación vs Hilos de servicio
  • Android - ¿cómo pasar los datos al Runnable en runOnUiThread?
  • Enrutado del reproductor multimedia de Android
  • Múltiples temas en la actividad / servicio de Android
  • ¿Cómo detener Handler Runnable?
  • ¿Cuál es la forma preferida de volver a llamar a una actividad de Android desde un subproceso de servicio
  • Android: actualiza el mapa de bits del hilo del temporizador
  • Actualización de la interfaz de usuario con Runnable y postDelayed no funciona con la aplicación de temporizador
  • Android: ¿Cuándo debo usar un Handler () y cuándo debo usar un Thread?
  • Android 4.0 asynctask al mismo tiempo no es posible
  • Android, Handler se está ejecutando en el hilo principal u otro hilo?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.