Como crear un Service en Android

Lo primero es saber que es un service (servicio). Llamamos Service en Android a las tareas que se ejecutan en segundo plano sin que el usuario tenga que estar interactuar con esa tarea. Un ejemplo claro de uso de servicios es un reproductor de música. La reproducción se iniciará desde una actividad pero la música se debe de seguir escuchando cuando el usuario salga de la aplicación y entre en otras aplicaciones distintas.

Es decir, los services son ideales para las situaciones en las que se necesita que se ejecute un proceso y que no es necesario que el usuario este interactuando con la aplicación como en un reproductor de música, una aplicación que cree copias de seguridad, haga tareas de sincronización, etc.

El ejemplo que vamos a ver paso a paso es como crear un cronometro, ya que es muy sencillo porque la lógica de un cronometro es prácticamente nula y a la vez ilustra bien el funcionamiento de un servicio, ya que de otro modo si queremos cronometrar algo que dure un periodo largo de tiempo no podríamos hacer nada con nuestro móvil mientras se está ejecutando si no usamos un servicio.

Ejemplo de Service en Android: Un cronometro

Para usar servicios en una aplicación no hay que hacer nada especial por lo que creamos nuestro proyecto de Android de la forma habitual.

El layout solo necesitamos que tenga un par de botones para iniciar y parar el cronometro y algún sitio para mostrar el tiempo transcurrido y para eso con lo siguiente es suficiente.


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/cronometro"
        android:layout_width="fill_parent"
        android:layout_height="50dp"
        android:layout_marginBottom="20dp"
        android:layout_marginTop="20dp"
        android:background="#147"
        android:textColor="#fff"
        android:gravity="center" />

    <Button
        android:id="@+id/btn_iniciar"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/btn_iniciar" />

    <Button
        android:id="@+id/btn_finalizar"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/btn_finalizar" />

</LinearLayout>

Ejemplo service android

Dejando de lado el layout que no tiene nada de especial, vamos a centrarnos en el código java. Para nuestra aplicación vamos a tener 2 clases, el activity que será la que nos permitirá interactuar con el cronometro iniciando, parando y viendo el cronometro y otra clase que extenderá la clase Service que va a ser la que se encargue de actualizar el cronometro en segundo plano tanto si se está dentro de la aplicación como si se sale de ella (sin cerrarla)

Desde el Activity es de desde donde iniciamos y paramos el servicio con los métodos startService y stopService que vamos a llamar dentro de las funciones que van a estar asociadas con los botones, y tanto a startService como a stopService hay que pasarles como parámetro un Intent con el servicio que se quiere arrancar o parar.

Con eso ya estamos en disposición de usar el servicio, pero para que se pueda actualizar la UI desde el servicio necesitamos pasarle el activity para que pueda hacerlo (Cronometro.setUpdateListener(this);).

La última cosa a tener en cuenta es que el ciclo de vida del servicio va a estar asociado al de la actividad y por lo tanto en el momento que se cierre (de verdad, es decir cuando se elimina de la lista de aplicaciones en ejecución) el servicio va a fallar porque va a intentar usar métodos de una aplicación que no se está ejecutando, y por lo tanto debemos de recordar parar el servicio en el método onDestroy del activity.

Esto ocurre en este caso porque el servicio interactúa con la interfaz de usuario, pero si no lo hiciese el servicio podría continuar ejecutándose sin ningún problema indefinidamente aunque la aplicación que lo hubiese lanzado estuviese completamente cerrada.


package com.poi.services;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class PoiService extends Activity {

    private TextView textoCronometro;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        textoCronometro = (TextView) findViewById(R.id.cronometro);

        Button startButton = (Button) findViewById(R.id.btn_iniciar);
        startButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                iniciarCronometro();
            }
        });

        Button stopButton = (Button) findViewById(R.id.btn_finalizar);
        stopButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                pararCronometro();
            }
        });

        Cronometro.setUpdateListener(this);

    }
    
    @Override
    protected void onDestroy() {
        // Antes de cerrar la aplicacion se para el servicio (el cronometro)
        pararCronometro();
        super.onDestroy();
    }

    /**
     * Inicia el servicio
     */

    private void iniciarCronometro() {
        Intent service = new Intent(this, Cronometro.class);
        startService(service);
    }

    /**
     * Finaliza el servicio
     */

    private void pararCronometro() {
        Intent service = new Intent(this, Cronometro.class);
        stopService(service);
    }

    /**
     * Actualiza en la interfaz de usuario el tiempo cronometrado
     * 
     * @param tiempo
     */

    public void actualizarCronometro(double tiempo) {
        textoCronometro.setText(String.format("%.2f", tiempo) + "s");
    }
}

Una vez visto el activity vamos a por el Service que es igualmente sencillo. Para que una clase funcione como un servicio es necesario extender la clase Service e implementar los métodos onCreate, onDestroy y onBind.

Para hacer el cronometro podemos usar la clase Timer y con scheduleAtFixedRate le indicamos que cada 10ms por ejemplo actualice el contador que funcionará como cronometro. Con lo que la lógica del servicio es simplemente la de los métodos iniciarCronometro() y pararCronometro() que sirven para arrancar y parar el temporizador.

Además de lo anterior necesitamos que los valores del cronometro se puedan ver en tiempo real en la aplicación y por eso necesitamos un handler al que llamamos cada vez que se actualiza el valor del contador y dentro del handler lo que hacemos es actualizar el valor del cronometro en el activity PoiService de la que tenemos la referencia que obtuvimos desde el onCreate de PoiService mediante el método setUpdateListener.


package com.poi.services;

import java.util.Timer;
import java.util.TimerTask;

import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;

public class Cronometro extends Service {

    private Timer temporizador = new Timer();
    private static final long INTERVALO_ACTUALIZACION = 10; // En ms
    public static PoiService UPDATE_LISTENER;
    private double cronometro = 0;
    private Handler handler;
    
    /**
     * Establece quien va ha recibir las actualizaciones del cronometro
     * 
     * @param poiService
     */

    public static void setUpdateListener(PoiService poiService) {
        UPDATE_LISTENER = poiService;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        iniciarCronometro();

        handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                UPDATE_LISTENER.actualizarCronometro(cronometro);
            }
        };
    }

    @Override
    public void onDestroy() {
        pararCronometro();
        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    private void iniciarCronometro() {
        temporizador.scheduleAtFixedRate(new TimerTask() {
            public void run() {
                cronometro += 0.01;
                handler.sendEmptyMessage(0);
            }
        }, 0, INTERVALO_ACTUALIZACION);
    }

    private void pararCronometro() {
        if (temporizador != null)
            temporizador.cancel();
    }

}

Y con esto está listo el servicio, sin olvidar llamar a los métodos iniciarCronometro() y pararCronometro() desde onCreate() y onDestroy() respectivamente.

Lo último que falta es añadir el servicio al AndroidManifest.xml, para añadir el servicio solo hay que añadirle <service android:name=»nombreServicio» />, para este ejemplo el nombre del servicio es Cronometro (el nombre de la clase que extiende de Service).


<?xml version= "1.0" encoding= "utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.poi.services"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="19"/>

    <application
        android:allowBackup="false"
        android:icon="@drawable/icon"
        android:label="@string/app_name" >
        <activity
            android:name="com.poi.services.PoiService"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name="Cronometro" />
    </application>

</manifest>

Y con esto ya tenemos nuestro aplicación lista para probarla.

Puedes descargar el proyecto del ejemplo completo haciendo click en este enlace.

2 Comments

  1. Alexandro Sifuentes Díaz 29 septiembre, 2015 Reply
  2. Juan Guzman 2 diciembre, 2016 Reply

Leave a Reply