servicios - lsub.orglsub.org/mov/11.service.pdf · servicios: ejemplo • me estoy bajando algo que...

37
Servicios LSUB, GYSC, URJC

Upload: lynhan

Post on 05-Apr-2018

219 views

Category:

Documents


5 download

TRANSCRIPT

ServiciosLSUB, GYSC, URJC

Servicios

• Sirve para hacer trabajo en background (i.e. sin UI). Más prioridad que activity (no suelen matarlo)

• Pueden ser privados a la aplicación (manifest)

• Continua ejecutando si el usuario cambia a otra aplicación.

• No es un proceso aparte.

• No crea un nuevo thread, ejecuta en el main thread:

• Si se bloquea o es lento, afecta al main thread.

• En estos casos, hay que crear un nuevo thread.

Servicios: ejemplo

• Me estoy bajando algo que lleva tiempo, (ojo al wifilock/batería) de vez en cuando me bajo un poco.

• Player de audio, quiero que suene en background

• Notificador de Whatsapp/actualizaciones del periódico/lluvia

Servicios

• ¿Debo usar un servicio o un thread?

• Si se necesita realizar un trabajo en background mientras se está usando la aplicación, mejor usar un thread (o un AsyncTask).

Servicios

• Un servicio puede ser:

• Started: el servicio se inicia y ejecuta en background indefinidamente, incluso si el componente que lo creó se destruye.

• Bound: el servicio ofrece una serie de operaciones al cliente con el que se ata. El servicio vive mientras viva el cliente.

• Un servicio puede actuar de las dos formas a la vez.

Servicios

Servicios

• Hay que declarar el nombre de la clase del servicio en el manifest: <manifest xmlns:android="http://schemas.android.com/apk/res/android" ... <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" >

<service android:name=“.PlayerService" android:exported=“false"/>

<activity

... </activity> </application></manifest>

Servicios

• Un servicio puede extender las clases:

• Service

• IntentService

IntentService

• No puede ser Bound.

• Crea un único thread (worker) que atenderá las Intents una a una (se puede bloquear, hacer trabajo intensivo, etc.)

• No hay que preocuparse por la concurrencia.

• Para el servicio automáticamente cuando ya no hay Intents por procesar.

• Sólo debemos implementar el método onHandleIntent()

IntentServicepublic class MyIntentService extends IntentService {

  /**    * A constructor is required, and must call the super IntentService(String)   * constructor with a name for the worker thread.   */  public MyIntentService() {      super("HelloIntentService");  }

  /**   * The IntentService calls this method from the default worker thread with   * the intent that started the service. When this method returns, IntentService   * stops the service, as appropriate.   */  @Override  protected void onHandleIntent(Intent intent) {      // Normally we would do some work here, like download a file.      // For our sample, we just sleep for 5 seconds.      long endTime = System.currentTimeMillis() + 5*1000;      while (System.currentTimeMillis() < endTime) {          synchronized (this) {              try {                  wait(endTime - System.currentTimeMillis());              } catch (Exception e) {              }          }      }  }}

ServiceLos servicios que heredan de Service son más complicados:

Para iniciar el servicio:

• Otro componente puede invocar el método startService(). Se necesita pasar un Intent.

• En el servicio, se invoca el método onStartCommand() por cada llamada a startService().

Para parar el servicio:

• El servicio puede invocar su método stopSelf().

• Otro componente puede invocar el método stopService(). Se usa una Intent para indicar el servicio.

• En ambos casos, el sistema destruye la instancia del servicio lo antes posible.

Service

public int onStartCommand(Intent intent, int flags, int startId)

• Ejecuta en el main thread.

• flags: puede especificar que el Intent es un reenvío de uno previo, un reintento, etc.

• startId: cada invocación a startService() tiene su propio id para identificarse.

Service

• Debe retornar una de las siguientes constantes:

• Service.START_NOT_STICKY: si se mata y no hay ninguna Intent pendiente, no se recreará (el servicio se creará cuando haya una nueva Intent).

• Service.START_STICKY: si el sistema mata el servicio, más tarde intentará recrear creando una nueva instancia. Se descarta la Intent que estaba en curso. Si no hay Intents pendientes en ese momento, la Intent es null.

• Service.START_REDELIVER_INTENT: si se mata, se recrea sólo si tenía Intents pendientes y las retoma. La Intent está pendiente hasta que se invoque stopSelf() con su id.

public int onStartCommand(Intent intent, int flags, int startId)

Service

• Hay redefinir también los siguientes métodos:

• onCreate(): invocado al crear el servicio. No es obligatorio invocar a super().

• onDestroy(): invocado al parar el servicio. No es obligatorio invocar a super().

• onBind(): el servicio empieza a operar en modo Bound. Se invoca cuando un cliente se ata.

Servicepublic class ServiceExample1 extends Service {

@Overridepublic void onCreate() {

super.onCreate();Log.v(this.getClass().getName(), "onCreate!");

}

@Overridepublic void onDestroy() {

super.onDestroy();Log.v(this.getClass().getName(), "onDestroy!");

}

@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {

final int id = startId;

Log.v(this.getClass().getName(), "onStartCommand! id : " + startId); (new Thread(new Runnable(){ public void run(){ for(int i = 0 ;i < 10 ; i++){ Log.v(this.getClass().getName(), "Worker: doing my stuff! id : " + id + " i: " + i); try {

Thread.sleep(1000);} catch (InterruptedException e) { }

} ServiceExample1.this.stopSelf(id); } })).start(); return Service.START_REDELIVER_INTENT; }}

Notificaciones

• Para notificar al usuario, un servicio puede usar:

• Toasts.

• Notificaciones de la barra de status.

http://developer.android.com/reference/android/app/Notification.htmlhttp://developer.android.com/guide/practices/ui_guidelines/icon_design_status_bar.html

Notificaciones

private void showNotification(String text){NotificationManager nmng = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);

Notification notification = new Notification.Builder(this) .setContentTitle("Service Example 1") .setContentText(text) .setSmallIcon(R.drawable.notification) .build();

// NOTIFICATION_ID is a constant nmng.notify(NOTIFICATION_ID, notification);

}

Foreground

• Un servicio en foreground es un servicio que se muestra al usuario (P. ej. un reproductor de música).

• Tiene más prioridad: el sistema evita matarlos.

• Debe mostrar notificaciones en la barra de estatus.

• Se controla con startForeground() y stopForeground()

Foreground...

Notification notification = new Notification.Builder(this).setContentTitle("Player Service").setContentText("Playing mp3 file with id: " + resource).setSmallIcon(R.drawable.notification).build();

startForeground(NOTIFICATION_ID, notification);

...

/* * Remove this service from foreground, allowing it to be * killed if more memory is needed. The boolean is used to remove * the notification. */stopForeground(true);

Bound Service• El cliente invoca bindService() para iniciar una sesión de

larga duración con el servicio.

• En el servicio se invoca onBind() cuando se ata un cliente.

• onBind() retorna un objeto IBinder que define la interfaz con el servicio.

• No hace falta parar el servicio (el objeto se destruye cuando no hay clientes atados).

• Múltiples clientes pueden estar atados al mismo servicio.

• Son más complicados que los Started Services.

Bound Service

• Para definir la interfaz IBinder podemos:

• Extender la clase Binder. Sólo para servicios internos de nuestra aplicación.

• Usar una clase Messenger. Forma sencilla de proveer la interfaz a otras aplicaciones (veremos esta)

• Definir la interfaz directamente en AIDL (Android Interface Definition Language). Para hablar con otros procesos con multithreading, ver http://developer.android.com/guide/components/aidl.html

• Broadcast (usando el LocalBroadcastManager).

Bound Service

• El servicio debe:

• Crear un objeto Messenger para generar el IBinder que permite que los clientes manden mensajes.

• Procesar los mensajes que envían los clientes (los clientes no invocan sus métodos) en un Handler.

Bound Service

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

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

<application

... <service android:name="com.example.androidboundedservice.MyBoundService"> <intent-filter> <action android:name="com.example.androidboundedservice.BOUNDHELLOSERVICE" > </action> </intent-filter> </service> </application>

</manifest>

Bound Servicepublic class MyBoundService extends Service {

private Messenger messenger;public static final int MSG_SAY_HELLO = 1;

@Overridepublic IBinder onBind(Intent intent) {

return messenger.getBinder();}

@Overridepublic void onCreate() {

Log.v(this.getClass().getName(), "onCreate!");messenger = new Messenger(new IncomingHandler(this));super.onCreate();

}

@Overridepublic void onDestroy() {

Log.v(this.getClass().getName(), "onDestroy!");super.onDestroy();

}

//continue ...

Bound Service

/* * The handler cannot be a non-static inner class, Android does not permit that. * The handler may be queued and retain the external object (MyBoundService). */static class IncomingHandler extends Handler {

/* * We use a weak reference to avoid leaks. A strong reference * will retain the referenced object (MyBoundService). */private WeakReference<Context> context;

public IncomingHandler(Context c){context = new WeakReference<Context>(c);

}

@Overridepublic void handleMessage(Message msg) {

if(context != null){switch (msg.what) {case MSG_SAY_HELLO:

Toast.makeText(context.get(), "hello!", Toast.LENGTH_SHORT).show();break;

default:super.handleMessage(msg);

}}

}}

Bound Service

• Cliente:

• Debe usar un objeto que implemente la interfaz ServiceConnection con callbacks para manejar conexiones/desconexiones.

• Debe usar bindService() para atarse al servicio.

• Debe usar unbindService() para desatarse.

Bound Servicepublic class MainActivity extends Activity {

private Button button;private Messenger service;private MyBoundServiceConnection connection;

private class MyBoundServiceConnection implements ServiceConnection{

@Overridepublic void onServiceConnected(ComponentName name, IBinder ibinder) {

Log.v(this.getClass().getName(), "onServiceConnected!");service = new Messenger(ibinder);

}

@Overridepublic void onServiceDisconnected(ComponentName arg0) {

Log.v(this.getClass().getName(), "onServiceDisconnected!");service = null;

}}

// continue...

Bound Service@Overrideprotected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);button = (Button) findViewById(R.id.button1);button.setOnClickListener(new ButtonOneListener());connection = new MyBoundServiceConnection();

}

@Overrideprotected void onStart() {

super.onStart();Intent i = new Intent("com.example.androidboundedservice.BOUNDHELLOSERVICE");if(i != null){

if(bindService(i, connection, Context.BIND_AUTO_CREATE)){Log.v(this.getClass().getName(), "bound!");

}}

}

@Overrideprotected void onStop() {

super.onStop();try{

if(service != null){unbindService(connection);

}}catch (Exception e){ ... }

}

Bound Serviceprivate class ButtonOneListener implements OnClickListener{

@Overridepublic void onClick(View arg0) {

if(service != null){/* * Create a message with the value. */Message m = Message.obtain(null, MyBoundService.MSG_SAY_HELLO, 0, 0);try{

service.send(m);}catch(Exception e){ ... }Log.v(this.getClass().getName(), "ButtonOneListener: message sent!");

}else{Log.v(this.getClass().getName(), "ButtonOneListener: the service not connected!");

}}

}

...} // end of MainActivity

Bound Service

• Si el servicio quiere contestar:

• Hay que crear un Messenger en el client

• Mandar un mensaje al servicio que incluye ese Messenger en el parámetro replyTo del send()

Bound Service

• Si únicamente se necesita el servicio cuando la Activity es visible, entonces el cliente debe:

• conectarse en onStart()

• desconectarse en onStop().

Started + Bound Service

Arrancar de Alarm (garantizar el servicio)

Calendar cal = Calendar.getInstance();

Intent intent = new Intent(this, MyService.class);PendingIntent pintent = PendingIntent.getService(this, 0, intent, 0);

AlarmManager alarm = (AlarmManager)getSystemService(Context.ALARM_SERVICE);// Start every 30 secondsalarm.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 30*1000, pintent);

Buen tutorial

• https://developer.android.com/guide/components/services.html

Broadcast Receiver

• Reciben avisos del sistema

• Heredan deBroadcastReceiver

• Ej: llamadas entrantes, batería baja, etc.

Broadcast Receiver

• Reciben avisos del sistema típicamente (cualquier intent mandado con sendBroadcast)

• Heredan deBroadcastReceiver

• Ej: llamadas entrantes, batería baja, etc.

• Versión local a las aplicaciones, LocalBroadcastManager

Broadcast Receiver

• http://developer.android.com/reference/android/content/BroadcastReceiver.html

• Callback onReceive() viene con un intent con Bundle