Utiliser les services sous Android
Romain            Raveaux       Â
Polytech’Tours
Dans     ce       TP,     nous allons            abordé          la       notion           de service          dans     l’environnement      Android.                 Â
A            l'instar           des Activities,      des     Intents,          les      services font    partie    des     briques         essentielles   d'Android. Ils      ne      disposent      pas    d'interface    utilisateur mais   fonctionnent            en      arrière          plan   pour une    période         de    temps            indéfinie. L'exemple     le        plus    courant         est      le lecteur           de   musique,       qui     vous   permet d'écouter      vos    mp3   sans   bloquer         la navigation       sur     internet         ou      consulter      la liste   des    vos    contacts.       Un   service          peut également     rapatrier       des    données        sur     internet tels    que   des    flux    RSS.  Â
Dans     ce       TP,     nous élaborerons   un      service          qui     récupérera   toutes les   secondes      l’heure          courante       au      format :         heure-Â?minute-Â?seconde.   Cette    information   sera envoyée        et       affichée         dans   le       champ texte   de   l’activity       Â
Dans     ce       TP      nous aborderons   :        Â
•       Les       services        (threads,       cycle   de vie )  Â
•       Services           Locaux          (LocalService)          o Déclaration   du         service          o         Implémentation des    listeners        o    Implémentation      d'un   binder
Votre    application   aura comme          nom,   les      noms             des    deux personnes    composants    votre   binôme.         Créer une    Activity          avec   4        boutons        et   un champ           texte.                       Â
Dans     la       fonction onCreate       de      votre   activité          récupérer les      composants    graphiques   grâce   à        Â
la          méthode       findViewById. Les variables correspondants aux objets graphiques seront déclarés comme champs de la classe de votre activité.Â
/** Called when the activity is first created. */
   @Override
   public void onCreate(Bundle savedInstanceState)        Â
Sur       chaque          bouton, ajouter          un      listener          pour   capturer l’action   d’un   click   que    le       bouton.                   Â
Les       services        ont pour   but     de      réaliser         des    tâches           de fond   sans     aucune          interaction    avec   l'utilisateur pour   une    durée            indéfinie.       Il   existe deux   type   de      services        :        Â
•     les        services        locaux           (ou LocalService)           qui     s'exécutent   dans   le même            processus     que    votre   application  Â
•     Les       services        distants         (ou RemoteService)       qui     s'exécutent   dans   un processus     différent       de      celui   de      application  Â
Les       services s'exécutent   dans   le       Thread          principal       du processus     parent.             Ils      doivent         être déclarés        dans   le       fichier           :           Â
<service android:name=".subpackagename.ServiceName"/>Â Â Â Â Â Â Â Â Â Â Â
Ils         doivent étendre         la       classe            Service          dont vous   devrez           surcharger      les      méthodes suivantes      en      fonction        de      vos    besoins :  Â
void onCreate();   //       initialisation   des     ressources               Â
void onStart(Intent         intent);          //       SDK<2.0         la tâche   de       fond   démarre             Â
void onStartCommand(Intent     intent,           int      flags,   int startId);        //       SDK>2.0   la       tâche   de       fond démarre                  Â
IBinder onBind(Intent          intent);          //       connexion     client distant                      boolean            onUnbind(Intent intent);          //       déconnexion   d'un   client              void onRebind(Intent          intent)          Â
Google   a        publié un      post   sur     la       méthode       onStartCommand() apparue   avec   le       SDK   2.0     :        Â
http://android-Â?Â?api-Â?changes-Â?starting-Â
La         méthode onStart()       est     dépréciée     mais   doit    être redéfinie       pour   une       compatibilité            avec   les SDK   antérieurs    (si      nécessaire).  Â
Quant   au      cycle   de vie     d'un   service,         Google          l'illustre        de la       manière           suivante        :        Â
Pour     interagir (demarrer/arrêter )          avec   un      service,         deux possibilités   s'offrent          à         nous   :         •         Soit       on      appelle          la       méthode startService()   qui     invoque        la       méthode onCreate()    puis   onStart()      Â
service.startService() |         -Â?>    onCreate()    -Â?       > onStartCommand()   [service   running]      Â
•                    Soit on      appelle          la       méthode       bindService() qui     appelle   uniquement   la       méthode       onCreate()   Â
activity.bindService() |         -Â?>onCreate()          [service         created]       Â
Il           est     possible de      voir   la       liste   des    services        exécutés en      allant    dans   Menu             >        Settings >        Applications             >        Running Services           >        du      téléphone:   Â
Un         service          Local n'est   accessible     que    par    les      Activity          de l'application.  Â
Pour     l'exemple,     notre service          initialise        un      Timer            et une    tâche   qui   sera   exécutée       toutes            les minutes.        Nous   allons            créer   une    classe héritant         de      Service          et       surcharger   les méthodes     onCreate(),   onStart()   et       onDestroy().
public class BackgroundService extends Service { private Timer timer ; @Override public void onCreate() {     super.onCreate();     timer = new Timer();    Log.d(this.getClass().getName(), "onCreate"); } @Override public int onStartCommand(Intent intent, int flags, int startId) {            // Executer de votre tâche        }    }, 0, 60000);    return START_NOT_STICKY; } @Override public void onDestroy() { Log.d(this.getClass().getName(), "onDestroy"); this.timer.cancel(); }  |
I°)         Modifier        votre programme   pour   qu’il   affiche           toutes            les secondes   dans   le       «        Logger          »        l’heure au      format           suivant          : heure:minute:seconde.          Pour   se       faire   vous   pourrez utiliser          le       classe   Date.  Â
II°)        Les    modes d’exécution   des    services.       Â
A           partir            du texte   suivant          extrait           de      la documentation        Android           (        Â
) expliquez      quel   est     l’impact            du      mot clé      START_NOT_STICKY.
«           For started          services,         there   are     two    additional major            modes   of       operation      they   can    decide to       run    in,      depending     on      the     value they   return           from   onStartCommand():            START_STICKY is        used   for        services          that   are explicitly        started          and    stopped          as needed,   while  Â
Nous déclarons le service dans le fichier à l'intérieur de la balise <application> :
<service android:name=".BackgroundService" /> Â
Pour le démarrer, nous faisons appel à la méthode startService(Intent) de l'Activity prenant en paramêtre un Intent. Ce dernier peut être initialisé de deux manières :
•   Soit en lui passant explicitement le context de l'application et la class du service.
Intent intent = new Intent(this,BackgroundService.class);Â startService(intent);
•   Soit en déclarant une action dans le fichier
<service android:enabled="true" android:name=".BackgroundService">Â
<intent-filter>Â
       <action android:name=".BackgroundService.ACTION" />Â
</intent-filter>Â
</service>
que nous passons au constructeur de l'intent
Intent intent = new Intent(".BackgroundService.ACTION"); startService(intent);
Il est possible de passer des objets complets serialisables en paramètre avec les méthodes intent.putExtra( ). Les objets pourront être récupérer dans la méthode onStart(Intent intent, int startId) du service avec intent.getExtras().get(String key). Le fonctionnement est similaire à une table de Hash.
Pour arrêter notre service :
I°)         Placer            le démarrage   du      service          pour   qu’il   démarre       lors de   l’appuie         sur     le       bouton         Â
«           start   ».       Â
Intent intent = new
Intent(,BackgroundService.class); startService(intent);
Intent intent = new Intent(VotreActivity .this,BackgroundService.class);Â stopService(intent);
III°) Vérifier dans votre émulateur que le service à bien été créé.          Â
Java      autorise        la séparation    entre   le       code   de      définition      du comportement            d'un   objet   et       le       code réalisant       son    implantation.           Â
L'écriture        d'une interface,      puis   d'une             classe            implantant cette   interface          réalise           cette   opération.    Â
Les       interfaces déclarent      les      prototypes   des    méthodes     d’une classe   donnée         mais   ne      définissent    pas    le contenu        des    méthodes.               Â
-Â?
Déclarer une variable static (ou meme une fonction) signifie que ce membre n'est pas spécifique à un objet mais à la classe, tous les objets de la classe partagent cette même variable. Et de ce fait tu peux y accéder ainsi : Maclasse.MonVarStatic ex: class UneClasse {Â
public static int counter = 0;Â
}Â
class Main { Â
public static void main (String []args) {Â
UneClasse un = new UneClasse (), deu = new UneClasse ();Â un.counter++;Â
println (UneClasse.counter); // affiche 1Â
deu.counter++ ;Â
println (un.counter); // affiche 2Â
}Â
} Â
Les       «        listeners »        sont   des    interface.      Ces    interfaces fournissent   une       ou      plusieurs      méthodes     qui peuvent        donc   être   implémentées          différemment   selon les      cas     et       les      besoins,        pour   répondre aux    événements.   Â
Dans un premier temps, nous allons implémenter un système de listeners très simple. L'interface IBackgroundServiceListener implémentée par notre Activity pour écouter les mises à jour du service est la suivante :
Les listeners peuvent être créés sous Eclipse via le menu contextuel :Â
         New->Interface
 Utiliser le mot clé : implements IBackgroundService lors de la déclaration de votre service. private ArrayList<IBackgroundServiceListener> listeners = null;Â
Déclarer la variable listeners et allouer de la mémoire pour cette variable dans la fonction public void onCreate().Â
 listeners = new ArrayList< IBackgroundServiceListener >();
private ArrayList<IBackgroundServiceListener> listeners = null; // Ajout d'un listener public void addListener(IBackgroundServiceListener listener) {     if(listeners != null){            (listener);     } } // Suppression d'un listener public void removeListener(IBackgroundServiceListener listener) {     if(listeners != null){ listeners.remove(listener);     } |
} // Notification des listeners private void fireDataChanged(Object data){     if(listeners != null){        for(IBackgroundServiceListener listener: listeners){             listener.dataChanged(data); }     } } @Override public void onDestroy() {     this.listeners.clear();     this.timer.cancel(); Log.d(this.getClass().getName(), "onDestroy"); }  Faites   en      sorte   que    l’information d’heure         rythmée        par    le       TIMER   fasse   appel à         la       méthode       fireDataChanged     afin    de prévenir       les   listeners        écoutant       le       service d’un   changement   dans   les      données.       Le binder           va      nous   permettre     d’obtenir      une référence      sur     le   service          en      cours. Comme          avec    un      RemoteService,        nous    allons nous    connecter     au      service          et       récupérer un      Binder.          A        travers   cet     objet   nous accéderons   aux    méthodes     publiques     du      service via     l'interface        IBackgroundService            que     nous avons            définie           plus    haut.    L'avantage    de cette   solution         est      d'unifier        l'utilisation des     LocalService    et       RemoteService         mais    surtout de     récupérer     l'instance      du      service.   Nous    redéfinissons           la       méthode onBind()       :         uniquement addListener(), removeListener() et pourquoi pas d'autres méthodes métiers. Il nous reste à nous connecter au service, accéder aux méthodes exposées par l'interface et s'ajouter comme listener pour mettre à jour l'interface utilisateur A présent, nous souhaitons que l’activty se connecte au service afin de récupérer une instance du service. Par la même occasion, une fois l’instance du service récupéré, nous ajouterons les listeners qui écouteront les opérations effectuées par le service. Cette partie du code est à placer lors de l’appui sur le bouton « Connexion ». Le listener créé sera déclaré comme un champ de votre Acitivity. Les variables « connection » et « service » seront aussi à déclarer comme variables globales à votre Activity. Définition du listener : Intent intent = new Intent(this,BackgroundService.class);  //Création des listeners IBackgroundServiceListener listener = new IBackgroundServiceListener() {     public void dataChanged(final Object data) {         .runOnUiThread(new Runnable() { public void run() {                 // Mise à jour de l'UI             }        });    } }; Au moment voulu, dans la méthode run() du Timer nous ferons appel à la méthode privéefireDataChanged(data) pour notifier les listeners (dans notre cas, l'Activity) de la mise à jour des données. Attention ! Si nous nous contentons d'implémenter la méthode dataChanged(), l'exception suivante sera levée au runtime : .ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. a°)         A        faire  ÂModifier           le       code de      la       fonction        dataChanged            afin    que l’objet   data   s’affiche        dans   le       champ texte   de      l’activité.       Définition      de      la       connexion   : La connexion au service se fait via la méthode bindService (Intent service, ServiceConnection conn, int flags)de la classe Context dont hérite Activity. L'argument flags peut prendre soit 0 soit BIND_AUTO_CREATE pour forcer le service à démarrer, s'il ne l'est pas, au moment de la connexion. Lors du click sur le bouton déconnexion, placer le code suivant : unbindService(connection);                                                                              service.removeListener(listener); 1°) Le code source commenté 2°) Un rapport décrivant le fonctionnement et le but de ce TP. Qu’avez-vous compris ? Avez-vous rencontré des problèmes ? Ecrivez les éléments qui permettraient à un de vos collègues de faire ce TP facilement. 3°) Le tout doit être déposé sur moodle pour la semaine suivant le TP. (si moodle n’est pas opérationnel, envoyer votre travail par email à votre enseignant () Ce         TP      est     inspiré          du tutoriel          «        Création        de      Service          » écrit   par    Nicolas          DRUET,        puis adapté           pour   convenir       aux    objectifs   pédagogiques des    DI3    Polytech’Tours.       Â?de-Â?service/ |