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.

IMPORTANT: TODO

É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.

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 el 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 classe servei

Per crear una classe servei només cal crear un classe filla de la classe 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 mentre 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 Cicla de vida dels procesos Android.

NOTA: 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:

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

  • Service: Aquesta és la classe pare de tots els serveis. És important crear un nou Thread per fer totes les feines que hagi de fer el servei, per evitar bloquejar el main thread.
  • IntentService: és una subclasse 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: Interessant 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.

Vegeu també

Enllaços externs