Article complet: Utiliser les services sous Android



Documents pareils
Tutorial pour une application simple

Outils, langage et approche Android Une introduction. Nicolas Stouls lyon.fr

TP SIN Programmation sur androïde Support : eclipse

Remote Method Invocation Les classes implémentant Serializable

Programmer en JAVA. par Tama

Développement sous Android

Auto-évaluation Programmation en Java

Calcul Parallèle. Cours 5 - JAVA RMI

TP2 : Client d une BDD SqlServer

TP au menu «UI ANDROID»

Remote Method Invocation (RMI)

RMI. Remote Method Invocation: permet d'invoquer des méthodes d'objets distants.

Java 7 Les fondamentaux du langage Java

Débuter avec OOo Base

Trier les ventes (sales order) avec Vtiger CRM

1.3 Gestion d'une application

Les fondamentaux du développement d applications Java

TP n 2 Concepts de la programmation Objets Master 1 mention IL, semestre 2 Le type Abstrait Pile

Création d'un questionnaire (sondage)

Premiers Pas en Programmation Objet : les Classes et les Objets

Android 4 Les fondamentaux du développement d'applications Java

Programmation Par Objets

Pour signifier qu'une classe fille hérite d'une classe mère, on utilise le mot clé extends class fille extends mère

TP1 : Initiation à Java et Eclipse

Création d un service web avec NetBeans 5.5 et SJAS 9

TD/TP 1 Introduction au SDK d Android

IFT287 Exploitation de base de données relationnelles et orientées objet. Laboratoire Mon premier programme Java en Eclipse

Programmation Objet Java Correction

RMI le langage Java XII-1 JMF

Serveur d'application Client HTML/JS. Apache Thrift Bootcamp

JAVA 8. JAVA 8 - Les fondamentaux du langage. Les fondamentaux du langage Java. Avec exercices pratiques et corrigés JAVA 8 29,90.

Séquence de découverte de SparkAngels Logiciel d entraide numérique

Sommaire Introduction... 3 Le but du projet... 3 Les moyens utilisés... 3 Informations sur le client FTP... 4 Pourquoi une version Linux et

Le stockage local de données en HTML5

Généralités sur le Langage Java et éléments syntaxiques.

Page 1 sur 5 TP3. Thèmes du TP : l la classe Object. l Vector<T> l tutorial Interfaces. l Stack<T>

TD Objets distribués n 3 : Windows XP et Visual Studio.NET. Introduction à.net Remoting

Assistance à distance sous Windows

Projet Android (LI260) Cours 2

Synchro et Threads Java TM

Le service de création de site Internet : Mode d emploi. La Création de Site Internet

TRUECRYPT SUR CLEF USB ( Par Sébastien Maisse 09/12/2007 )

Créer un sondage en ligne

Chapitre 10. Les interfaces Comparable et Comparator 1

SYNC FRAMEWORK AVEC SQLITE POUR APPLICATIONS WINDOWS STORE (WINRT) ET WINDOWS PHONE 8

Installation et prise en main

J2SE Threads, 1ère partie Principe Cycle de vie Création Synchronisation

Un ordonnanceur stupide

SHERLOCK 7. Version du 01/09/09 JAVASCRIPT 1.5

La base de données XML exist. A. Belaïd

Tutoriel Inscription et utilisation basique d'un blog hébergé chez Blogger.com

Programmation des applications mobiles avec Android. 1 Inspiré du cours de Olivier Le Goaer

TalkToMe : votre première appli App Inventor Ce tutoriel vous aidera à construire une appli parlante qui peut sauvegarder des phrases sur demande.

Programmation par les Objets en Java

Info0604 Programmation multi-threadée. Cours 5. Programmation multi-threadée en Java

ANIMER UNE COMMUNAUTÉ CONSTRUCTION21

Votre premier projet Android

Programmation par composants (1/3) Programmation par composants (2/3)

Education Delivery Intelligent Tool

Warren PAULUS. Android SDK et Android x86

Projet serveur OwnCloud

Navigation dans Windows

NFP 121. Java et les Threads. Présentation : Thierry Escalarasse Mai 2007

Ateliers Python+Qt : Premiers pas : Comment développez ses propres interfaces graphiques sur le RaspberryPi?

Cours 1: Java et les objets

Initiation à JAVA et à la programmation objet.

Applet pour visualiser les variables «automate» notifiées

Une introduction à la technologie EJB (2/3)

1. Base de données SQLite

Android et le Cloud Computing

Compte-rendu de projet de Système de gestion de base de données

Threads. Threads. USTL routier 1

TD/TP PAC - Programmation n 3

Java RMI. Arnaud Labourel Courriel: Université de Provence. 8 mars 2011

Lambda! Rémi Forax Univ Paris-Est Marne-la-Vallée

Polymorphisme, la classe Object, les package et la visibilité en Java... 1

INITIATION AU LANGAGE JAVA

Plateforme PAYZEN. Intégration du module de paiement pour la plateforme Magento version 1.3.x.x. Paiement en plusieurs fois. Version 1.

Encapsulation. L'encapsulation consiste à rendre les membres d'un objet plus ou moins visibles pour les autres objets.

Contrôler un ordinateur à distance avec Google Chrome

Tutorial et Guide TeamViewer

Développement Logiciel

Maîtriser le menu contextuel dans un OfficeBean

Apprendre la Programmation Orientée Objet avec le langage Java (avec exercices pratiques et corrigés)

Dis papa, c est quoi un bus logiciel réparti?

italc supervision de salle

EXTENSION WORDPRESS. Contact Form 7. Proposé par :


1-Introduction 2. 2-Installation de JBPM 3. 2-JBPM en action.7

RapidMiner. Data Mining. 1 Introduction. 2 Prise en main. Master Maths Finances 2010/ Présentation. 1.2 Ressources

Introduction à JDBC. Accès aux bases de données en Java

API04 Contribution. Apache Hadoop: Présentation et application dans le domaine des Data Warehouses. Introduction. Architecture

Administration du site (Back Office)

JADE : Java Agent DEvelopment framework. Laboratoire IBISC & Départ. GEII Université & IUT d Evry nadia.abchiche@ibisc.univ-evry.

Micro-ordinateurs, informations, idées, trucs et astuces utiliser le Bureau à distance

Généralités. javadoc. Format des commentaires. Format des commentaires. Caractères spéciaux. Insérer du code

Tutoriel pour la création d'un Google Sites

AUVRAY Clément (168187) HOMBERGER Alexandre (186897) GLADE. Langages, outils et méthodes pour la programmation avancée Page 1 sur 12

Transcription:

Page 1 of 12 Forums Tutoriels Magazine FAQs Blogs Projets Chat Newsletter Études Emploi Club Contacts Accueil Conception Java.NET Dév. Web EDI Langages SGBD Office Solutions d'entreprise Applications Systèmes INDEX DES BLOGS REGLES ENREGISTREZ-VOUS IDENTIFIEZ-VOUS OUVREZ VOTRE BLOG Blogs des développeurs» Android - Le blog d'un développeur Article complet: Utiliser les services sous Android 26/02/2010 14:54:06, Catégories: Récapitulatif Java, Android, Android, 6754 mots, Nicolas Druet [Android][Java] Utiliser les services sous 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 consultre la liste des vos contacts. Un service peut également rapatrier des données sur internet tels que des flux RSS. Dans ce long article nous aborderons : Les services (threads, cycle de vie...) Services Locaux (LocalService) Déclaration du service Implémentation des listeners Implémentation d'un binder Services Distants (RemoteService) Déclaration du service Mise en place des Callbacks Démarrer le service au boot du téléphone Exemple : Connexion au service MP3 [Suite:] > Les services (threads, cycle de vie...) 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 AndroidManifest.xml: <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 void ondestroy(); // libération des ressources 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-developers.blogspot.com/2010/02/service-api-changes-starting-with.html 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).

Page 2 of 12 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] L'appel de la méthode stopservice() invoque la méthode ondestroy() 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: > Services locaux (LocalService) >> Déclaration du service 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 ;

Page 3 of 12 protected void oncreate() { super.oncreate(); timer = new Timer(); Log.d(this.getClass().getName(), "oncreate"); public int onstartcommand(intent intent, int flags, int startid) { Log.d(this.getClass().getName(), "onstart"); timer.scheduleatfixedrate(new TimerTask() { public void run() { // Executer de votre tâche, 0, 60000); return START_NOT_STICKY; public void ondestroy() { Log.d(this.getClass().getName(), "ondestroy"); this.timer.cancel(); Nous déclarons le service dans le fichier AndroidManifest.xml à 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 AndroidManifest.xml... <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 : Intent intent = new Intent(this,BackgroundService.class); stopservice(intent); >> Implémentation des listeners 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 : public interface IBackgroundServiceListener { public void datachanged(object o); Nous créons l'interface IBackgroundService : public interface IBackgroundService { public void addlistener(ibackgroundservicelistener listener); public void removelistener(ibackgroundservicelistener listener); Nous implémentons l'interface dans le service : private List<IBackgroundServiceListener> listeners = null; // Ajout d'un listener public void addlistener(ibackgroundservicelistener listener) { if(listeners == null){ listeners = new ArrayList<IGoogleWeatherListener>(); listeners.add(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){

Page 4 of 12 listener.datachanged(data); Pour accéder au service depuis l'activity nous rajoutons une variable static IBackgroundService service que nous initialiserons dans la méthode oncreate() : public class BackgroundService extends Service implements IBackgroundService {...... private static IBackgroundService service; public void oncreate() {... service = this; public static IBackgroundService getservice() { return service; L'emploi d'une variable static n'est pas la meilleure solution. Nous verrons plus tard comment nous en passer. Mais à ce stade, il n'est pas possible d'accéder aux méthodes du service et d'appeler un setter. Nous poursuivons en définissant notre listener dans notre Activity: Intent intent = new // Lancement du service Intent(this,BackgroundService.class); startservice(intent); // Ajout de l'activity à la liste des listeners du service BackgroundService.getService().addListener(new IBackgroundServiceListener() { public void datachanged(object o) { // mise à jour de l'interface graphique ); Au moment voulu, dans la méthode run() du Timer nous ferons appel à la méthode privée firedatachanged(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 : android.view.viewroot$calledfromwrongthreadexception: Only the original thread that created a view hierarchy can touch its views. En effet, la méthode du listener datachanged() est appelée par le Timer, et donc exécutée dans le Thread de celui-ci. L'exception nous indique que toute mise à jour de l'interface utilisateur ne peut se faire que par le Thread responsable de la création des View, Button, TextView... Nous utilisons donc la méthode runonuithread de la classe Activity pour poster des instructions dans l'uithread : // Ajout de l'activity à la liste des listeners du service BackgroundService.getService().addListener(new IBackgroundServiceListener() { public void datachanged(final Object o) { MyActivity.this.runOnUiThread(new Runnable() { public void run() { // Mise à jour de l'ui ); ); Il nous reste à mettre à jour la méthode ondestroy() du service pour vider la liste des listeners : public void ondestroy() { this.listeners.clear(); this.timer.cancel(); Log.d(this.getClass().getName(), "ondestroy"); A ce stade, nous avons vu comment déclarer notre service et y accéder, définir une tâche de fond, et mettre à jour notre interface utilisateur. Le défaut de la solution proposée est l'utilisation d'une variable static pour accéder au service depuis l'activity. Malheureusement, la méthode startservice(intent) n'offre pas d'accès direct aux méthodes du services. Pour se faire, nous allons avoir recours au binding. >> Implémentation d'un binder 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 public 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(), modifions l'implémentation du listener et déportons notre tâche de fond dans la méthode _onstart() : public class BackgroundService extends Service implements IBackgroundService { private Timer timer ; private BackgroundServiceBinder binder ; private List<IBackgroundServiceListener> listeners = null; public void oncreate() { timer = new Timer();

Page 5 of 12 binder = new BackgroundServiceBinder(this); _onstart(); public IBinder onbind(intent intent) { return binder; public void _onstart(){ timer.scheduleatfixedrate(new TimerTask() { public void run() { if(listeners!= null){ firedatachanged(new Object());, 0, 60000); public int onstartcommand(intent intent, int flags, int startid) { _onstart(); return START_NOT_STICKY; // Ajout d'un listener public void addlistener(ibackgroundservicelistener listener) { if(listeners == null){ listeners = new ArrayList<IGoogleWeatherListener>(); listeners.add(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); public void ondestroy() { this.listeners.clear(); this.timer.cancel(); Nous définissons le binder qui nous permettra de récupérer l'instance de notre service : public class BackgroundServiceBinder extends Binder{ private IBackgroundService service = null; public BackgroundServiceBinder(IBackgroundService service) { super(); this.service = service; ; public IBackgroundService getservice(){ return service; L'avantage de l'interface IBackgroundService est de masquer les méthodes oncreate(), onbind()... et de ne proposer 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 : Intent intent = new Intent(this,BackgroundService.class); final IBackgroundServiceListener listener = new IBackgroundServiceListener() { public void datachanged(final Object data) { MyActivity.this.runOnUiThread(new Runnable() { public void run() { // Mise à jour de l'ui ); ; ServiceConnection connection = new ServiceConnection() { public void onserviceconnected(componentname name, IBinder service) { Log.i("BackgroundService", "Connected!"); IBackgroundService service = ((BackgroundServiceBinder)service).getService(); service.addlistener(listener); public void onservicedisconnected(componentname name) { Log.i("BackgroundService", "Disconnected!");

Page 6 of 12 ; bindservice(intent,connection, Context.BIND_AUTO_CREATE); 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 Context.BIND_AUTO_CREATE pour forcer le service à démarrer, s'il ne l'est pas, au moment de la connexion. La partie sur les LocalService est terminée. Je ne suis pas personnellement convaincu de leur intéret. Les classes Service, ServiceConnection, IBinder... sont relativement contraignantes, complexifient le code et n'offrent pas vraiment d'aide à la réalisation de services. Mais si un jour votre LocalService doit se transformer en RemoteService alors vous serez prets. > Services distants (RemoteService) >> Déclaration du service Contrairement aux LocalService qui s'exécutent dans le processus de l'application et plus particulièrement dans le thread principal, les RemoteService s'exécutent dans un processus totalement différent de celui de votre application. Afin que deux processus différents puissent communiquer et s'échanger des données, Google a implémenté son IDL appelé AIDL (Android Interface Definition Language) pour décrire les méthodes publiques et les données échangées. Cette description se fait dans un fichier.aidl qui devra être connu des deux processus! Pourront être échangées entre deux processus, seulement les types primitifs java (int, boolean, etc), les String, List, Map et CharSequence. Pour des objects personnalisés, vous devrez implémenter l'interface Parcelable. Un RemoteService peut être sollicité par plusieurs applications différentes. C'est pourquoi je préfère le déporter dans un nouveau projet Android sous Eclipse. Je conseille également la réalisation d'une petite interface graphique avec deux boutons pour le démarrer et l'arrêter. Si vous ne souhaitez pas créer un projet particulier, il faudra ajouter android:process=":remote" à la déclaration de votre service (<service>) dans le fichier AndroidManifest.xml. Après avoir créer un projet BackgroundService (package de l'application com.android23.backgroundservice), nous créons notre service com.android23.backgroundservice.service.backgroundservice le plus simple qui soit, avec un Timer et un objet Data: public class BackgroundService extends Service { private Timer timer ; private BackgroundServiceBinder binder ; private Data data; public void oncreate() { binder = new BackgroundServiceBinder(this); timer = new Timer(); _onstart(); public void _onstart(){ timer.scheduleatfixedrate(new TimerTask() { public void run() { data = new Data(18, Data.Weather.ENSOLEILLE);, 0, 60000); public void ondestroy() { super.ondestroy(); this.binder = null; this.timer.cancel(); public IBinder onbind(intent intent) { return binder; public Data getdata() { return data; Pour chaque RemoteService il est nécessaire de déclarer un fichier.aidl que nous plaçons dans le même package (com.android23.backgroundservice.service). Voici le contenu de notre fichier IRemoteBackgroundService.aidl : package com.android23.backgroundservice.service; import com.android23.backgroundservice.bo.data; interface IRemoteBackgroundService { int getpid(); // Renvoie le PID du processus du service Data getdata(); // Renvoie notre objet mis à jour //void updatedata(in Data data); Il n'y pas de règle concernant le nom du fichier. Ce fichier sert d'interface entre le client et le service. Nous y déclarons seulement les méthodes public. La syntaxe ressemble fortement au Java bien que cela n'en soit pas. Les mots clefs 'in', 'out', 'inout' sont ajoutés aux paramètres des méthodes. '//' permet d'ajouter des commentaires. Enfin on peut spécifier 'oneway' devant interface. Nous créons dans le package com.android23.backgroundservice.bo, notre objet java Data implémentant l'interface Parcelable : public class Data implements Parcelable { public static enum Weather {NUAGEUX,PLUVIEUX,ENSOLEILLE,BROUILLARD private int temperature;

Page 7 of 12 private Weather today; public static final Parcelable.Creator<Data> CREATOR = new Parcelable.Creator<Data>() { public Data createfromparcel(parcel in) { return new Data(in); ; public Data[] newarray(int size) { return new Data[size]; public Data(int temperature, Weather today) { super(); this.temperature = temperature; this.today = today; public int gettemperature() { return temperature; public Weather gettoday() { return today; private Data(Parcel in) { readfromparcel(in); public void readfromparcel(parcel in) { temperature = in.readint(); today = (Weather)in.readSerializable(); public int describecontents() { return 0; public void writetoparcel(parcel arg0, int arg1) { arg0.writeint(temperature); arg0.writeserializable(today); Notre objet contient le temps et la température, deux types java simples. Le code est calqué sur l'exemple que vous trouverez sur http://developer.android.com/guide/developing/tools/aidl.html. L'interface Parcelable nous oblige à définir la méthode writetoparcel() dans laquelle nous ajoutons à l'objet Parcel tous les attributs de notre objet Data. Un Parcel peut être vue comme l'unité de communication entre deux processus. Attention, l'ordre dans lequel nous ajoutons les attributs à une importance. C'est ordre doit être respecté dans la méthode readfromparcel(). L'attribut static CREATOR est obligatoire. Il implémente l'interface Parcelable.Creator. L'implémentation de cette interface nous force a écrire la méthode readfromparcel(). Pour que notre objet Data puisse voyager d'un processus à l'autre, il nous reste à déclarer son fichier Data.aidl : package com.android23.backgroundservice.bo; parcelable Data; Le mot clef parcelable spécifie que notre objet Data implémente bien le protocole Google pour les communication IPC. Les erreurs contenues dans les fichiers.aidl devraient disparaitre. Eclipse génère alors l'interface Java IRemoteBackgroundService dans le répertoire source "gen" du projet : Il nous reste ensuite à déclarer notre BackgroundServiceBinder qui permettra au client d'accéder aux méthodes déclarées dans le fichier aidl. Notre binder hérite de IRemoteBackgroundService.Stub généré par Eclipse : public class BackgroundServiceBinder extends IRemoteBackgroundService.Stub{

Page 8 of 12 private BackgroundService service = null; public BackgroundServiceBinder(BackgroundService service) { super(); this.service = service; public Data getdata() throws RemoteException { return service.getdata(); ; public int getpid() throws RemoteException { return Process.myPid(); N'oublions pas de déclarer notre service dans le fichier AndroidManifest.xml : <application android:icon="@drawable/icon" android:label="@string/app_name"> <service android:exported="true" android:name=".service.backgroundservice" /> </application> Il est possible d'ajouter une permission : <permission android:name="com.android23.backgroundservice.backgroundservice_permission" /> <application android:icon="@drawable/icon" android:label="backgroundservice"> <service android:exported="true" android:enabled="true" android:name=".service.backgroundservice" android:permission="com.android23.backgroundservice.backgroundservice_permission"/> </application> Les clients devront déclarer cette permission dans leur fichier manifest pour se connecter au service. Sans quoi une SecurityException sera levée à l'appel des méthodes startservice ou bindservice. Pour plus d'informations concernant les permissions, jetez un coup d'oeil à la documentation Google: http://developer.android.com/guide/topics/manifest/manifest-intro.html#perms http://developer.android.com/guide/topics/security/security.html Revenons à notre projet Eclipse principal. Pour réaliser notre appel distant au service, nous devons y copier/coller les fichier.aidl Data.aidl, et IRemoteBackgroundService.aidl dans le même package que le projet BackgroundService, ainsi que votre objet java Data! Eclipse créera automatiquement les interfaces d'accès. Pour nous connecter au service, rien de bien compliqué : Intent intent = new Intent(); intent.setclassname("com.android23.backgroundservice", "com.android23.backgroundservice.service.backgroundservice"); ServiceConnection remoteconnection = new ServiceConnection() { public void onservicedisconnected(componentname name) { ; public void onserviceconnected(componentname name, IBinder service) { IRemoteBackgroundService service = IRemoteBackgroundService.Stub.asInterface(service); try { service.getpid(); service.getdata(); catch (RemoteException e) { // TODO Auto-generated catch block bindservice(intent, remoteconnection, Context.BIND_AUTO_CREATE); Nous passons au constructeur de l'intent le package principal de notre projet BackgroundService puis la classe du service. IRemoteBackgroundService.Stub.asInterface() nous permet de récupérer notre service. A noter également que chaque appel de méthode peut lever une RemoteException. Pour nous déconnecter au service, nous faisons appel à la méthode unbindservice(serviceconnection) qui prend en paramêtre l'object ServiceConnection que nous avons créé : unbindservice(remoteconnection); Il est indispensable d'ajouter la permission dans le fichier AndroidManifest.xml, au même niveau que la balise <application> sans quoi notre Activity ne se connectera jamais : <uses-permission android:name="com.android23.backgroundservice.backgroundservice_permission"></usespermission> Il n'est pas possible de débugger à la fois le service et notre application. Pour tester, notre service il sera nécessaire de développer dans notre projet BackgroundService une Activity avec 2 boutons : un pour démarrer le service, et l'autre pour l'arrêter. Pour tester notre application, nous déployons le service en premier puis nous lançons notre application en mode debug. >> Mise en place d'un syteme de Callback/Listeners Comme pour les LocalService, les RemoteService peuvent également notifier leurs clients grâce une liste RemoteCallbackList. Pour cela nous allons apporter quelques modifications. Pour commencer, retournons dans le projet BackgroundService, et modifions le fichier IRemoteBackgroundService.aidl en supprimant la méthode getdata() et en rajoutant les méthodes registercallback et unregistercallback qui permettrons aux clients de s'enregistrer auprès du service : package com.android23.backgroundservice.service; import com.android23.backgroundservice.service.iremotebackgroundservicecallback;

Page 9 of 12 interface IRemoteBackgroundService { int getpid(); void registercallback(iremotebackgroundservicecallback cb); void unregistercallback(iremotebackgroundservicecallback cb); A travers cette interface, les clients pourront s'enregistrer. Une fois enregistrés il leur faudra implémenter l'interface IRemoteBackgroundServiceCallback que nous déclarons dans le fichier IRemoteBackgroundServiceCallback.aidl : package com.android23.backgroundservice.service; import com.android23.backgroundservice.bo.data; interface IRemoteBackgroundServiceCallback { void datachanged(in Data data); Nous déclarons dans notre service, la liste des clients à l'écoute : final RemoteCallbackList<IRemoteBackgroundServiceCallback> callbacks = new RemoteCallbackList<IRemoteBackgroundServiceCallback>(); public RemoteCallbackList<IRemoteBackgroundServiceCallback> getcallbacks() { return callbacks; Nous mettons à jour la méthode _onstart() afin de créer un nouvel objet Data toutes les minutes et notifier les clients : public void _onstart() { Log.i(getClass().getSimpleName(), "_onstart()"); timer.scheduleatfixedrate(new TimerTask() { public void run() { data = new Data(++counter, Data.Weather.BROUILLARD); // Broadcast to all clients the new value. final int N = callbacks.beginbroadcast(); for (int i = 0; i < N; i++) { try { callbacks.getbroadcastitem(i).datachanged(data); catch (RemoteException e) { callbacks.finishbroadcast();, 0,60000); callbacks.beginbroadcast() et callbacks.finishbroadcast(); encadre le début et la fin de la notification des clients. callbacks.getbroadcastitem(i) vous permet d'appeler les méthodes de l'intarface IRemoteBackgroundServiceCallback et prévenir les clients. Elle ne peut être appelée qu'après beginbroadcast() Nous désactivons la liste des callbacks dans la méthode ondestroy() : public void ondestroy() { super.ondestroy(); this.binder = null; this.callbacks.kill(); // Désactive tous les éléments de la liste this.timer.cancel(); N'oublions pas de mettre à jour notre Binder implémentant l'interface IRemoteBackgroundService.Stub : public class BackgroundServiceBinder extends IRemoteBackgroundService.Stub{ private BackgroundService service = null; public BackgroundServiceBinder(BackgroundService service) { super(); this.service = service; public int getpid() throws RemoteException { return Process.myPid(); public void registercallback(iremotebackgroundservicecallback cb) throws RemoteException { if(cb!= null){ service.getcallbacks().register(cb); ; public void unregistercallback(iremotebackgroundservicecallback cb) throws RemoteException { if(cb!= null){ service.getcallbacks().unregister(cb); Retournons maintenant du côté de notre application principale pour y copier les fichiers IRemoteBackgroundService.aidl et IRemoteBackgroundServiceCallback.aidl dans le package com.android23.backgroundservice.service.

Page 10 of 12 Puis mettons à jour la manière dont nous nous connectons au service en déclarant un IRemoteBackgroundServiceCallback et en l'inscrivant auprès du service dans la méthode onserviceconnected() : Intent intent = new Intent(); intent.setclassname("com.android23.backgroundservice", "com.android23.backgroundservice.service.backgroundservice"); final IRemoteBackgroundServiceCallback callback = new IRemoteBackgroundServiceCallback.Stub() { public void datachanged(data data) throws RemoteException { Log.d( getclass().getsimplename(), "Temperature : " + data.gettemperature()); Log.d( getclass().getsimplename(), "Temps : " + data.gettoday().tostring()); ; ServiceConnection remoteconnection = new ServiceConnection() { public void onservicedisconnected(componentname name) { Log.d( getclass().getsimplename(), "onserviceconnected()" ); ; public void onserviceconnected(componentname name, IBinder service) { remotebackgroundservice = IRemoteBackgroundService.Stub.asInterface(service); try { remotebackgroundservice.getpid(); remotebackgroundservice.registercallback(callback); catch (RemoteException e) { bindservice(intent, remoteconnection, Context.BIND_AUTO_CREATE); Pour tester, nous déployons le service et puis nous lançons en mode debug notre application. >> Démarrer le service au boot du téléphone Dans le projet BackgroundService, nous rajoutons un BroadcastReceiver : public class BackgroundServiceReceiver extends BroadcastReceiver{ public void onreceive(context context, Intent intent) { Intent serviceintent = new Intent(); serviceintent.setclassname("com.android23.backgroundservice", "com.android23.backgroundservice.service.backgroundservice"); context.startservice(serviceintent); Nous le rajoutons dans le fichier AndroidManifest.xml : <permission android:name="com.android23.backgroundservice.backgroundservice_permission" /> <application android:icon="@drawable/icon" android:label="@string/app_name"> <service android:exported="true" android:name=".service.backgroundservice" android:permission="com.android23.backgroundservice.backgroundservice_permission" /> <receiver android:name=".service.backgroundservicereceiver"> <intent-filter> <action android:name="android.intent.action.boot_completed" /> <category android:name="android.intent.category.home" /> </intent-filter> </receiver> </application> <uses-permission android:name="com.android23.backgroundservice.backgroundservice_permission"/> Nous avons également précisé que notre service demande la permission BACKGROUNDSERVICE_PERMISSION pour pouvoir démarrer tout seul. Déployons le service sur l'émulateur en faisant un clic droit sur le projet : Run As > Android Application. Une fois déployé, re-démarrons l'émulateur en cliquant sur le bouton dans la barre d'outil d'eclipse puis sur le bouton start dans la fenêtre qui apparait. Une fois l'émulateur lancé, il suffit d'aller dans Menu > Settings > Applications > Running Services > pour constater que notre BackgroundService est bien en route. >> Exemple : Connexion au service MP3 Le lecteur MP3 d'android est lui aussi un RemoteService. Il est donc très simple de s'y connecter pour récupérer la chanson en cours, l'artiste... Pour l'exemple n'oubliez pas de lancer la lecture d'un fichier mp3. Profitons du fait qu'android soit open-source pour nous rendre sur Google Code et récupérer le fichier IMediaPlaybackService.aidl. Une fois le fichier récupéré, nous créons le package "com.android.music" dans lequel nous insérons le fichier.aidl. Eclipse génère alors automatiquement l'interface IMediaPlaybackService. Le service ne renvoyant que des types primitifs, seul ce fichier suffit au client. Nous nous connectons au lecteur mp3 comme nous nous connections à notre BackgroundService : Intent intent = new Intent(); intent.setclassname("com.android.music", "com.android.music.mediaplaybackservice"); ServiceConnection conn = new ServiceConnection() { public void onservicedisconnected(componentname componentname) { public void onserviceconnected(componentname componentname, IBinder ibinder) {

Page 11 of 12 IMediaPlaybackService service = IMediaPlaybackService.Stub.asInterface(ibinder); try { Log.i("ServiceConnection", "Playing track: " + service.gettrackname()); Log.i("ServiceConnection", "By artist: " + service.getartistname()); catch (Exception e) { ; this.bindservice(intent, conn, 0); Malheureusement, si Google modifie l'interface.aidl de son lecteur MP3 il faudra mettre à jour le fichier IMediaPlaybackService.aidl dans notre projet. > Conclusion Le post est relativement long mais regroupe tout ce que j'ai pu apprendre en parcourant le net. Je vais réfléchir à le publier au format PDF dans la rubrique tutorial. L'intérêt des LocalService reste flou à mes yeux. J'ai la fâcheuse impression de gagner plus en complexité qu'en productivité. A contrario, les RemoteService ont une véritable valeur ajoutée. Ainsi on peut imaginer un service météo commun à toutes nos applications et nos widgets. Les possibilités sont nombreuses dès lors que l'os gère le multi-taches Si vous souhaitez que certains points soient approfondis, ou corrigés, n'hésitez pas à m'en faire part. > Ressources http://developer.android.com/reference/android/app/service.html http://blog.7touchgroup.com/tag/android-services-versus-broadcast-receivers/ http://www.brighthub.com/mobile/google-android/articles/34861.aspx http://saigeethamn.blogspot.com/2009/09/android-developer-tutorial-part-9.html http://www.androidcompetencycenter.com/2009/06/start-service-at-boot/ http://www.alexc.me/android-music-app-service-currently-playing-song/231/ Commentaires: Commentaire de: vincebodi [Membre] Merci pour ce tutorial. Cependant, je ne suis toujours pas sûr d'avoir bien compris le principe du service. Une contradiction dans le premier paragraphe me désarme complétement dans le choix de mon implémentation d'une tâche de fond dans mon appli. La phrase : "Les services s'exécutent dans le Thread principal du processus parent." est pour moi très contradictoire avec la phrase qui la précède : "Les services distants (ou RemoteService) qui s'exécutent dans un processus différent de celui de application" Du coup, si je considère la première phrase, ma tâche de fond sera interrompue lors de la destruction de l'activité qui l'a lancé (ce que je ne souhaite pas). Si je considère la deuxième phrase, un RemoteService me permet d'exécuter ma tâche de fond dans un processus indépendant de l'application. Seulement, son processus parent est le processus de mon application. Voilà la source de ma grande détresse. Help ;-) 30/05/2010 @ 20:44 Commentaire de: Nicolas Druet [Membre] "Les services s'exécutent dans le Thread principal du processus parent" C'est pourquoi on crée un thread dans le localservice. Ainsi avoir un vrai tâche de fond. Mais qui sera détruite quand l'application sera fermée. "Les services distants (ou RemoteService) qui s'exécutent dans un processus différent de celui de application" Tout a fait. Mais en aucun cas son processus parent est le processus de l'application. Sinon ca n'aurait pas grand intérêt. Le RemoteService est une autre application Android sans IHM ;) 30/05/2010 @ 22:32 Commentaire de: Inrokuptible [Membre] Merci pour ce riche tutoriel. Mon activité doit être amenée à invoquer régulièrement des méthodes distantes. Dois-je pour ça appeler à chaque fois la méthode bindservice()? 31/05/2010 @ 17:40 Commentaire de: Nicolas Druet [Membre] Il suffit de faire un bind une fois pour que l'application soit "connectée" au service. 31/05/2010 @ 18:00 Commentaire de: Inrokuptible [Membre] re Est ce que les objets contenus dans l'objet qui sera échangé entre les processus devront eux aussi être déclaré dans des AIDL différents et implémenter l'interface Parcelable? Par exemple dans ta classe Data si Weather n'était pas de type primitif je l'aurai déclaré dans un AIDL et lui faire implémenter parcelable?

Page 12 of 12 23/06/2010 @ 14:15 Commentaire de: Inrokuptible [Membre] re Est ce que les objets contenus dans l'objet qui sera échangé entre les processus devront eux aussi être déclaré dans des AIDL différents et implémenter l'interface Parcelable? Par exemple dans ta classe Data si Weather n'était pas de type primitif je l'aurai déclaré dans un AIDL et lui faire implémenter parcelable? 23/06/2010 @ 14:15 Commentaire de: jekho [Membre] Bonjour, J'ai testé ce tutoriel mais lors du test sur l'émulateur android, j'ai un warning : 'unable to start service intent : "nomdemonpackage" : not found ' alors que le nom du package est bien celui que j'ai... je ne comprends pas. D'ailleurs, il y a un point sur le tutoriel qui est ambigüe : public void onserviceconnected(componentname name, IBinder service) { IRemoteBackgroundService service = IRemoteBackgroundService.Stub.asInterface(service); try { service.getpid(); service.getdata(); catch (RemoteException e) { // TODO Auto-generated catch block le 'service' du type IRemoteBackgroundService doit être différent du 'service' type IBinder non? 22/07/2010 @ 15:11 Commentaire de: jekho [Membre] désolé pour le double post, j'ai trouvé la solution : pour appeler le service par un objet Intent, mettre directement le nom de la classe du service plutôt que d'utiliser la méthode 'setclassename()', cela marche beaucoup. Super tuto sinon. 22/07/2010 @ 15:37 Commentaire de: James_Carr [Membre] A propos des binder pour les services locaux. jai implémenté un service local comme decrit dans le tuto. Jai créé deux activité : La premiere se connecte à l'activité comme dans le tuto. De cette activité, je passe à la seconde activité. j'aiconnecté la 2eme activité au même service, mais dans cette nouvelle activité, je n'arrive pas à recuperer l'instance du service. jai un NullPointerException. Normallement, plusieurs activités devraient pouvoir se connecter au même service grace au binding. Pouvez vous m'aider à resoudre ce pb? merci 07/10/2010 @ 14:45 Commentaire de: capslock [Membre] J'aime bien lire un bon article avec le code source complet à coté. J'ai beau regarder, mais je ne vois de lien pour le code de la demo... 26/12/2010 @ 17:39 Vous devez être identifié pour poster un commentaire. Responsable bénévole de la rubrique Blogs : - Contacter par email Vos questions techniques : forum d'entraide Blogs - Publiez vos articles, tutoriels et cours et rejoignez-nous dans l'équipe de rédaction du club d'entraide des développeurs francophones Nous contacter - Hébergement - Participez - Copyright 2000-2011 www.developpez.com - Legal informations.