Android Services desde cero

Los servicios nos acompa√Īan en Android desde el primer d√≠a.¬†A√ļn as√≠, muchas de las aplicaciones que hacemos¬†no los necesitan o los usan muy superficialmente. Por ello, para muchos desarrolladores se han convertido en unos desconocidos. Este post es el primero de una serie en la que describiremos¬†con detalle, y con cierto esp√≠ritu revisionista, las posibilidades que nos ofrece la clase Service y sus asociados en el framework de la plataforma.

Un poco de Contexto

Service nació como uno de los componentes fundamentales de la interfaz de programación de aplicaciones (API) de Android. Por aquél entonces el término componente se usaba explícitamente para referirse a cuatro elementos del framework: Activity, Service, ContentProvider y BroadcastReceiver. Actualmente el uso de la palabra componente parece difuminarse en la documentación oficial, pero las dos características que hacen especiales a estos elementos siguen vigentes.

Por un lado, todos ellos son componentes¬†gestionados: clases dise√Īadas para ser extendidas y cuyas instancias no deben ser usadas¬†de cualquier manera en las aplicaciones, sino que deben adecuarse a un ciclo de vida predefinido y dirigido por el sistema. No creamos objetos de nuestras clases Activity, Service o ContentProvider, los arrancamos indirectamente utilizando Intents o ContentResolvers. No ejecutamos¬†m√©todos en nuestros BroadcastReceiver, sino que los registramos y esperamos a que reciban llamadas en su m√©todo onReceive(…).

En segundo lugar, y de forma √ļnica, estas cuatro clases son las¬†que introducen contexto en nuestro c√≥digo. Literalmente, son las que permiten acceder a objetos¬†de la clase Context, fundamentales para que las aplicaciones puedan acceder a recursos del dispositivo o servicios de sistema. Activity y Service son contextos ellos mismos, ya que extienden la clase Context. ContentProvider da acceso a un contexto inmutable con su m√©todo getContext(), y BroadcastReceiver lo recibe como par√°metro de onReceive(…).

Activity, ContentProvider, BroadcastReceiver y Service : proveedores de Context

Debido a esto, los (antes llamados) componentes tienen un gran peso en la estructura de nuestras apps. Cada uno de ellos tiene caracter√≠sticas √ļnicas que los dotan de un prop√≥sito diferenciado. Veamos cu√°l es el de los servicios.

Nacido para servir

Vayamos a la definición original en la guía de desarrollo para tener claro cuál es la misión de la clase Service en Android.

Un Service es un componente de una aplicaci√≥n que puede realizar operaciones de larga ejecuci√≥n en segundo plano y que no proporciona una interfaz de usuario. Otro componente de la aplicaci√≥n puede iniciar un servicio y continuar√° ejecut√°ndose en segundo plano aunque el usuario cambie a otra aplicaci√≥n. Adem√°s, un componente puede enlazarse con un servicio para interactuar con √©l e incluso realizar una comunicaci√≥n entre procesos (IPC). Por ejemplo, un servicio puede manejar transacciones de red, reproducir m√ļsica, realizar I/O de archivos o interactuar con un proveedor de contenido, todo en segundo plano.

La primera línea define claramente la misión de los servicios en Android: ejecutar operaciones de larga duración. Para ello, y en oposición directa a la misión de una Activity, un Service no proporciona una interfaz de usuario, o dicho de otro modo, no depende de una interfaz de usuario.

¬ŅQu√© operaciones de larga duraci√≥n puede interesarnos realizar en un dispositivo m√≥vil? Pensemos en una aplicaci√≥n como ownCloud, que permite acceder, gestionar y compartir ficheros en nuestra nube privada. La app de ownCloud ejecuta tareas que requieren descargar o subir ficheros a un servidor ownCloud remoto. T√≠picamente, un usuario que quiera subir, por ejemplo, varios v√≠deos desde su m√≥vil a su cuenta de ownCloud preferir√° salir de la aplicaci√≥n y seguir con su vida, antes que mirar pacientemente la barra de progreso con la app abierta¬†mientras se completa la transferencia.

Afortunadamente, contamos con Service para ayudarnos a implementar este tipo de trabajos. La clase Activity no es apropiada para ello, ya que está concebida para proporcionar una interfaz de usuario a la que está íntimamente ligada. Una instancia de Activity, de forma general, debería interrumpir o pausar cualquier trabajo que ejecute en segundo plano cuando el usuario la retira de pantalla.

¬ŅY qu√© proporciona un¬†Service que no tiene una Activity para facilitar nuestra vida como desarrolladores de tareas largas? La diferencia fundamental est√° en su ciclo de vida. Cuando queremos desarrollar un servicio, debemos escribir una clase que extienda a la clase¬†Service o alguna de sus herederas. Al hacerlo, tendremos que sobrecargar algunos m√©todos espec√≠ficos de¬†Service que ser√°n llamados¬†en nuestra aplicaci√≥n en respuesta a las peticiones de los componentes que act√ļen como clientes del servicio. En dichos m√©todos tendremos que iniciar la ejecuci√≥n de las tareas a ejecutar. El framework se compromete, por medio de la especificaci√≥n del ciclo de vido de Service, a no interrumpir la operaci√≥n del servicio aunque los cliente que inicien operaciones en √©l se retiren, salvo que se den determinadas condiciones.

¬ŅEs esto suficiente para desarrollar operaciones de largo duraci√≥n? Desde luego es necesario, pero debemos ser cuidadosos con lo que no proporciona¬†la clase¬†Service. A pesar de que su misi√≥n es la ejecuci√≥n de tareas en segundo plano, la gran mayor√≠a de los m√©todos de entrada en los servicios son ejecutados por el framework en el hilo principal de la aplicaci√≥n, y no en un hilo en segundo plano, como ser√≠a f√°cil asumir. Android deja bajo nuestra responsabilidad garantizar que las tareas se ejecutar√°n¬†en otros hilos para no ocupar durante demasiado tiempo el hilo principal y que la app funcione correctamente.

Tres eran tres

Tras la definición, la guía de desarrollo describe tres tipos de servicios (1), aunque quizá es más exacto pensar en tres modos de ejecución de los mismos.

Los modos de ejecución clásicos son los de servicio iniciado y servicio enlazado. Los servicios iniciados (2) soportan la misión fundamental de la clase Service, sólo en este modo de funcionamiento se garantiza que el servicio permanecerá activo indefinidamente hasta que él mismo indique que ha terminado su trabajo o hasta que lo pare explícitamente otro componente (3).

Por otro lado, los servicios enlazados (4) ofrecen la posibilidad de que uno o varios componentes cliente obtengan una referencia a un objeto con el que interactuar de forma directa con el servicio, mediante una interfaz Java al uso, en lugar de con peticiones asíncronas lanzadas vía Intents. Los servicios enlazados no se ejecutan indefinidamente, sino que serán interrumpidos cuando todos los componentes cliente deshagan el enlace. Esto va en contra del interés del Service de ejecutar tareas de forma independiente de sus clientes, pero es fácil  de solucionar ya que un mismo servicio puede ejecutarse en los modos iniciado y enlazado al mismo tiempo.

El tercer modo de funcionamiento es el de servicios programados (5). Fue incorporado con Android 5 (nivel de API 21), y Google recomienda expresamente su uso frente a los otros dos para todas las versiones de Android que lo soportan. Su uso supone un cambio drástico para las aplicaciones, ya que los servicios programados no se ejecutan inmediatamente después de las peticiones de sus clientes. Una petición de un trabajo programado supone que el cliente defina algunas condiciones que deben ser ciertas para que el trabajo sea realizado por el servicio. El framework garantiza que el servicio no intentará ejecutarse sin que esas condiciones se cumplan, pero no se compromete a que lo haga tan pronto como se cumplan.

Continuar√°

En próximas entregas de esta serie estudiaremos más detalles sobre cómo funcionan los tres modos de ejecución de un Service, cómo podemos sacar partido de cada uno y cómo pueden afectar a nuestras apps sus distintas restricciones.

Otros enlaces relacionados

Usa la librería Android Priority Job Queue para tus tareas en segundo plano

Conferencia de ownCloud 2016

 

(1) En la documentaci√≥n original en ingl√©s, la traducci√≥n al espa√Īol no est√° actualizada y s√≥lo incluye los dos modos cl√°sicos.

(2) Started service.

(3) En condiciones de escasez de recursos el sistema puede decidir matar el proceso que ejecuta un servicio iniciado, pero se considera un caso de funcionamiento excepcional.

(4) Bound service.

(5) Scheduled service.

Acerca de David A. Velasco

Arquitecto software y desarrollador móvil nativo con cierto favoritismo hacia Android. Obsesionado con la creación de valor para el usuario y la prevención de riesgos durante el desarrollo. Nada me emociona más que priorizar la pila de producto.

Deja un comentario

Responsable ¬Ľ Solidgear.
Finalidad ¬Ľ Gestionar los comentarios.
Legitimaci√≥n ¬Ľ Tu consentimiento.
Destinatarios ¬Ľ Los datos que me facilitas estar√°n ubicados en los servidores SolidgearGroup dentro de la UE.
Derechos ¬Ľ Podr√°s ejercer tus derechos, entre otros, a acceder, rectificar, limitar y suprimir tus datos.

¬ŅNecesitas una estimaci√≥n?

Calcula ahora