Retomamos nuestro Curso Android para hablarte en esta ocasión de los servicios.

Un servicio es una sub-aplicación que se ejecuta en segundo plano, normalmente sin interacción con el usuario, y que realiza una serie de tareas dependiendo de la activación de determinados eventos externos (aquellos para cuya atención ha sido programado), tales como la recepción de un correo electrónico, la activación de un temporizador, etc.

En Android los servicios se ejecutan en el contexto del proceso al que pertenecen, que no es más que aquel que los ha creado, por lo que pueden acceder a cualquiera de los objetos creados por éste, a excepción de los relacionados con la interfase gráfica, que tal como sucedía con los Threads, son accesibles unicamente desde el flujo principal.

Tal y como se aprecia en la imagen inferior, el ciclo de vida de un servicio se sitúa entre la activación de los eventos onCreate y onDestroy, garantizando el sistema que una activación del evento onCreate asociado a un servicio se complementará mediante la activación del evento onDestroy correspondiente.

Ciclo de vida de un servicio

Aunque no es obligatorio, lo habitual es que el evento onCreate asociado a un servicio se encargue de la inicialización de un objeto de tipo BroadcastReceiver que se encargará de monitorizar los eventos (externos o internos) que activen o desactiven el servicio, mientras que el evento onDestroy se encargará de destruir el mencionado objeto.

Definición de un servicio

Al igual que ocurre con las actividades, los servicios deben definirse en el manifiesto de nuestra aplicación, tal como se indica a continuación, teniendo en cuenta que la clase que implemente el servicio deberá heredar de la superclase Service.

<service android:name=".ServiceClassName" />

Instanciación de un servicio

Al igual que ocurre con las actividades, no pueden cohexistir en el sistema dos servicios de la misma clase, por lo que antes de instanciar un servicio deberemos comprobar si éste ya se está ejecutando, procediendo a activarlo mediante un evento o a instanciarlo según sea el caso, tal como se muestra en el ejemplo.

boolean is_running = serviceRunning();
Intent intent = is_running ? new Intent() : new Intent(context, service_class);
intent.setAction(action);
if (is_running) context.sendBroadcast(intent);
else context.startService(intent);

Destrucción de un servicio

La activación de un evento provoca que una porción de código asociada a un servicio se ejecute, pero éste permanece residente en memoria, a la espera de que el mismo u otro evento se active, hasta que el servicio es explicitamente destruido, lo cual puede hacerse mediante la llamada stopService (a la que se le pasa como parámetro el tipo de servicio a deterner) o mediante la llamada al procedimiento stopSelf asociada al servicio.

protected synchronized static void stopService(String service_name, Service service)
{
  Debug.d(service_name, "Stopping service");
  service.stopSelf();
}

protected synchronized static void stopService(Context context, String service_name, Classservice_class)
{
  Debug.d(service_name, "Stopping service");
  context.stopService(new Intent(context, service_class));
}

Activando un evento en un servicio

Tal como hemos indicado con anterioridad, lo habitual es que todo servicio lleve asociado un objeto BroadcastReceiver que sirve como punto de entrada al servicio.

Esta instancia de la clase BroadcastReceiver implementa el código asociado al procesamiento de los eventos externos (del sistema) que atiende el servicio, así como el código asociado a los eventos de la propia aplicación que pueden modificar el comportamiento del servicio, que suelen asociarse a eventos externos que se procesan parcialmente en otros contextos o a cambios en los parámetros de configuración de la aplicación.

Aunque no es obligatorio, es una buena práctica definir una operación estática asociada al servicio para cada uno de los puntos de entrada al mismo, de forma que sea en ese procedimiento en el que se compruebe si éste se encuentra o no en ejecución y se proceda a su creación o activación, según el caso.

public static synchronized void screenIsOff(Context context)
{
  boolean is_running = isRunning();
  Intent intent = is_running ? new Intent() : new Intent(context, ScreenStateService.class);
  intent.setAction(INTERNAL_ACTION_SCREEN_OFF);
  if (is_running) context.sendBroadcast(intent);
  else context.startService(intent);
}

En el ejemplo anterior, el BroadcastReceiver asociado al servicio captura los eventos de tipo INTERNAL_ACTION_SCREEN_OFF, lo que provoca la ejecución de la instancia del servicio, mientras que si el servicio no existe se procede a crearlo mediante el procedimiento startService.

Nótese que el procedimiento screenIsOff se define como static, lo que permite que sea ejecutado fuera del contexto de un servicio, y como synchronized, lo que impide que otro Thread ejecute cualquier otro procedimiento de la clase marcado como synchronized hasta que la ejecución de éste acabe, lo que garantiza acceso exclusivo a la clase mientras el servicio se crea.

Capturas de pantalla de nuestra aplicación

Primera activación de un servicio tras su instanciación

Al instanciar un servicio, y justo después de activarse el evento onCreate, se produce la activación del evento onStartCommand, que recibe como parámetro un Intent que puede procesarse usando el BroadcastReceiver que asociemos al servicio.

public int onStartCommand(Intent intent, int flags, int start_id)
{
  iBroadcastReceiver.onReceive(this, intent);
  return super.onStartCommand(intent, flags, start_id);
}

Y hasta aquí las características principales de los servicios, que podrás ver en detalle en nuestro programa de ejemplo, que hemos adaptado para que el repintado del widget se asocie a uno, lo que en condiciones reales permitiría ejecutar procedimientos y cálculos complejos ya que, como recordarás, el tiempo de proceso máximo asociado a los eventos del sistema es de cinco segundos.

La próxima semana finalizaremos nuestro curso Android, que esperamos haya sido de vuestro interés, otorgando privilegios de root a nuestro gestor de archivos, lo que permitirá acceder a archivos de sistema, para lo que necesitarás haber rooteado previamente el terminal.

Como siempre, os invitamos a visitar el Hilo oficial del Curso de Android de Foromóviles, donde podéis hacernos llegar vuestras dudas y comentarios.

Sin comentarios

Deja una respuesta