Lanzar Service con IntentService en Android

Buenas tardes en esta entrada vamos a ver como usar la clase IntentService.

En otras entradas hemos visto como comenzar un Service usando el método startService() y detenerlo mediante el método stopService(). Hemos visto que se pueden ejecutar tareas que consumen bastante tiempo en un hilo por separado, es decir, no en el mismo hilo que el principal. Es importante ver que una vez que el Service se ha terminado, se debe detener tan pronto como sea posible para no ocupar los recursos del sistema. Es por ello por lo que hemos usado el método stopSelf() para parar un servicio cuando este se ha terminado.

Afortunadamente hay mas herramientas con las que podemos tratar este caso, y es mediante el uso de la clase IntentService. Se ejecuta al igual que un Service, y ejecuta su tarea dentro de un hilo separado, y cuando la operación o tarea se ha completado, este Service se detiene automáticamente.


Vamos a ponernos manos a la obra:

  • Creación del Proyecto

    Para crear nuestro proyecto, vamos a ir a File -> New -> Android Application Project. Si por algún caso no lo vemos directamente, vamos a “Other”, donde una ventana nos aparecerá con una lista. En esta lista deberemos ir a la carpeta de “Android”, y allí elegir “Android Application Project”. Si por otro lado deseamos filtrar en la lista, podemos escribir “Android” y se filtrará la lista, haciendola mas pequeña.

    Una vez que tengamos seleccionado “Android Application Project”, nos aparecerá el asistente de creación del proyecto, y en esta primera ventana podremos establecer el nombre de nuestro proyecto. En mi caso lo he llamado “EjemploIntentService”, aunque podéis llamarlo como queráis. El resto de opciones podéis dejarlas igual.

    A medida que vamos avanzando, nos aparecerá una ventana en la que configuraremos el nombre de nuestra Activity. En mi caso la he llamado “IntentServiceActivity”, y su layout será “activity_service.activity.xml”.

    Una vez terminemos estos pasos, podremos continuar construyendo nuestra aplicación.

  • IntentServiceActivity

    Cuando acabemos el asistente de creación del proyecto, tendremos a nuestra disposición una primera actividad, a la cual le hemos dado anteriormente el nombre, en mi caso era “IntentServiceActivity”. Su layout se llamaba “activity_service.xml” y se encuentra bajo la ruta “res/layout/activity_service.xml”. Vamos a editarlo para que quede como a continuación:

    		<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    		    xmlns:tools="http://schemas.android.com/tools"
    		    android:layout_width="match_parent"
    		    android:layout_height="match_parent"
    		    android:orientation="vertical"
    		    tools:context=".ServiceActivity" >
    		
    		    <Button
    		        android:id="@+id/btnComenzar"
    		        android:layout_width="fill_parent"
    		        android:layout_height="wrap_content"
    		        android:onClick="startService"
    		        android:text="Comenzar Servicio" />
    		
    		</LinearLayout>
    		

    Como vemos, nuestro botón tiene como prioridad android:onCLick el valor “startService”. Este método lo implementaremos en nuestra clase Activity.

    A continuación vamos a ir a nuestra clase, la que se nos ha creado junto con el proyecto, la cual se encuentra en la ruta “src/package_name/IntentServiceActivity.java”. Su código lo vamos a ver a continuación:

    		package sekth.droid.Services;
    
    		import android.app.Activity;
    		import android.content.Intent;
    		import android.os.Bundle;
    		import android.view.View;
    		
    		public class IntentServiceActivity extends Activity {
    		
    			@Override
    			protected void onCreate(Bundle savedInstanceState) {
    				super.onCreate(savedInstanceState);
    				setContentView(R.layout.activity_service);
    			}
    		
    			public void startService(View v) {
    				startService(new Intent(getBaseContext(), MyIntentService.class));
    			}
    		
    		}
    		

    Como vemos, lo único que hemos agregado es el método startService(), el cual ejecuta un servicio que pasamos como argumento al nuevo Intent. Este Intent es creado con el Context de la aplicación, y la clase que representa al Service, la cuál aún no tenemos pero será nuestro siguiente paso.

  • IntentService

    Ahora vamos a crear una nueva clase Java en nuestro package, la cuál yo voy a llamar “MyIntentService”, y heredará de la clase IntentService.

    		package sekth.droid.Services;
    
    		import java.net.MalformedURLException;
    		import java.net.URL;
    		
    		import android.app.IntentService;
    		import android.content.Intent;
    		import android.util.Log;
    		
    		public class MyIntentService extends IntentService {
    		
    			public MyIntentService() {
    				super("MyIntentServiceName");
    				// TODO Auto-generated constructor stub
    			}
    		
    			@Override
    			protected void onHandleIntent(Intent arg0) {
    				// TODO Auto-generated method stub
    				try {
    					int result = DownloadFile(new URL(
    							"http://www.google.com/imagen1.png"));
    					Log.d("IntentService", "Descargados " + result + " KBytes");
    				} catch (MalformedURLException ex) {
    					ex.printStackTrace();
    				}
    		
    			}
    		
    			private int DownloadFile(URL url) {
    				try {
    					// Simulamos una tarea larga
    					Thread.sleep(5000);
    				} catch (InterruptedException ex) {
    					ex.printStackTrace();
    				}
    				return 100;
    			}
    		
    		}
    		

    Como podemos apreciar, el primer método es su constructor, el cual llama al superconstructor pasando como argumento el nombre de nuestro IntentService:

    		public MyIntentService() {
    		super("MyIntentServiceName");
    			// TODO Auto-generated constructor stub
    		}
    		

    Una vez creado el constructor, vamos a implementar el método que contiene toda la funcionalidad en nuestro ejemplo. El método se llama onHandleIntent, y será el encargado de lanzar un hilo propio para realizar la tarea:

    		@Override
    		protected void onHandleIntent(Intent arg0) {
    			// TODO Auto-generated method stub
    			try {
    				int result = DownloadFile(new URL(
    						"http://www.google.com/imagen1.png"));
    				Log.d("IntentService", "Descargados " + result + " KBytes");
    			} catch (MalformedURLException ex) {
    				ex.printStackTrace();
    			}
    	
    		}
    		

    El último método, llamado DownloadFile lo único que hacemos es emular al descarga de un fichero, y para simularlo ponemos a “dormir” el hilo durante 5 segundos.

    		private int DownloadFile(URL url) {
    			try {
    				// Simulamos una tarea larga
    				Thread.sleep(5000);
    			} catch (InterruptedException ex) {
    				ex.printStackTrace();
    			}
    			return 100;
    		}
    		

    Como siempre, al usar el método Thread.sleep() tenemos que encerrar el código en un bloque try/catch, ya que puede lanzar una excepción del tipo “InterrptedException”.

  • AndroidManifest

    Aún no podemos ejecutar nuestra aplicación, puesto que tenemos un IntentService, que tiene que ser declarado:

    		<?xml version="1.0" encoding="utf-8"?>
    		<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    		    package="sekth.droid.Services"
    		    android:versionCode="1"
    		    android:versionName="1.0" >
    		
    		    <uses-sdk
    		        android:minSdkVersion="8"
    		        android:targetSdkVersion="17" />
    		
    		    <application
    		        android:allowBackup="true"
    		        android:icon="@drawable/ic_launcher"
    		        android:label="@string/app_name"
    		        android:theme="@style/AppTheme" >
    		        <activity
    		            android:name="sekth.droid.Services.IntentServiceActivity"
    		            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=".MyIntentService" />
    		    </application>
    		
    		</manifest>
    		

    Hemos añadido la línea:

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

Nuestra aplicación es ahora totalmente funcional, y podremos comprobar como al pulsar el Button nuestra aplicación no se bloquea, sino que el Service se está ejecutando en otro hilo.

Al terminar el Service, podemos ver que en el Log se mostrará el mensaje de que ha sido descargado.


Con esta entrada hemos cubierto otro modo de ejecutar Service, y que automáticamente se ejecute en otro hilo, no bloqueando la aplicación, y sin tener que crear una AsyncTask para ello. Además, cuando se termina, el propio Service finaliza.

El código de este ejemplo podéis bajarlo de aquí.

Sin más, cualquier aporte o corrección es bienvenido.

Saludos!!!

  • Narco

    Buenas,

    Felicidades por el blog, me está sirviendo de mucha ayuda para el aprendizaje de Android.

    Ayuda mucho el disponer de los proyectos ya que se pierde menos tiempo tanto en probar el proyecto como en buscar errores cuando uno está comenzando.

    Estaba mirando este artículo que me interesa mucho pero por algún motivo el servicio no llega a ejecutarse, me parece que no se ejecuta el onHandleIntent. ¿podrías combrobarlo a ver si falta algo o hay algún fallo?

    Muchas gracias.

    Saludos.

    • Buenas Narco,

      He bajado el proyecto (el link está operativo) y he probado ejecutandolo, y efectivamente, el método onHandleIntent se ejecuta correctamente.

      Te sale algún mensaje o algo en el LogCat?

      Saludos!!