Servicios
Los servicios son un componente de la aplicación que pueden realizar largas operaciones de “fondo” sin la necesidad de interacción con el usuario.
Cualquier otro componente de la aplicación puede iniciar un servicio y este puede seguir “corriendo” aunque el usuario cambie o cierre la app.
Por ejemplo un servicio podría reproducir música, manejar conexiones con servicios web o realizar operaciones en el disco desde el “fondo” .
Un Servicio NO NECESARIMENTE corre en el UI Thread (Hilo de Interfaz de Usuario, o Hilo Principal).
Entonces, la pregunta es ¿Porque usar un Servicio y no una AsyncTask? O dicho de otra manera: ¿Cuándo usar un Servicio y cuando un AsyncTask?
Por ejemplo, como vimos la clase anterior, si queremos descargar información de un Servicio Web al presionar un Boton, usamos un AsyncTask.
Se podría hacer lo mismo con un Servicio: iniciarlo, tomar los datos, y detenerlo, pero no sólo es más complejo sino que ademas es ineficiente.
La AsyncTask se ejecuta, corre una única vez, y obtiene los datos.
A diferencia del ejemplo que plateábamos antes, como en este caso vamos a estar constántemente consumiendo datos de la Web, es más eficiente utilizar un servicio. Y además ni siquiera necesitamos una actividad para mostrar los datos.
Como regla general, podemos decir que los Servicios son para cuando queremos realizar acciones por más de que nuestra aplicación esté cerrada.
Y las AsyncTask están diseñadas para ejecutar código fuera del Hilo Principal de una manera rápida y sencilla.
Un Servico es Started cuando es lanzado por otro componente de una Aplicación mediante el método startService. Una vez que es iniciado el servicio corre de manera indefinida, aunque el componente que lo inició sea destruido. Usualmente este servicio corre una sola tarea y no devuelve ningún resultado a quien lo inició. Cuando la operación finalizó el servicio debe detenerse por sí mismo. Este tipo de Servicio no tiene conexión con el componente que lo inició. Es decir, no pueden directamente realizar actualizaciones de UI
Por ejemplo, cuando descargamos un archivo de la web y lo guarda en disco
Un Servico es Bound cuando es lanzado por otro componente de una Aplicación mediante el método bindService. Este servicio ofrece una interfaz cliente-servidor que permite a los componentes interactuar con él. Un servicio de este tipo sólo corre mientras algún componente de la aplicación esté conectado a él. Muchos componentes pueden conectarse con el servicio, pero cuando el último se desconecte el servicio se destruirá. A diferencia de los started, pueden comunicarse con el componente que lo inició.
Por ejemplo, un reproductor de música que envia info sobre el estado del playback y tiempo transcurrido.
Todo servicio debe tener un metodo onBind que es el que se llama cuando un componente se quiere conectar con él. Sólo lo usamos con los Bound Services, por tanto los Started Services, sólo requieren una implementación básica del mismo.
Cuando se inicia un servicio por primera vez con startService, se invoca en método onCreate. Y luego se llama al método onStartCommand. Si el servicio ya se encontraba iniciado, no se llama al onCreate pero sí al onStartCommand.
Los parametros que recibe el onStartCommand son:
El valor de retorno del onStartCommand indica el comportamiento en caso de que el servicio sea terminado por el sistema:
<service android:name=".StartedService"></service>
Vamos a ver el comportamiento tras iniciar, y volver a iniciar, detener y reiniciar, etc.
Asi como también vamos a ver los diferentes valores de retorno.
Una forma de realizar una tarea temporizada, es a travez de un AsyncTask. Necesitamos este AsyncTask para separar el proceso del servicio del proceso de la app.
Los started Services no pueden retornar valores o resultados, ni interactuar con el componente que los creo. Para esos casos, usamos los bound Services, ya que pueden enviar datos al componente que los inició.
Por eso es que un componente crea un servicio y ademas se "une" o "ata" (bind) a él. Un servicio se puede ligar a varios componentes, y liberarlo (unbind) cuando termine con él.
Cuando el último componente se desligue, recién ahí se destruye el servicio. Internamente, el servicio lleva la cuenta de los componentes que tiene unidos, cada vez que se libera uno, decrementa este valor, y al llegar a CERO lo destruye.
Así mismo, si el componente al que está unido es destruido o finaliza su ciclo de vida, el servicio también se destruye con él.
Cuando un componente se une a un bound services, se crea una interface de comunicación donde pueden enviar y recibir información. Esta interface es de tipo IBinder, y el cliente (componente) la obtiene del método onBind (luego de invocar bindService), a traves de una interfaz ServiceConnnection, la cual debe ser provista por el cliente.
Este IBinder es básicamente una interfaz de comunicación:
El cliente invoca métodos de esta interface y la ejecución ocurre en el Servicio
public class CronoBinder extends Binder{
public BoundService obtenerServicio (){
return BoundService.this;
}
}
public IBinder onBind(Intent intent) {
// Creamos la interfaz de comunicacion (IBinder) y la devolvemos al cliente
binder = new CronoBinder();
return binder;
}
bindService(boundIntent, conexion, BIND_AUTO_CREATE);
private ServiceConnection conexion = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
BoundService.CronoBinder binder = (BoundService.CronoBinder) service;
cronometro = binder.obtenerServicio();
boundConectado = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
boundConectado = false;
}
};
if (boundConectado == true){
unbindService(conexion);
boundConectado = false;
}
Es importante remarcar que los metodos son ejecutados en el mismo hilo (Thread) que el cliente que lo invoca, en este caso el UI Thread, por tanto si la operación es de largo tiempo de ejecución, deberíamos crear un hilo a parte, donde correr este método
Vamos a notar los LOGS para ver la evolución del servicio, por sus diferentes estados.
Notar que pasa al cerrar la app y al abrirla