jueves, 10 de noviembre de 2011

Servicios en Android con interfaces remotas

Aunque existen multitud de foros de programación en donde se explica de todo y este blog va en otra línea voy a intentar explicar con un ejemplo sencillo sin entrar demasiado en la explicación del código algo de lo que me ha costado encontrar información en condiciones al respecto (aunque seguro que la hay), y así de paso pongo mi granito de arena en el asunto.
Se trata del uso de servicios en Android con interfaces remotas. Muchas veces necesitamos que algún proceso se ejecute en background y procese cierta información y el servicio pida y ofrezca información a la Activity llamante. Ejecutar servicios en background no tiene ningún misterio, pero el hecho de pasar información entre la activity y el servicio para mi hasta hace bien poco si que lo tenía y ahí es donde entran en juego las interfaces remotas.
Nuestro ejemplo consistirá en una activity que ejecutará un servicio que busque en un servidor el último mensaje disponible para un usuario determinado. Para ello el servicio necesitará la información del usuario que se lo proporcionará la Activity y una vez que el servicio obtenga dicha información se la pasará a la Activity que la mostrará en pantalla.
Inicialmente debermos definir las interfaces remotas. Una interfaz remota representa un mecanismo mediante el cual dos procesos separados
pueden utilizar métodos declarados en el otro proceso. Cada uno de los extremos
desconoce la implementación del otro, simplemente conoce a través de una interfaz qué
métodos se ofrecen, pudiendo utilizarlos como si de cualquier otra clase local se tratase.
Utilizaremos el lenguaje AIDL (Android Interface Definition Language). Eclipse se encargará de generar las clases automáticamente aunque deberemos lógicamente implementar los métodos definidos en las interfaces. Este código deberá ser guardado en ficheros .aidl

Este sería el código de la interfaz que llamaremos IRemoteCallback:


package com.mdps;

interface IRemoteCallback {

String getUsuario();
void postMensaje(String data);

}



Nuestro servicio también ha de ofrecer otros métodos remotos que
permitan a nuestra Activity pasarle un instancia de su interfaz remota con la que pueda
invocar a sus métodos, es decir, hacer lo que se denomina callback. El servicio deberá
implementar, pues, los siguientes métodos remotos:

register(): guarda un objeto de la interfaz remota.
unRegister(): elimina un objeto de la interfaz remota.


package com.mdps;
import com.mdps.IRemoteCallback;
interface IRemoteRegister {

void register(IRemoteCallback regService);
void unRegister(IRemoteCallback regService);
}


Una vez definidas las interfaces procedemos a su implementación. En nuestra Activity MostrarMensaje implementaremos:

private RemoteServiceConnectionActualizarMensajes mConnectionActualizarMensajes;
private IRemoteRegister mServiceActualizarMensajes = null;
private IRemoteCallback.Stub mCallbackActualizarMensajes = new IRemoteCallback.Stub() {
public String getUsuario() {
return usuario;
}

public void postMensaje(String data) {
mensaje = data;
}

};

class RemoteServiceConnection implements
ServiceConnection {
public void onServiceConnected(ComponentName className, IBinder service) {
// Obtener instacia gracias al stub
mServiceActualizarMensajes = IRemoteRegister.Stub
.asInterface(service);

// Hacer llamada remota: registrar instancia de IRemoteCallback
try {
mServiceActualizarMensajes
.register(mCallbackActualizarMensajes);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

public void onServiceDisconnected(ComponentName className) {
mServiceActualizarMensajes = null;
}
};


En el onCreate de nuestra Activity invocaremos el servicio en el momento que nos interese:

// Vincular el Service y la Activity actual
if (mConnectionActualizarMensajes == null) {
mConnectionActualizarMensajes = new RemoteServiceConnection();

Intent i = new Intent();
i.setClassName("com.mdps", "com.mdps.ServicioObtenerMensajes");
bindService(i, mConnectionActualizarMensajes,
Context.BIND_AUTO_CREATE);
Log.d(getClass().getSimpleName(), "bindService()");
// Lanzar Service
this.startService(i);
}

Ya tenemos definidos en nuestra activity los métodos necesarios y hemos realizado la invocación del servicio. Ahora toca el turno del código que deberemos implementar en el servicio. En el servicio deberemos implementar los metodos register y unregister
definidos en la interfaz IRemoteRegister. Este servicio será un Thread que en el caso del ejemplo se estará ejecutando continuamente cada minuto.

public class ServicioObtenerMensajes extends Service
{
public static Activity ACTIVIDAD;
// temporizador de conexión
private int mTime = 60000;

Runnable mTask = new Runnable() {
public void run()
{
Context contexto = getApplicationContext();

// Mientras dure el Service
while (true){
// Activar mCallbacks
mCallbacks.beginBroadcast();

try{
String usuario = mCallbacks.getBroadcastItem(0).getUsuario();

// A continuación se implementaría la llamada al servidor para
// obtener la información del ultimo mensaje asociado al usuario // obtenido

String ultimoMensaje = llamadaServidor();

mCallbacks.getBroadcastItem(0).postMensaje(ultimoMensaje);

} catch (Exception e) {
e.printStackTrace();
}
// Desactivar mCallbacks
mCallbacks.finishBroadcast();
// Dormir el hilo
try {
Thread.sleep(mTime);


} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};


public Thread mThread = new Thread(mTask);

@Override
public void onCreate() {
super.onCreate();
mThread.start();
}




private RemoteCallbackList<IRemoteCallback> mCallbacks =
new RemoteCallbackList<IRemoteCallback>();
private IRemoteRegister.Stub mRegister = new IRemoteRegister.Stub(){
public void register(IRemoteCallback interfac) {
// Guardar interfaz remota
if (interfac!= null) mCallbacks.register(interfac);
}


public void unRegister(IRemoteCallback interfac) {
// Eliminar interfaz remota
if (interfac!= null) mCallbacks.unregister(interfac);
}
};




@Override
public IBinder onBind(Intent intent) {
return mRegister;
}


}



Resumiendo el funcionamiento en conjunto sería.
Nuestra activity en un momento dado lanza el servicio ServicioObtenerMensaje que será un Thread que se ejecutará indefinidamente.
Al comenzar solicita a la activity mediante el método getUsuario el usuario para el que quiere obtener el mensaje.
Realizará la llamada al servidor donde se procesará la información.
Mientras tanto la activity ha continuado con su ejecución y en el momento en que el servicio disponga del mensaje se lo notificará a mediante el método postMensaje implementado en la Activity. Una vez esta información esté disponible se mostrará por pantalla (este código no aparece).









No hay comentarios:

Creative Commons License
LoloSil-La Rosa de los Vientos es una idea original de MDPS - This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License