IMPORTANT: Per accedir als fitxer de subversion: http://acacha.org/svn (sense password). Poc a poc s'aniran migrant els enllaços. Encara però funciona el subversion de la farga però no se sap fins quan... (usuari: prova i la paraula de pas 123456)

Pel impacients, en resum un servei Android:

Introducció

Un servei és un component d'una aplicació que realitza tasques de "llarga durada" en segon terme i que no disposa d'interfície d'usuari.

Per exemple, una aplicació pot executar un servei i aquest servei continuar executant-se encara que l'aplicació que ha executat el servei finalitzi.

Com es comuniquem amb els serveis si no tenen interfície gràfica? Es possible fer una operació bind que permeti realitzar operacions interprocess communication (IPC) amb els serveis.

Exemples de tasques que pot realitzar un servei.

  • Gestionar transaccions de xarxa (actualitzacions, descarregues, etc.)
  • Executar música.
  • Realitzar operacions I/O amb fitxers.
  • Interaccions amb un content provider
  • ...

Els serveis poden tenir dos formes:

  • Started: Un servei està iniciat quan un altre components android com una activitat executa startService(). Un cop iniciat, un servei pot romandre en execució de forma indefinida inclús si el components que l'ha executat finalitzi (això sempre que s'executin en threads independents). Típicament els serveis d'aquest tipus realitzen una tasca (per exemple descarregar un fitxer) i un cop l'operació s'acaba finalitzen.
  • Bound: un servei està "bound" quan un altre component executa bindService(). Un servei bound ofereix una interfície client servidor que permet comunicar-se entre els clients (component que crida el servei) i el servei. El client pot fer peticions (requests) al servei i obtenir resultats d'aquestes peticions, implementant un sistema interprocess communication (IPC). Un servei bounded només dura el que el component lligat al servei. Múltiples components es poden lligar a un sol servei, però un cop tots han fet un unbound aleshores el servei es destrueix.

És possible que un servei sigui dels dos tipus al mateix temps, implementant els següents mètodes:

onStartCommand() per controlar com s'inicia el servei

i

onBind() per controlar el binding.

Els components poden utilitzar el servei de la mateixa forma que les activitats poden cridar altres activitats: utilitzant Intents.

IMPORTANT: Compte: Un servei s'executa per defecte al main thread del component que executa el servei. Cal fer-ho explícitament. Cal seguir les recomanacions de l'article Procesos Android per tal de fer que l'aplicació doni bons resultats de cara a l'usuari i evitar els errors: Application Not Responding (ANR)

Quan utilitzar un servei o un thread ?

Un servei és simplement un component que s'executa en segon terme quan l'usuari està treballant amb altres aplicacions. Per exemple, si només ens interessa realitzar tasques llargues fora del UI thread però aquestes tasques només es realitzen mentrestant l'usuari està interactuant amb la nostra aplicació, aleshores no utilitzeu un servei.

Un exemple es executar música en segon terme quan esteu utilitzant una aplicació. Un exemple seria un joc amb música. En canvi si esteu fent una aplicació que reprodueixi música i els vostres usuaris volen escoltar aquesta música mentrestant treballen amb altres aplicacions, aleshores cal fer un servei.

IMPORTANT: Cal tenir en compte que els serveis també s'executen al fil d'execució principal si no dieu el contrari. Per tant, també sol ser necessari executar els serveis en fils d'execució independents o worker threads

Declarar els serveis al fitxer de manifest

Igual que les activitat (o qualsevol altre component android) el serveis s'han de declarar al fitxer de manifest de la vostra app.

Vegeu un exemple:

<manifest ... >
  ...
  <application ... >
      <service android:name=".ExampleService" />
      ...
  </application>
</manifest>

Com podeu veure és fa dins de l'element XML application i s'utilitzar l'exemple service. L'únic atribut requerit és:

android:name

i especifica la el nom de la clase que implementa el servei.

IMPORTANT: un cop es publica l'aplicació no es recomanable canviar el nom del servei ja que això pot provocar que aplicacions externes deixin de funcionar al utilitzant Intents.

Consulteu:

http://developer.android.com/guide/topics/manifest/service-element.html

Per a més detalls sobre els atributs vàlids possibles.

Si la idea és que el servei només es pugui utilitzar localment (és a dir només per la vostra pròpia aplicació) no s'ha de registrar camp Intent Filter. També es pot indicar com a privat amb:

android:exported amb valor false

Com les activitats, es poden executar Intent Filters que permetin que altres components puguin invocar el servei utilitzant implicit intents. D'aquest forma qualsevol aplicació del dispositiu pot executar el vostre servei si utilitza el mètode startService() i aquest coincideix amb els intent filters declarats.

Crear una clase servei

Per crear una classe servei només cal crear un clase filla de la clase Service o qualsevol de les subclasses existents i aleshores sobreescriure els mètodes adients. Els més importants són:

  • onStartCommand(): El sistema crida aquest mètode quan un altre component, com per exemple una activitat, demana que el servei s'executi amb el mètode: startService(). Un cop el mètode s'executa el servei s'inicia i pot executar-se en segon terme de forma indefinida. Si implementem aquest mètode és responsabilitat del programador acabar l'execució del servei amb stopSelf() o stopService() (Sí només es vol implementar el Binding no cal implementar aquest mètode.)
  • onBind(): El sistema crida aquest mètode quan un altre components com una activitat cridant el mètode bindService(). Si s'implementa aquest mètode cal proveir una interfície de comunicació per tal que els clients es puguin comunicar amb el servei. Això es fa retornant un IBinder. Aquest mètode sempre s'ha d'implementar però si no és vol utilitzar binding el mètode ha de retornar null.
  • onCreate(): El sistema crida aquest mètode quan el servei és creat per primera vegada, i s'utilitza per realitzar accions de configuració d'un sol cop (s'executa abans de onStartCommand() o onBind()). Si el servei ja està en execució aquest mètode no s'executa.
  • onDestroy(): El sistema crida aquest mètode quan el servei ja no s'utilitza més. Aquí s'han d'implementar les operacions de neteja per alliberar recursos ( eliminar threads, registered listeners, receivers, etc.). Aquest és l'últim mètode que executa el servei.

Si un component inicia un servei cridant a startService() (que realment provoca una crida a onStartCommand()), aleshores el servei s'executa fins que s'atura amb stopSelf() o un altre component l'atura amb stopService().

Si un component executa bindService() per a crear un servei (aleshores onStartCommand() no es crida, aleshores el servei només s'executa mentres el component es mantingui lligat al servei. Un cop tots els clients es deslliguen del servei el servei és aturat.

El sistema operatiu Android forçarà l'aturada d'un servei només quan la memòria esta molt malmesa i només per a recuperar l'aplicació amb la que està interactuant l'usuari. Vegeu Cicle de vida dels procesos Android

NOTA. Android 1.6 o inferior

Si la versió d'Android és 1.6 o inferior, cal implementar onStart(), en comptes de onStartCommand().

Crear un Started Service

Un started service és un que s'executa amb startService(), que provoca una crida al mètode onStartCommand().

En aquest cas el servei té un cicle de vida independent i s'executa de forma indefinida en segon terme. El cicle de vida es controla amb el mètodes:

NOTA: Caution: Recordeu d'executar el servei en un thread apart

Tradicionalment hi ha dos classe que es poden utilitzar per crear el vostre propi servei:

  • Service: Aquesta és la clase pare de tots els serveis. És important crear un nou Thread per fer totes les feines que hagui de fer el servei, per evitar bloquejar el main thread.
  • IntentService: és una subclase de Service que utilitza un worker thread per gestionar totes les peticions però una darrera de l'altre. Aquest és la millor opció si no es requereix programació concurrent o múltiples threads executant-se al mateix temps per al vostre servei. Tot el que cal fer és implementar onHandleIntent()

Implementat IntentService

La majoria de serveis no necessiten processar múltiples peticions al mateix temps (el que implica un escenari de programació concurrent i multi-threading que pot ser perillós sinó s'implementa en cura). Per això utilitzem IntentService.

IntentService realitza les següents tasques:

En resum només cal implementar el mètode:

onHandleIntent()

per fer la feina que indiqui el client (cal també però tenir en compte el constructor del servei)

Vegem un exemple:

public class HelloIntentService extends IntentService {

  /** 
   * A constructor is required, and must call the super IntentService(String)
   * constructor with a name for the worker thread.
   */
  public HelloIntentService() {
      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) {
              }
          }
      }
  }
}

Si es decideix sobrescriure algun altre mètode com onCreate(), onStartCommand(), o onDestroy(), cal assegurar-se de cridar al mètode del pare.

Vegem un exemple:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
    return super.onStartCommand(intent,flags,startId);
}

Implementant la classe Service

NOTA: interesant quan cal fer multi-threading

As you saw in the previous section, using IntentService makes your implementation of a started service very simple. If, however, you require your service to perform multi-threading (instead of processing start requests through a work queue), then you can extend the Service class to handle each intent.

Vegem un exemple, que fa el mateix que el codi de l'apartat anterior (fixeu-vos que és molt més simple fer-ho amb l'apartat anterior):time.

public class HelloService extends Service {
  private Looper mServiceLooper;
  private ServiceHandler mServiceHandler;

  // Handler that receives messages from the thread
  private final class ServiceHandler extends Handler {
      public ServiceHandler(Looper looper) {
          super(looper);
      }
      @Override
      public void handleMessage(Message msg) {
          // 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) {
                  }
              }
          }
          // Stop the service using the startId, so that we don't stop
          // the service in the middle of handling another job
          stopSelf(msg.arg1);
      }
  }

  @Override
  public void onCreate() {
    // Start up the thread running the service.  Note that we create a
    // separate thread because the service normally runs in the process's
    // main thread, which we don't want to block.  We also make it
    // background priority so CPU-intensive work will not disrupt our UI.
    HandlerThread thread = new HandlerThread("ServiceStartArguments",
            Process.THREAD_PRIORITY_BACKGROUND);
    thread.start();
    
    // Get the HandlerThread's Looper and use it for our Handler 
    mServiceLooper = thread.getLooper();
    mServiceHandler = new ServiceHandler(mServiceLooper);
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();

      // For each start request, send a message to start a job and deliver the
      // start ID so we know which request we're stopping when we finish the job
      Message msg = mServiceHandler.obtainMessage();
      msg.arg1 = startId;
      mServiceHandler.sendMessage(msg);
      
      // If we get killed, after returning from here, restart
      return START_STICKY;
  }

  @Override
  public IBinder onBind(Intent intent) {
      // We don't provide binding, so return null
      return null;
  }
  
  @Override
  public void onDestroy() {
    Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show(); 
  }
}

Notice that the onStartCommand() method must return an integer. The integer is a value that describes how the system should continue the service in the event that the system kills it (as discussed above, the default implementation for IntentService handles this for you, though you are able to modify it). The return value from onStartCommand() must be one of the following constants:

  • START_NOT_STICKY: If the system kills the service after onStartCommand() returns, do not recreate the service, unless there are pending intents to deliver. This is the safest option to avoid running your service when not necessary and when your application can simply restart any unfinished jobs.
  • START_STICKY: If the system kills the service after onStartCommand() returns, recreate the service and call onStartCommand(), but do not redeliver the last intent. Instead, the system calls onStartCommand() with a null intent, unless there were pending intents to start the service, in which case, those intents are delivered. This is suitable for media players (or similar services) that are not executing commands, but running indefinitely and waiting for a job.
  • START_REDELIVER_INTENT: If the system kills the service after onStartCommand() returns, recreate the service and call onStartCommand() with the last intent that was delivered to the service. Any pending intents are delivered in turn. This is suitable for services that are actively performing a job that should be immediately resumed, such as downloading a file.

Com iniciar un Servei

Els serveis els inicien altres components (inclús un servei pot executar un altre servei) utilitzant Intents i

startService()

El sistema s'encarrega d'executar el mètode:

onStartCommand()

i li passa l'intent (mai s'ha de cridar directament el mètode onStartCommand())

Vegem un exemple de com executar el servei dels apartats anteriors:

 Intent intent = new Intent(this, HelloService.class);
 startService(intent);

El mètode startService() retorna immediatament i el sistema executa el servei, cridant primer onCreate() (si el servei encara no s'havia executat) i després onStartCommand().

Si el servei no proveïx de Binding, el intent que es passa a startService() és el únic mètode per comunicar-se entre el client (component que executa el servei) i el servei.

Si el servei vol enviar un resultat de retorn al client, el servei pot crear un PendingIntent per fer un broadcast (amb getBroadcast())

Com aturar un servei

Un started service ha de gestionar el seu propi cicle de vida. El sistema no aturarà el servei a no ser que estigui molt apurat de recursos (vegeu Cicle de vida dels procesos Android).

Normalment el que cal fer és utilitzar:

stopSelf()

per tal que el servei s'aturi a si mateix un cop ha acabat la feina o:

stopService()

este mètode s'ha de cridar des de un altre components. Un cop es demana aturar el servei, el sistema el destrueix el més aviat possible.

Cal tenir en compte que el mètode:

stopSelf(int)

permet indicar un enter. Això s'utilitzi quan el sistema executa el mateix servei de forma concurrent i permet aturar el servei que s'escaigui en cada cas. El int es per passar el ID de la petició que ha iniciat el servei (aka startId del mètode onStartCommand())

Consulteu Gestionant el cicle de vida d'un servei per a més detalls de com controlar el cicle de vida.

NOTA: Oco: és important que la vostra aplicació aturi el serveis un cop han finalitzat la seva feina, per evitar el malbaratament de recursos del sistema.

Com crear un Bound Service

Un bound service és un servei que permet als components d'una aplicació lligar-se (to bind en anglès) amb el servei utilitzant el mètode bindService() per tal de crear una connexió entre servei i client de llarga durada (normalment, tot i que no és obligatori, no s'executen aquest tipus de serveis amb startService() )

Els serveis bound serveixen per quan hem de comunicar/transferir força informació entre client i servidor. A més, cal tenir en compte que els serveis de tipus started l'única forma de comunicar-se entre client i servidor és el Intent o els pending Intents, que són sistemes limitats que només poden comunicar informació al inici del servei i al final (en el cas del PendingIntent).

Els serveis bounded es creen quan es vol interactuar amb el servei o quan es vol exposar o fer públiques certs funcionalitats de la vostra aplicació a altres aplicacions (Interprocess communication (IPC).

Per crear un bound service cal implementar:

onBind()

que retorna un objecte

IBinder

que defineix la interfície de comunicació amb el servei.

Les altres aplicacions poden aleshores cridar a bindService() per obtenir l'objecte IBinder i comunicar-se amb el servei.

only to serve the application component that is bound to it, so when there are no components bound to the service, the system destroys it (you do not need to stop a bound service in the way you must when the service is started through onStartCommand()).

To create a bound service, the first thing you must do is define the interface that specifies how a client can communicate with the service. This interface between the service and a client must be an implementation of IBinder and is what your service must return from the onBind() callback method. Once the client receives the IBinder, it can begin interacting with the service through that interface.

Multiple clients can bind to the service at once. When a client is done interacting with the service, it calls unbindService() to unbind. Once there are no clients bound to the service, the system destroys the service.

There are multiple ways to implement a bound service and the implementation is more complicated than a started service, so the bound service discussion appears in a separate document about Bound Services. Sending Notifications to the User

Once running, a service can notify the user of events using Toast Notifications or Status Bar Notifications.

A toast notification is a message that appears on the surface of the current window for a moment then disappears, while a status bar notification provides an icon in the status bar with a message, which the user can select in order to take an action (such as start an activity).

Usually, a status bar notification is the best technique when some background work has completed (such as a file completed downloading) and the user can now act on it. When the user selects the notification from the expanded view, the notification can start an activity (such as to view the downloaded file).

Consulteu Toast Notifications i Status Bar Notifications (vegeu també Android Notifications) per a més informació o les webs:

http://developer.android.com/guide/topics/ui/notifiers/toasts.html
http://developer.android.com/guide/topics/ui/notifiers/notifications.html

Executant un servei en Foreground

Un servei en segon terme (aka foreground service) és un servei que està executant quelcom del que l'usuari és conscient, com per exemple música.

is a service that's considered to be something the user is actively aware of and thus not a candidate for the system to kill when low on memory. A foreground service must provide a notification for the status bar, which is placed under the "Ongoing" heading, which means that the notification cannot be dismissed unless the service is either stopped or removed from the foreground.

For example, a music player that plays music from a service should be set to run in the foreground, because the user is explicitly aware of its operation. The notification in the status bar might indicate the current song and allow the user to launch an activity to interact with the music player.

To request that your service run in the foreground, call startForeground(). This method takes two parameters: an integer that uniquely identifies the notification and the Notification for the status bar. For example:

 Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
 System.currentTimeMillis());
 Intent notificationIntent = new Intent(this, ExampleActivity.class);
 PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
 notification.setLatestEventInfo(this, getText(R.string.notification_title),
        getText(R.string.notification_message), pendingIntent);
 startForeground(ONGOING_NOTIFICATION, notification);

Per eliminar el servei del segon terme executeu:

stopForeground()

Aquest mètode permet passar un booleà que permet eliminar també la notificació de l'status bar també. Aquest mètode no atura el servei. De totes maneres si s'elimina el servei quan encara està executant-se en segon terme, la notificació també s'esborra.

NOTA: Els mètodes startForeground() i stopForeground() es van introduir a la versió Android 2.0 (API Level 5). En versions anteriors cal utilitzar setForeground() i startForeground()

Vegeu també:

Android_Media#Executant_com_un_servei_en_segon_terme

i

PendingIntent

For more information about notifications, see Creating Status Bar Notifications.

Gestionant el cicle de vida d'un servei

The lifecycle of a service is much simpler than that of an activity. However, it's even more important that you pay close attention to how your service is created and destroyed, because a service can run in the background without the user being aware.

The service lifecycle—from when it's created to when it's destroyed—can follow two different paths:

  • started service: The service is created when another component calls startService(). The service then runs indefinitely and must stop itself by calling stopSelf(). Another component can also stop the service by calling stopService(). When the service is stopped, the system destroys it..
  • bound service:

The service is created when another component (a client) calls bindService(). The client then communicates with the service through an IBinder interface. The client can close the connection by calling unbindService(). Multiple clients can bind to the same service and when all of them unbind, the system destroys the service. (The service does not need to stop itself.)

These two paths are not entirely separate. That is, you can bind to a service that was already started with startService(). For example, a background music service could be started by calling startService() with an Intent that identifies the music to play. Later, possibly when the user wants to exercise some control over the player or get information about the current song, an activity can bind to the service by calling bindService(). In cases like this, stopService() or stopSelf() does not actually stop the service until all clients unbind. Implementing the lifecycle callbacks

Like an activity, a service has lifecycle callback methods that you can implement to monitor changes in the service's state and perform work at the appropriate times. The following skeleton service demonstrates each of the lifecycle methods:

public class ExampleService extends Service {
    int mStartMode;       // indicates how to behave if the service is killed
    IBinder mBinder;      // interface for clients that bind
    boolean mAllowRebind; // indicates whether onRebind should be used

    @Override
    public void onCreate() {
        // The service is being created
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // The service is starting, due to a call to startService()
        return mStartMode;
    }
    @Override
    public IBinder onBind(Intent intent) {
        // A client is binding to the service with bindService()
        return mBinder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        // All clients have unbound with unbindService()
        return mAllowRebind;
    }
    @Override
    public void onRebind(Intent intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }
    @Override
    public void onDestroy() {
        // The service is no longer used and is being destroyed
    }
}

Note: Unlike the activity lifecycle callback methods, you are not required to call the superclass implementation of these callback methods.

Figure 2. The service lifecycle. The diagram on the left shows the lifecycle when the service is created with startService() and the diagram on the right shows the lifecycle when the service is created with bindService().

By implementing these methods, you can monitor two nested loops of the service's lifecycle:

The entire lifetime of a service happens between the time onCreate() is called and the time onDestroy() returns. Like an activity, a service does its initial setup in onCreate() and releases all remaining resources in onDestroy(). For example, a music playback service could create the thread where the music will be played in onCreate(), then stop the thread in onDestroy().

The onCreate() and onDestroy() methods are called for all services, whether they're created by startService() or bindService(). The active lifetime of a service begins with a call to either onStartCommand() or onBind(). Each method is handed the Intent that was passed to either startService() or bindService(), respectively.

If the service is started, the active lifetime ends the same time that the entire lifetime ends (the service is still active even after onStartCommand() returns). If the service is bound, the active lifetime ends when onUnbind() returns.

NOTA: Note: Although a started service is stopped by a call to either stopSelf() or stopService(), there is not a respective callback for the service (there's no onStop() callback). So, unless the service is bound to a client, the system destroys it when the service is stopped—onDestroy() is the only callback received.

Figure 2 illustrates the typical callback methods for a service. Although the figure separates services that are created by startService() from those created by bindService(), keep in mind that any service, no matter how it's started, can potentially allow clients to bind to it. So, a service that was initially started with onStartCommand() (by a client calling startService()) can still receive a call to onBind() (when a client calls bindService()).

For more information about creating a service that provides binding, see the Bound Services document, which includes more information about the onRebind() callback method in the section about Managing the Lifecycle of a Bound Service.

Exemples

Hello World example

Executar música de fons

Executar un Audio en segon terme

Consulteu Android_Media#Executant_com_un_servei_en_segon_terme

AuthenticatorService amb AccountManager

Vegeu Android_AccountManager#Registrar.2Fdefinir_servei_Android_al_fitxer_Manifest

Vegeu també

Enllaços externs