Bienvenidos sean a este post, hoy hablaremos sobre la clase IntentService. En el post de Servicios hemos visto en el ejemplo que la clase ServicioMusica extiende a la clase Service y esta nos permitira crear un servicio pero este lo hara sobre un hilo principal pero si nosotros necesitaramos correrlo sobre un hilo secundario para que no nos bloquee la aplicacion cuando se quede sin memoria o sature el uso del CPU usaremos la clase IntentService. La clase tiene un constructor donde deberemos informarle el nombre que queremos dar el servicio de tipo String. Lo habitual cuando extendamos esta clase por medio del constructor llamemos al constructor padre, esto es por medio de la palabra super, donde le pasaremos el nombre identificador, un ejemplo de sintaxis es:

public class MiServicio extends IntentService {

	public MiServicio(){
		super("Nombre de mi servicio");
	}

	@Override
	protected void onHandleIntent(Intent intencion){
		... Instrucciones ...
	}
}

En este caso podemos ver como el constructor por medio de super le envia a la clase padre el nombre de identificacion del servicio y despues sobreescribimos (o anulamos) el metodo onHandleIntent() para poder lanzar cuando iniciemos el servicio y por medio de intencion se podran enviar datos de tipo extra, si nosotros lanzamos varias de estas peticiones las mismas se encolaran y las mismas se iran atendiendo una tras otra sin que haya dos a la vez en ejecucion y esto puede ser interesante para algunas tareas pero no para otras.
Al igual que sucede con Service para llamar a IntentService se debe usar startService(). Tomemos el ejemplo del post anterior y modifiquemos el metodo sleep() de 5000ms a 25000ms y veamos que sucede

Este error puede suceder porque utilizamos demasiados recursos con nuestra app, para evitar debemos pasar a IntentService.

Para poder implementar IntentService deberemos hacer una serie de modificaciones, para ello volvamos a la app y procedamos a crear una nueva clase, para ello debemos hacer click con el boton derecho sobre el contenedor de clases java y seleccionar New -> Java Class, en el cuadro modificaremos el campo Name y pondremos el valor ServicioOperacionIntent, el resto debe quedar como aparece y pulsen Ok para crear nuestra nueva clase. Con nuestra nueva clase debemos reemplazar el codigo existente por el siguiente

package org.example.intentservice;

import android.app.IntentService;
import android.content.Intent;
import android.os.SystemClock;


public class ServicioOperacionIntent extends IntentService {

    public ServicioOperacionIntent(){
        super("ServiceOperacionIntent");
    }

    @Override
    protected void onHandleIntent(Intent intento) {
        double n = intento.getExtras().getDouble("numero");
        SystemClock.sleep(5000);
        MainActivity.salida.append(n * n + "\n");
    }
}

En esta clase utilizaremos IntentService, primero definiremos el constructor y con super enviaremos la identificacion, luego sobreescribiremos el metodo onHandleIntent() con e cual recibiremos los datos enviados por la actividad de MainActivity, esperaremos 5s (5000ms) y luego agregaremos el valor del cuadrado del numero informado pero antes debemos hacer una serie de modificaciones mas, para ello debemos volver a la clase MainActivity y cambiar la siguiente linea:

Intent i = new Intent(this,ServicioOperacion.class);

Por la siguiente linea:

Intent i = new Intent(this,ServicioOperacionIntent.class);

Nuestra siguiente modificacion sera en el archivo AndroidManifest.xml donde cambiaremos la siguiente linea:

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

Y lo reemplazaremos por la siguiente linea:

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

Con estas modificaciones podemos compilar la app y probar pero nos devolvera el siguiente error

En este caso hemos tenido este error porque nosotros intentamos enviar un dato desde un hilo secundario al hilo principal y como vimos en Threads esto no esta permitido. Para corregir esto debemos utilizar un receptor de anuncios y para ello debemos hacer las siguientes modificaciones. Primero debemos agregar la siguiente clase en nuestra clase MainActivity:

    public class ReceptorOperacion extends BroadcastReceiver {
        public static final String ACTION_RESP =
                "org.example.intentservice.intent.action.RESPUESTA_OPERACION";

        @Override
        public void onReceive(Context contexto, Intent intento){
            Double res = intento.getDoubleExtra("resultado",0.0);
            salida.append(" " + res + "\n");
        }
    }

Esta clase va a ser unicamente para la clase MainActivity, esta se encarga de extender BroadcastReceiver, en ella crearemos una constante, para luego sobreescribir el metodo onReceive() donde obtendra el valor de resultado, dato enviado por la actividad, y la agregara a nuestro elemento salida, nuestro siguiente paso sera agregar el siguiente bloque dentro del metodo onCreate():

IntentFilter filtro = new IntentFilter(ReceptorOperacion.ACTION_RESP);
filtro.addCategory(Intent.CATEGORY_DEFAULT);
registerReceiver(new ReceptorOperacion(), filtro);

En este caso crearemos un objeto de IntentFilter llamado filtro, el cual lo hara con la clase ReceptorOperacion, luego le dira cual es su categoria al filtro y por ultimo registramos al receptor la cual va a ser la clase antes creada. Ahora iremos a la clase ServicioOperacionIntent donde modificaremos la siguiente linea:

MainActivity.salida.append(n * n + "\n");

Por el siguiente bloque:

        Intent i = new Intent();
        i.setAction(MainActivity.ReceptorOperacion.ACTION_RESP);
        i.addCategory(Intent.CATEGORY_DEFAULT);
        i.putExtra("resultado", n * n);
        sendBroadcast(i);

En este bloque crearemos una nueva intencion, luego le diremos que accion debe utilizar en este caso la clase antes creada (ReceptorOperacion), para luego asignarle una categoria, enviar el dato por medio de putExtra() y por ultimo enviara un broadcast por medio de la intencion antes creada. Antes de probar nuestra app, veamos como quedo el codigo fuente de MainActivity:

package org.example.intentservice;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends Activity {
    public EditText entrada;
    public static TextView salida;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        entrada = (EditText) findViewById(R.id.entrada);
        salida = (TextView) findViewById(R.id.salida);
        IntentFilter filtro = new IntentFilter(ReceptorOperacion.ACTION_RESP);
        filtro.addCategory(Intent.CATEGORY_DEFAULT);
        registerReceiver(new ReceptorOperacion(), filtro);
    }

    public void calcularOperacion(View view){
        double n = Double.parseDouble(entrada.getText().toString());
        salida.append(n + "^2 = ");
        Intent i = new Intent(this,ServicioOperacionIntent.class);
        i.putExtra("numero", n);
        startService(i);
    }

    public class ReceptorOperacion extends BroadcastReceiver {
        public static final String ACTION_RESP =
                "org.example.intentservice.intent.action.RESPUESTA_OPERACION";

        @Override
        public void onReceive(Context contexto, Intent intento){
            Double res = intento.getDoubleExtra("resultado",0.0);
            salida.append(" " + res + "\n");
        }
    }

}

Pasemos a ver el codigo de nuestra clase ServicioOpercionIntent:

package org.example.intentservice;

import android.app.IntentService;
import android.content.Intent;
import android.os.SystemClock;


public class ServicioOperacionIntent extends IntentService {

    public ServicioOperacionIntent(){
        super("ServiceOperacionIntent");
    }

    @Override
    protected void onHandleIntent(Intent intento) {
        double n = intento.getExtras().getDouble("numero");
        SystemClock.sleep(5000);
        Intent i = new Intent();
        i.setAction(MainActivity.ReceptorOperacion.ACTION_RESP);
        i.addCategory(Intent.CATEGORY_DEFAULT);
        i.putExtra("resultado", n * n);
        sendBroadcast(i);
    }
}

Con todas nuestras modificaciones realizadas podemos pasar a compilar y probar nuestra app, como se ve en el siguiente video

Como pueden ver funciono perfecto, esto gracias a que enviamos nuestra informacion de retorno por medio de un broadcast y ahora nuestra clase MainActivity tiene un clase a la espera de esto, lo procesa y lo envia al elemento salida, si quieren pueden probar de nuevo lo mismo de pasar de 5000 a 25000 para cambiar el tiempo de espera y deberia funcionar correctamente.

Nota: Esta solucion por medio de broadcast lo veremos mas en detalle en algunos posts mas adelante, no se preocupen porque no lo dejaremos suelto.

En resumen, hoy hemos visto que es y para que sirve la clase IntentService, hemos visto como solucionar un problema de saturacion de recursos, hemos hecho un ejemplo el cual nos devolvio un error y luego hemos visto como solucionarlo, espero les haya sido util sigueme en Twitter, Facebook o Google+ para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios