miércoles, 24 de septiembre de 2014

Servicios, conceptos básicos

Un servicio es un componente de aplicación que ejecuta de manera indefinida operaciones en segundo plano y por tanto no necesita de una interfaz de usuario. Un servicio puede ser iniciado desde otro componente y seguir ejecutándose independientemente del estado del componente que lo inició. Además, un componente podrá conectarse a un servicio e interactuar con él a través de una interfaz (IPC - Inter Process Communication, Comunicación Entre Procesos). Por ejemplo, un servicio podría encargarse de gestionar las transacciones de red, de reproducir música, de realizar operaciones de entrada y salida (E/S), o de interactuar con un proveedor de contenido; todo ello desde un segundo plano.

Los servicios pueden ser:

  • Iniciados.
    Se dice que un servicio está iniciado cuando un componente de aplicación (una actividad, por ejemplo) lo utiliza a través de su método startService(). Una vez iniciado, un servicio puede seguir ejecutándose aunque el componente que lo inició deje de existir. Normalmente, un servicio iniciado realiza una tarea concreta y no retorna valor alguno al componente que lo inició. Por ejemplo, un servicio cuyo único propósito es la descarga de cierto archivo desde Internet y que finaliza cuando la descarga ha finalizado.

  • Enlazados.
    Se dice que un servicio está enlazado cuando un componente de aplicación lo utiliza a través de su método bindService(). Un servicio enlazado cuenta con una interfaz que permitirá a los componentes (clientes) interactuar con él. Permite la comunicación entre procesos (IPC). Un servicio enlazado estará ejecutándose mientras tenga componentes enlazados a él, en caso contrario, finalizará su ejecución.

Un servicio puede ser iniciado y enlazado al mismo tiempo. Para ello, tan solo hay que implementar sus métodos onBind() y onStartCommand().

Por defecto, un servicio definido en el archivo manifest de nuestra aplicación será público de cara al exterior. Es decir, que un componente cualquiera de otra aplicación podría acceder a él salvo que indiquemos lo contrario.

Importante. Un servicio se ejecutará por defecto en la hebra principal de su proceso anfitrión. Un servicio no creará su propia hebra de ejecución y tampoco se ejecutará en un proceso diferente a menos que especifiquemos lo contrario. Es decir, si nuestro servicio va a hacer un uso intensivo de CPU  (reproducir un MP3, por ejemplo) o va a realizar operaciones donde se producen bloqueos, deberíamos crear de manera explícita una nueva hebra para realizar esa operación. Usar una nueva hebra reducirá el riesgo de que la aplicación nos deje de responder (ANR). La hebra principal, o hebra de la IU, debería ser usada solo para interactuar con el usuario.

¿Hebra o servicio?

Podríamos decir que un servicio es un componente que puede ejecutarse en segundo plano incluso cuando el usuario no interactúa con nuestra aplicación. Deberíamos crear un servicio siempre que necesitemos este tipo de comportamiento.

Si lo que queremos es realizar una tarea mientras el usuario sigue interactuando con nuestra aplicación, es muy probable que lo que necesitemos sea una hebra secundaria en lugar de un servicio. Por ejemplo, si queremos reproducir música mientras una actividad determinada está en primer plano, deberíamos crear una nueva hebra en su método onCreate(), empezar su ejecución en su método onStart() y parar su ejecución en su método onStop().

En el caso de necesitar una hebra secundaria, es conveniente usar las clases AsyncTask o HandlerThread de Android, en lugar de la clase Thread.

Funcionamiento básico.

Para crear un servicio, crearemos una subclase de la clase Service (o de cualquiera de sus clases derivadas). A la hora de implementar nuestro servicio, sobreescribiremos los métodos más importantes asociados a su ciclo de vida:

onStartCommand()
El sistema llama a este método cada vez que un componente (una actividad, por ejemplo) inicia el servicio llamando al método startService(). Después, el servicio pasará a ejecutarse en segundo plano de manera indefinida. Si implementamos este método, tendremos que proporcionar una manera de poder finalizar el servicio. Para finalizar un servicio usaremos el método stopSelf() desde el propio servicio, o el método stopService() desde un componente. Si queremos que nuestro servicio permita solo enlaces, no implementaremos este método.
onBind()
El sistema llama a este método cada vez que un componente enlaza con el servicio llamando al método bindService() (para RPC - Remote Procedure Call - Llamada de Procedimiento Remoto, por ejemplo). El método devolverá una interfaz de tipo IBinder que los clientes usarán para interactuar con el servicio.
onCreate()
El sistema llamará a este método cuando el servicio se crea por primera vez (antes de llamar a los métodos onStartCommand() o onBind()). Si el servicio ya se encontraba en ejecución, no se llamará a este método.
onDestroy()
El sistema llamará a este método cuando el servicio deja de utilizarse y pasa a ser eliminado. Nuestro servicio debería utilizar este método para liberar algunos recursos como hebras, receptores de avisos, observadores suscritos, etc. Esta es la última llamada que recibirá el servicio durante su ciclo de vida.

Si un componente inicia un servicio llamando al método startService() (que a su vez implica una llamada al método onStartCommand()), el servicio permanecerá en ejecución mientras no se llame al método stopSelf() o mientras que un componente no llame al método stopService().

Si un componente enlaza con un servicio llamando al método bindService() (en este caso el método onStartCommand() no será llamado), el servicio permanecerá en ejecución durante el tiempo que dure el enlace. Si el servicio pierde el enlace con todos sus clientes, éste será eliminado por el sistema.

El sistema se verá obligado a detener un servicio cuando haya poca memoria y una actividad que pasa a un primer plano necesita de recursos. Si un servicio se encuentra enlazado por una actividad con la que interactúa el usuario, la probabilidad de que éste pueda ser eliminada por el sistema es menor. Si el servicio se declara para ser ejecutado en primer plano, nunca será eliminado por el sistema. Por otro lado, si el servicio fue iniciado de manera indefinida en segundo plano, entonces el sistema le acabará asignando una prioridad baja y la probabilidad de ser eliminado será mayor. Si desarrollamos un servicio de estas características, en su implementación deberíamos tener en cuenta que el servicio puede ser eliminado y posteriormente reiniciado por el sistema.

Declarando el servicio en el archivo manifest.

Para publicar un servicio, tendremos que declararlo en el archivo manifest:
<manifest ... >
  ...
  <application ... >
      <service android:name=".ExampleService" />
      ...
  </application>
</manifest>
Podremos especificar atributos adicionales tales como los permisos requeridos para que se pueda usar y el proceso en el que queremos se ejecute. Su atributo android:name es el único atributo obligatorio y su valor está asociado al nombre de la clase del servicio. No deberíamos cambiar este nombre, ya que si lo hacemos, corremos el riesgo de romper el enlace existente entre nuestro servicio y las intenciones que lo inician o enlazan.

Para hacer que nuestra aplicación sea segura, siempre usaremos intenciones explícitas a la hora de usar un servicio y no declararemos filtros de intenciones en su interior como norma general.

También podemos restringir su uso a la aplicación que lo alberga utilizando el atributo android:exported="false". Esto evitará que otras aplicaciones puedan usar nuestro servicio incluso si éstas utilizan intenciones explícitas.

Iniciando un servicio.

Se trata de un servicio que es utilizado por un componente que llama al método startService() que a su vez hace que el sistema llame al método onStartCommand() del servicio.

Cuando un servicio es iniciado por primera vez, éste se crea y pasa a ejecutarse de manera indefinida. El servicio cuenta con su propio ciclo de vida que es distinto al ciclo de vida del componente que lo inició. Es decir, el servicio podrá continuar su ejecución incluso si el componente que lo inició es eliminado por el sistema. El servicio finaliza cuando éste llama al método stopSelf(), o bien, cuando otro componente llama al método stopService().

Para iniciar un servicio desde una actividad, por ejemplo, llamaremos al método startService() y le pasaremos como parámetro una intención con el servicio al que va dirigida junto con los datos necesarios para su uso. El servicio recibirá la intención a través del método onStartCommand().

Por ejemplo, supongamos que una actividad necesita guardar datos en una base de datos remota. La actividad puede iniciar el servicio asociado y entregar esos datos a través de una intención que irá como argumento del método startService(). El servicio recibirá la intención con los datos desde el método onStartCommand(), se conectará a Internet y realizará la transacción con la base de datos. Una vez finalizada la transacción, el servicio finaliza (ejecuta el método stopSelf()) y desaparece.

A la hora de crear un servicio de este tipo, podremos hacerlo extendiendo una de las dos clases siguientes:

Service
Esta es la clase base a todos los servicios. Si usamos esta clase, es importante que la ejecución del servicio se haga en una hebra distinta a la hebra principal. Si no lo hacemos así,  esto podría ocasionar problemas de rendimiento en la actividad con la que el usuario estuviese interactuando en ese momento.
IntentService
Se trata de una subclase de la clase Service que usa su propia hebra para gestionar todas las peticiones de inicio que reciba el servicio. Esta es la mejor opción si no necesitamos que nuestro servicio gestione las peticiones de manera concurrente. Adicionalmente, tendremos que implementar el método onHandleIntent(), desde donde podremos procesar las intenciones asociadas a las peticiones de inicio secuencialmente.

Extendiendo la clase IntentService.

Debido a que la mayoría de los servicios no tienen que gestionar sus peticiones de manera simultánea, la mejor opción es que implementemos nuestros servicios extendiendo la clase IntentService.

La clase IntentService funciona de la siguiente manera:
  • Crea una hebra distinta a la principal para ejecutar todas y cada una de las peticiones de inicio que recibe. Todas las peticiones pasan por el método onStartCommand().
  • Si se reciben más de una intención a la vez, éstas se irán encolando y se irán entregando de una en una al método onHandleIntent(), donde las acabaremos procesando.
  • El servicio finaliza automáticamente tan pronto hayan sido procesadas todas las peticiones. Ya no tendremos que invocar el método stopSelf() en nuestro código.
  • Nos proporciona una implementación por defecto para el método onBind() que devuelve el valor null.
  • Nos proporciona una implementación por defecto para el método onStartCommand() que se encargará de ir encolando cada una de las intenciones que posteriormente serán entregadas al método onHandleIntent().

Por lo tanto, solo tendremos que encargarnos de implementar el método onHandleIntent() y como extra, el constructor de la clase. Desde el constructor llamaremos al constructor de la clase padre especificándole como argumento el nombre de la hebra asociada a la ejecución del servicio.

Un ejemplo de implementación de la clase IntentService:
public class HelloIntentService extends IntentService {

  /**
   * El constructor es necesario.
   * Llamamos al constructor de la clase padre y le pasamos como argumento
   * el nombre de nueva hebra asociada a la ejecución del servicio.
   */
  public HelloIntentService() {
      super("HelloIntentService");
  }

  /**
   * Se llamará a este método por cada petición recibida.
   * El método se ejecutará en una hebra en segundo plano.
   * Cuando el método retorna, el servicio finalizará si no quedan
   * peticiones pendientes.
   */
  @Override
  protected void onHandleIntent(Intent intent) {
      // Aquí es donde procesamos la intención (por ejemplo, descargar un archivo).
      // En el ejemplo introducimos un retardo de 5 segundos.
      long endTime = System.currentTimeMillis() + 5*1000;
      while (System.currentTimeMillis() < endTime) {
          synchronized (this) {
              try {
                  wait(endTime - System.currentTimeMillis());
              } catch (Exception e) {
              }
          }
      }
  }
}
Eso es todo: un constructor y una implementación para el método onHandleIntent().

Si vamos a sobreescribir otros métodos como onCreate, onStartCommand(), o onDestroy(), tendremos que asegurarnos de llamar a sus correspondientes métodos de la clase padre para tener en cuenta la hebra asociada al servicio. Desde el método onBind() no será necesario invocar al método de la clase padre.

Extendiendo la clase Service.

Como acabamos de ver, implementar nuestro servicio extendiendo la clase IntentService es sencillo. Sin embargo, si lo que necesitamos es que nuestro servicio gestione múltiples hebras (en vez de procesar las peticiones de inicio a través de una cola de trabajos), tendremos que extender la clase Service.

En el siguiente ejemplo utilizamos la clase Service para hacer exactamente el mismo trabajo que hacíamos en el ejemplo anterior utilizando la clase IntentService. Es decir, por cada petición de inicio, usamos una sola hebra para realizar el trabajo y procesamos las solicitudes de una en una:
public class HelloService extends Service {
  private Looper mServiceLooper;
  private ServiceHandler mServiceHandler;

  // Gestor de mensajes de la hebra
  private final class ServiceHandler extends Handler {
      public ServiceHandler(Looper looper) {
          super(looper);
      }
      @Override
      public void handleMessage(Message msg) {
          // Procesamos cada solicitud; descargar un archivo, por ejemplo.
          // Para nuestro ejemplo, introducimos un retardo de 5 segundos.
          long endTime = System.currentTimeMillis() + 5*1000;
          while (System.currentTimeMillis() < endTime) {
              synchronized (this) {
                  try {
                      wait(endTime - System.currentTimeMillis());
                  } catch (Exception e) {
                  }
              }
          }
          // Finalizamos el servicio
          stopSelf(msg.arg1);
      }
  }

  @Override
  public void onCreate() {
    // Iniciamos la hebra que ejecutará el servicio. Como no queremos bloquear
    // la hebra principal, hebra por defecto en la que se ejecuta el servicio,
    // creamos una hebra distinta. Además, le asignaremos una prioridad de
    // ejecución baja para evitar que la hebra principal se vea afectada por
    // el uso de CPU o bloqueos del servicio.
    HandlerThread thread = new HandlerThread("ServiceStartArguments",
            Process.THREAD_PRIORITY_BACKGROUND);
    thread.start();

    // Configuramos el gestor de mensajes.
    mServiceLooper = thread.getLooper();
    mServiceHandler = new ServiceHandler(mServiceLooper);
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();

      // Para cada petición de inicio, enviamos un mensaje para iniciar el trabajo y
      // pasamos el ID de la petición para poder finalizar el servicio de la manera
      // adecuada.
      Message msg = mServiceHandler.obtainMessage();
      msg.arg1 = startId;
      mServiceHandler.sendMessage(msg);

      // Indicamos qué debe hacer el sistema en el caso de interrumpir el servicio
      // de manera inesperada. En este caso le indicamos que lo vuelva a iniciar en
      // cuanto le sea posible.
      return START_STICKY;
  }

  @Override
  public IBinder onBind(Intent intent) {
      // No utilizamos enlaces al servicio.
      return null;
  }

  @Override
  public void onDestroy() {
    Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
  }
}
Definir un servicio de esta manera implica más trabajo que usando la clase IntentService. Sin embargo, hacerlo de esta manera nos proporciona la flexibilidad suficiente para poder tratar las peticiones de manera simultánea. No es el caso de nuestro ejemplo pero podríamos haber creado una hebra para cada una de las peticiones y haberlas ejecutado concurrentemente.

El método onStartCommand() devuelve un entero. Este valor indica al sistema cómo debe actuar después de haberse visto obligado a interrumpir la ejecución del servicio. El método puede devolver uno de los siguientes valores:

START_STICKY
Una vez el sistema vuelve a disponer de recursos suficientes, vuelve a iniciar el servicio llamando al método onStartCommand(), entregándole una intención nula (null) como argumento. La intención nula la podremos utilizar como indicador para reestablecer el servicio de manera adecuada.
START_NOT_STICKY
Una vez el sistema vuelve a disponer de recursos sufientes, no se molestará en volver iniciar el servicio.
START_REDELIVER_INTENT
Una vez el sistema vuelve a disponer de recursos suficientes, vuelve a iniciar el servicio llamando al método onStartCommand(), entregándole la última intención interrumpida como argumento. Lo utilizaremos con servicios donde queremos asegurar la ejecución de las tareas.

Iniciando un servicio.

Podemos iniciar un servicio desde una actividad u otro componente de aplicación utilizando el método startService(). Le pasaremos una intención como argumento y en ésta, especificaremos el servicio que queremos iniciar. El sistema se encargará de invocar el método onStartCommand del servicio, al que le pasará la intención (no hay que llamar a este método directamente).

Por ejemplo, una actividad puede iniciar el servicio del ejemplo anterior (HelloService) utilizando una intención explícita de la siguiente manera:
Intent intent = new Intent(this, HelloService.class);
startService(intent);
El método startService() retorna inmediatamente y el sistema llama a su método onStartCommand(). Si el servicio no se estaba ejecutando, se llamará al método onCreate() seguido del método onStartCommand().

Si el servicio no permite enlaces, el método startService() será la única manera en la que podremos comunicarnos con él. Si queremos que el servicio nos devuelva un valor de vuelta, podremos hacerlo entregándole una intención PendingIntent y capturando el mensaje de vuelta con el método getBroadcast().

Son múltiples las llamadas que un servicio recibe a su método onStartCommand(). Sin embargo, para finalizar su ejecución, tan solo necesitaremos invocar el método stopSelf() o stopService().

Finalizando un servicio.

Un servicio que ya se está ejecutando debe ser capaz de gestionar su propio ciclo de vida. Es decir, que el sistema no debería de interrumpir su ejecución, a menos que éste se vea obligado. Por ello, será el propio servicio responsable de finalizar su ejecución llamando al método stopSelf(), o bien, será otro componente el encargado de finalizar la ejecución del servicio llamando al método stopService(). En cualquiera de los dos casos, el sistema acabará con el servicio en cuanto le sea posible.

Cuando nuestro servicio es capaz de trabajar con múltiples peticiones de manera concurrente, tendremos que poner especial atención a la hora de finalizar su ejecución. Finalizar el servicio desde una petición, sin más, podría interrumpir la ejecución de una segunda petición de manera inesperada. Para evitar este contratiempo, podemos utilizar el argumento extra que proporciona el método stopSelf(int). Es decir, cada vez que llamemos a este método, le pasaremos como argumento el ID de la petición actual (argumento startId del método onStartCommand()). De esta manera, se puede saber si la intención que intenta finalizar el servicio es la más reciente o no. En caso afirmativo, el servicio finalizará.

Importante. Nuestra aplicación debe finalizar los servicios que utiliza para liberar recursos y para no consumir batería innecesariamente. Si fuera necesario, podemos detener un servicio desde otros componentes haciendo uso del método stopCommand().

Enlazando un servicio.

Un servicio enlazado es un servicio que permite ser utilizado desde desde un componente de aplicación a través del método bindService() con el objetivo de establecer una o varias conexiones persistentes. Por razones de coherencia, no deberíamos permitir que servicios enlazados fuesen iniciados a través del método startService().

Utilizaremos un servicio enlazado cuando queramos interactuar con él desde actividades u otros componentes o para publicar parte de la funcionalidad de nuestra aplicación a otras aplicaciones mediante comunicación entre procesos (IPC).

Para crear un servicio enlazado, debemos implementar el método onBind() que devolverá un objeto de tipo IBinder que define la interfaz para interactuar con el servicio. Para obtener la interfaz desde un componente y poder empezar a usarla, llamaremos al método bindService().

Lo primero que tenemos que hacer a la hora de crear un servicio enlazado es definir la interfaz de comunicación con sus clientes. Esta interfaz entre el servicio y el cliente será una implementación de la clase IBinder y será devuelta por el método onBind().

Varios clientes podrán estar conectados al mismo servicio a la vez. Cuando un cliente ha acabado de interactuar con el servicio, éste puede acabar la conexión con él llamando al método unbindService(). El servicio permanecerá activo mientras disponga de componentes enlazados. Si el servicio no dispone de enlaces, éste será finalizado.

Enviando notificaciones al usuario.

Desde un servicio podremos comunicarnos con el usuario de dos formas: a través de notificaciones emergentes, o bien,  a través de notificaciones de la barra de estado.

Una notificación emergente se trata de un mensaje que se muestra al usuario superponiéndolo a la ventana actual durante un breve período de tiempo.

Una notificación de la barra de estado se trata de un icono más un mensaje en la barra de estado y con los que el usuario podrá interactuar (iniciar otra actividad al hacer clic, por ejemplo).

Generalmente la barra de estado es la mejor opción para comunicar al usuario que un trabajo en segundo plano ha finalizado (la descarga de un archivo, por ejemplo).

Ejecutando un servicio en primer plano.

Un servicio en primer plano es un servicio que realiza una actividad de la que el usuario es consciente en todo momento y que no será susceptible de ser eliminado por el sistema cuando éste necesite de memoria. Un servicio en primer plano dispone de su propia notificación en la barra de estado y ésta permanecerá durante el tiempo que el servicio esté disponible.

Por ejemplo, un reproductor de música debería ejecutarse en primer plano ya que la reproducción de música es una operación con la que el usuario podría interactuar en todo momento. La notificación asociada en la barra de estado mostrará la canción actual y permitirá al usuario lanzar la actividad relacionada al reproductor de música para interactuar con él.

Para que un servicio se ejecute en primer plano, utilizaremos el método startForeground(). Este método toma dos parámetros: un número entero que identifica la notificación y un objeto de tipo Notification que irá en la barra de estado:
Notification notification = new Notification(R.drawable.icon,     
    getText(R.string.ticker_text),
    System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this,
    getText(R.string.notification_title),
    getText(R.string.notification_message),
    pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);
Nota. El ID que especifiquemos para el método startForeground() no puede ser cero.

Para detener un servicio en primer plano usaremos el método stopForeground(). Podremos indicarle como parámetro, si queremos eliminar la notificación de la barra de estado o por contra, deseamos conservarla. Este método no finaliza el servicio, lo que hace es sacarlo del primer plano. En cualquier caso, si el servicio finaliza, la notificación asociada desaparecerá de la barra de estado.

Gestionando el ciclo de vida de un servicio.

El ciclo de vida de un servicio es mucho más sencillo que el ciclo de vida de una actividad. No obstante, es importante prestar atención a cómo crear y finalizar un servicio ya que un servicio se ejecutará en segundo plano sin la intervención del usuario.

El ciclo de vida de un servicio, desde que éste es creado y hasta que éste es finalizado, puede seguir dos caminos:
  • Si se trata de un servicio iniciado. Un servicio que se crea desde un componente cuando se llama por primera vez al método startService(). El servicio pasará a ejecutarse de manera indefinida hasta que: el propio servicio llame al método stopSelf(), o bien, otro componente llame al método stopService(). Cuando un servicio finaliza, el sistema lo elimina.
  • Si se trata de un servicio enlazado. Un servicio que se crea desde un componente cuando se llama por primera vez al método bindService(). El método devuelve una interfaz de tipo IBinder, necesaria para la interacción con el servicio. El cliente podrá eliminar su enlace con el servicio utilizando el método unbindService(). Múltiples clientes podrán enlazar con el mismo servicio a la vez. Cuando un servicio no tiene clientes enlazados, el sistema lo elimina.

Habrá situaciones en las que un servicio podrá ser iniciado y enlazado a la vez. Es decir, que podremos enlazar a un servicio que previamente ha sido iniciado con el método startService(). Por ejemplo, un servicio para reproducir música en segundo plano podría haber sido iniciado con el método startService() pasándole una intención en la que se especificaba la música que se quería escuchar. Más tarde, el usuario decide consultar información concreta sobre la canción actual y para ello inicia una actividad que establece un enlace al servicio mediante el método bindService(). En este tipo de casos, los métodos stopService() y stopSelf() no finalizarán el servicio hasta que todos sus clientes se hayan desconectado.

Implementado los métodos del ciclo de vida.

Para controlar el estado de nuestro servicio, disponemos de un conjunto de métodos asociados a su ciclo de vida:
public class ExampleService extends Service {
    int mStartMode;       // Estrategia a seguir si el servicio es reiniciado
                          // por el sistema.
    IBinder mBinder;      // Interfaz para la interacción cliente/servicio.
    boolean mAllowRebind; // Indica si se debe utilizar onRebind

    @Override
    public void onCreate() {
        // El servicio se crea con la primera petición.
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // Se ha llamado al método startService() desde algún componente.
        return mStartMode;
    }
    @Override
    public IBinder onBind(Intent intent) {
        // Un cliente se ha conectado al servicio llamando al método bindService().
        // Devolvemos la interfaz para interactuar con el servicio.
        return mBinder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        // Un cliente se ha desconectado del servicio con el método unbindService().
        return mAllowRebind;
    }
    @Override
    public void onRebind(Intent intent) {
        // Un cliente vuelve a conectarse al servicio con bindService().
    }
    @Override
    public void onDestroy() {
        // El servicio finaliza.
    }
}
Nota. En estos métodos no hace falta llamar a las implementaciones de la clase padre, como si sucedía con los métodos asociados a una actividad.

Ciclo de vida de un servicio
Imagen 1. Ciclo de vida de un servicio. El diagrama de la izquierda muestra el ciclo de vida para un servicio iniciado con startService(). El diagrama de la derecha muestra el ciclo de vida para un servicio enlazado con bindService().
En el ciclo de vida de un servicio podemos distinguir dos subciclos bien diferenciados:
  • El subciclo completo que comprende desde el método onCreate() y hasta el método onDestroy(). Utilizaremos el método onCreate() para configurar el servicio y el método onDestroy() para liberar recursos. Por ejemplo, podríamos crear una hebra para reproducir música en el método onCreate() y detener su ejecución en el método onDestroy().

    Los métodos onCreate() y onDestroy() serán invocados tanto para servicios iniciados como para servicios enlazados.

  • El subciblo de actividad (active lifetime), que comienza cuando se llama al método onStartCommand() o cuando se llama al método onBind(). Estos métodos se encargarán de procesar las intenciones que reciben a través de los métodos startService() y bindService() respectivamente.

    Si el servicio es iniciado, este ciclo coincide con el ciclo anterior (el servicio se encontrará aún activo incluso después de que el método onStartCommand() haya finalizado) y finalizará cuando se invoque el método stopSelf() o el método stopService(). Si el servicio es enlazado, este ciclo finalizará cuando el método onUnBind() haya finalizado.

Nota. Cuando un servicio se detiene usando el método stopSelf() o stopService(), no hay un método onStop() previo a onDestroy(). A menos que el servicio se encuentre enlazado a un cliente, el sistema llamará al método onDestroy().

No hay comentarios:

Publicar un comentario