Développement sous Android

Jean-Francois Lalande - November 2011 Le but de ce cours est de découvrir la programmation sous Android, sa plate-forme de développement et les spécificités du développement embarqué sur téléphone mobile. Ce cours est basé sur l'excellent livre Programmation Android, de la conception au déploiement avec le SDK Google Android 2 [PA].

Ce cours est mis à disposition par Jean-François Lalande selon les termes de la licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage à l'Identique 3.0 non transposé.

1 Plan du module

Ensi de Bourges - Filière STI

1 Plan du module Plan du module
1 Plan du module 2 Le SDK Android 3 Interfaces graphiques 4 Les Intents 5 Persistance des données 6 Les services, processus et threads 7 Connectivité 8 Divers 9 Annexes 10 Bibliographie 2 3 9 19 27 35 44 51 53 75

Développement sous Android - J.-F. Lalande

2 / 79

2 Le SDK Android

Ensi de Bourges - Filière STI

2 Le SDK Android
2.1 Android L'Operating System Projet ADT Les éléments d'une application Le Manifest de l'application 2.2 Les ressources Les chaines Autres valeurs simples Autres ressources 2.3 Les activités Sauvegarde des interfaces d'activité Démonstration 3 3 4 4 4 5 5 6 6 7 7 8

2.1 Android
L'ecosystème d'Android s'appuie sur deux piliers: • le langage Java • le SDK qui permet d'avoir un environnement de développement facilitant la tâche du développeur Le kit de développement donne accès à des exemples, de la documentation mais surtout à l'API de programmation du système et à un émulateur pour tester ses applications. Stratégiquement, Google utilise la licence Apache pour Android ce qui permet la redistribution du code sous forme libre ou non et d'en faire un usage commercial. Le plugin Android Development Tool permet d'intégrer les fonctionnalités du SDK à Eclipse. Il faut l'installer comme un plugin classique en précisant l'URL du plugin. Ensuite, il faut renseigner l'emplacement du SDK (préalablement téléchargé et décompressé) dans les préférences du plugin ADT.

L'Operating System
Android est en fait un système de la famille des Linux, pour une fois sans les outils GNU. L'OS s'appuie sur: • un noyau Linux (et ses drivers) • une machine virtuelle: Dalvik Virtual Machine • des applications (navigateur, gestion contact, application de téléphonie...) • des bibliothèques (SSL, SQLite, OpenGL ES, etc...) [Dalvik] est le nom de la machine virtuelle open-source utilisée sur les systèmes Android. Cette machine virtuelle exécute des fichiers .dex, plus ramassés que les .class classiques. Ce format évite par exemple la duplication des String constantes. La machine virtuelle utilise elle-même moins d'espace mémoire et l'adressage des constantes se fait par un pointeur de 32 bits.

Développement sous Android - J.-F. Lalande

3 / 79

ContentProvider): permet le partage d'informations au sein ou entre applications • des widgets (android.app.app.content.Service): il s'agit d'une activité tâche de fondsans vue associée • des fournisseurs de contenus (android.Intent): permet d'envoyer un message pour un composant externe sans le nommer explicitement • des récepteurs d'Intents (android.BroadcastReceiver): permet de déclarer être capable de répondre à des Intents • des notifications (android.zip: les ressources de l'application • bin/myapp.Activity): il s'agit d'une partie de l'application présentant une vue à l'utilisateur • des services (android. La librairie d'accès est donc redéfinie entièrement par Google.apk: application empaquetée avec ses ressource et prête pour le déploiement Les éléments d'une application Une application Android est composée des éléments suivants: • des activités (android.appwidget.Projet ADT Ensi de Bourges .*): une vue accrochée au Bureau d'Android • des Intents (android.J.jf" Développement sous Android .-F.com/apk/res/android" package="andro.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.content.dex: exécutable pour la JVM Dalvik • bin/myapp.android.xml déclare l'ensemble des éléments de l'application.Filière STI [Dalvik] n'est pas compatible avec une JVM du type Java SE ou même Java ME.content.Notifications): permet de notifier l'utilisateur de la survenue d'événements Le Manifest de l'application Le fichier AndroidManifest. Lalande 4 / 79 .class • bin/classes. <?xml version="1. Projet ADT Un projet basé sur le plugin ADT est décomposé de la manière suivante: • src/: les sources Java du projet • libs/: bibliothèques tierces • res/: • res/drawable: ressources images • res/layout: description des IHMs en XML • res/values: chaines de caractères et dimensions • gen/: les ressources auto générées par ADT • assets/: ressources brutes (raw bytes) • bin/: • bin/classes: les classes compilées en .app.

</provider> </application> </manifest> 2.-F. String hw = res.0" encoding="utf-8"?> <resources> Développement sous Android . Voici un exemple: <?xml version="1.</receiver> <provider>. Il s'agit en fait de l'identifiant de la ressource.string.. ce qui permet d'agir sur ces instances même si elles ont été créées via leur définition XML: TextView texte = (TextView)findViewById(R.2 Les ressources Ensi de Bourges .setText("Here we go !")..0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".Main" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.R.LAUNCHER" /> </intent-filter> </activity> <service>. Lalande 5 / 79 .xml.. ADT re-génère automatiquement la classe statique R à chaque changement dans le projet.getString(R.MAIN" /> <category android:name="android. Une méthode spécifique pour les objets graphiques permet de les récupérer à partir de leur id.2 Les ressources Les ressources de l'applications sont utilisées dans le code au travers de la classe statique R.id. Les ressources sont utilisées de la manière suivante: android.getXXX(id).le_texte).nom_ressource qui est de type int.J.action. texte. L'externalisation des chaines permettra de réaliser l'internationalisation de l'application. Les chaines Les chaines constantes de l'application sont situées dans res/values/strings..hello). XXX o = res. On peut alors utiliser cet identifiant ou récupérer l'instance de la ressource en utilisant la classe Resources: Resources res = getResources().</service> <receiver>. dés qu'elles sont déclarées dans le fichier XML ou que le fichier associé est déposé dans le répertoire adéquat.type_ressource.2.intent.category..Filière STI android:versionCode="1" android:versionName="1. Toutes les ressources sont accessibles au travers de R..

Filière STI <string name="hello">Hello Hello JFL !</string> <string name="app_name">AndroJF</string> </resources> Resources res = getResources().Autres valeurs simples Ensi de Bourges .0" encoding="utf-8"?> <resources> <string-array name="test"> <item>it1</item> <item>it2</item> </string-array> </resources> Autres ressources D'autres ressources sont spécifiables dans res: • les menus Développement sous Android .getString(R. comme par exemple un tableau de chaines: <?xml version="1. L'assistant de création permet de créer de nouveaux fichiers de ressources contenant des valeurs simples. Cela permet de définit des chaines.string.-F. String hw = res. des tableaux.J. Lalande 6 / 79 .hello). Autres valeurs simples Plusieurs fichies xml peuvent être placés dans res/values. des couleurs.

l'application peut avoir perdu les valeurs saisis dans les zones de texte. "Sauvegarde !".drawable) • des dimensions (R. Si une zone de texte n'a pas d'identifiant. } protected void onDestroy() { super.LENGTH_LONG). Le code suivant permet de visualiser le déclenchement des sauvegardes: protected void onSaveInstanceState(Bundle outState) { super. il est possible d'aller dans "Development tools > Development Settings" et de cocher "Immediately destroy activities".onPause(). Toast. L'activité peut passer des états: • démarrage -> actif: détient le focus et est démarré (onStart invoqué) • actif -> suspendue: ne détient plus le focus (onPause invoqué) • suspendue -> actif: onResume invoqué • suspendue -> détruit: onDestroy invoqué public class Main extends Activity { public void onCreate(Bundle savedInstanceState) { super. } protected void onStart() { super.onStop().layout. Lalande 7 / 79 . Toast. Pour forcer Android à décharger les valeurs.color) 2. } Développement sous Android .onSaveInstanceState(outState).-F.Filière STI • les images (R.J. Android ne pourra pas la sauver et elle ne pourra pas être restaurée à partir de l'objet Bundle.onResume(). rien n'est restauré. Si l'on rebascule sur l'application (appui long sur Home).2. } protected void onPause() { super. Android peut-être amené à déchargé les éléments graphiques de la mémoire pour gagner des ressources.onCreate(savedInstanceState).3 Les activités Ensi de Bourges .onStart(). lorsque l'on appuie par exemple sur la touche Home. setContentView(R. } } Sauvegarde des interfaces d'activité L'objet Bundle passé en paramètre de la méthode onCreate permet de restaurer les valeurs des interfaces d'une activité qui a été déchargée de la mémoire.show().acceuil).onDestroy().makeText(this. Si l'application est complètement détruite (tuée). En effet. } protected void onStop() { super.3 Les activités Une application Android étant hebergée sur un système embarqué.dimen) • des couleurs (R. en revenant sur le bureau. } protected void onResume() { super. le cycle de vie d'une application ressemble à celle d'une application Java ME.

Démonstration Ensi de Bourges .J.Filière STI Démonstration Développement sous Android . Lalande 8 / 79 .-F.

5 Les onglets Activité et onglets Créer les onglets Démonstration 9 9 10 10 11 11 12 12 12 13 13 13 14 14 14 15 16 17 17 18 3.3 Interfaces graphiques Ensi de Bourges .Filière STI 3 Interfaces graphiques 3.1 Vues et gabarits Attributs des gabarits L'interface comme ressource 3. Les plus importants sont: • android:layout_width et android:layout_height: • ="fill_parent": l'élément remplit tout l'élément parent Développement sous Android . Attributs des gabarits Les attributs des gabarits permettent de spécifier des attributs supplémentaires.4 Les listes Démonstration Liste de layouts plus complexes Interface résultat 3.3 Positionnement avancé Preview du positionnement 3. ce qui évite de passer par les instanciations Java. Lalande 9 / 79 .J.-F. On peut regrouper des éléments graphiques dans une ViewGroup. Des ViewGroup particuliers sont prédéfinis: ce sont des gabarits (layout) qui proposent une prédispositions des objets graphiques: • LinearLayout: dispose les éléments de gauche à droite ou du haut vers le bas • RelativeLayout: les éléments enfants les uns par rapport aux autres • TableLayout: disposition matricielle • FrameLayout: disposition en haut à gauche en empilant les éléments Les déclarations se font principalement en XML.2 Les éléments graphiques de base Les labels de texte Les zones de texte Les images Les boutons Interface résultat Démonstration 3.1 Vues et gabarits Les élements graphiques héritent de la classe View.

acceuil).layout. Ainsi. comme tous les autres objets graphiques. permettant de factoriser des morceaux d'interface.setBackgroundColor(Color.accueil.Filière STI • ="wrap_content": prend la place nécessaire à l'affichage • android:orientation: définit l'orientation d'empilement • android:gravity: définit l'alignement des éléments Voici un exemple de LinearLayout: <LinearLayout xmlns:android="http://schemas. Ainsi. il est important de spécifier un id dans la définition XML du gabarit (android:id="@+id/accueilid").android.onCreate(savedInstanceState).android.accueilid). Pour cela.L'interface comme ressource Ensi de Bourges . 3. setContentView(R.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <include android:id="@+id/include01" Développement sous Android .-F. Le nom du fichier xml.id. Cette remarque est aussi valable pour tous les autres objets graphiques. ou d'autres gabarits. par example accueil. Lalande 10 / 79 .J. } Le layout reste modifiable au travers du code. on peut accéder à cet élément par son id et agir dessus au travers du code Java: LinearLayout l = (LinearLayout)findViewById(R. On utilise dans ce cas le mot clef include: <?xml version="1. l.layout.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center" android:id="@+id/accueilid" > </LinearLayout> L'interface comme ressource Une interface graphique définie en XML sera aussi générée comme une ressource dans la classe statique R. On retrouve le même principe que les tableaux imbriqués de l'HTML.2 Les éléments graphiques de base Un gabarit peut contenir des éléments graphiques. pour associer la première vue graphique à l'activité principale de l'application. il faut faire: public void onCreate(Bundle savedInstanceState) { super.BLACK).xml permet de retrouver le layout dans le code java au travers de R. Les interfaces peuvent aussi inclure d'autres interfaces.

// empiler vers le bas ! TextView texte = new TextView(this).VERTICAL).CENTER).onCreate(savedInstanceState).-F.addView(edit).setText("Edit me"). edit.addView(texte).Les labels de texte Ensi de Bourges . } } Les zones de texte En XML: <EditText android:text="" android:id="@+id/EditText01" android:layout_width="fill_parent" android:layout_height="wrap_content"> </EditText> Par la programmation: EditText edit = new EditText(this). LinearLayout gabarit = new LinearLayout(this). gabarit. setContentView(gabarit). Interception d'événements: Développement sous Android . // centrer les éléments graphiques gabarit. gabarit.setOrientation(LinearLayout.setGravity(Gravity.Filière STI android:layout_width="wrap_content" android:layout_height="wrap_content" layout="@layout/acceuil" ></include> </LinearLayout> Les labels de texte En XML: <TextView android:id="@+id/le_texte" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello" android:layout_gravity="center" /> Par la programmation: public class Activity2 extends Activity { public void onCreate(Bundle savedInstanceState) { super. texte.setText("Programming creation of interface !").J. gabarit. Lalande 11 / 79 .

show().id. Toast.drawable.makeText(v.getContext().J.LENGTH_LONG). Les boutons En XML: <Button android:text="Go !" android:id="@+id/Button01" android:layout_width="wrap_content" android:layout_height="wrap_content"> </Button> La gestion des événements de click se font par l'intermédiaire d'un listener: Button b = (Button)findViewById(R. Lalande 12 / 79 . "Stop !". b.Button01).addView(image).addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast. gabarit.Filière STI edit. } Interface résultat Développement sous Android . } }).ensi). image. int start.setImageResource(R. Les images En XML: <ImageView android:id="@+id/logoEnsi" android:src="@drawable/ensi" android:layout_width="100px" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" ></ImageView> Par la programmation: ImageView image = new ImageView(this).Les images Ensi de Bourges . int before.-F. int count) { // do something here } ).

On inclut ensuite le texte. La difficulté est d'arriver à programmer un placement qui n'est pas dépendant de l'orientation ou de la taille de l'écran. ImageView. Cela peut être particulièrement utile si par exemple on réalise une liste d'item qui contienne à chaque fois un texte et une icone. on trouve une explication pour réaliser un placement simple: un texte à gauche et une image à droite de l'écran.-F.android. • Puis. l'image étant censée être à droite. alignée avec le texte. Dans [VL]. Lalande 13 / 79 . Pour aligner les éléments. Son orientation doit être horizontal et sa gravité (gravity) center.Filière STI Ce screenshot montre une interface contenant des TextView.com/apk/res/android" android:layout_height="fill_parent" android:orientation="horizontal" android:layout_width="fill_parent" android:gravity="center"> <TextView . Preview du positionnement Le layout décrit ci-avant ressemble à: <LinearLayout xmlns:android="http://schemas. il est souvent nécessaire de réaliser correctement le positionnement des éléments graphiques. il faut créer un LinearLayout consécutif au texte et préciser que la gravité (gravity) est right. il faut préciser que la gravité du layout (layout_gravity) est center.3 Positionnement avancé Pour obtenir une interface agréable. EditText. Le principe réside dans l'imbrication de LinearLayout: • Un premier layout contiendra l'ensemble des éléments.J. Démonstration 3.></TextView> <LinearLayout android:layout_height="wrap_content" Développement sous Android . et un bouton (cf.Démonstration Ensi de Bourges .. ANX_Interfaces-graphiques)..

.. for (int i=0./> </LinearLayout></LinearLayout> Ce qui produit dans l'interface de preview.add("coucou " + i). Prenons un exemple simple: une liste de chaine de caractères.> <ListView android:id="@+id/listView1" . R. il suffit juste de poser un layout linéaire et d'y implanter une ListView. Lalande 14 / 79 . ArrayAdapter<String> tableau = new ArrayAdapter<String>(list.layout.. i<40.> </ListView></LinearLayout> Etant donné qu'une liste peut contenir des éléments graphiques divers et variés.listView1). les éléments de la liste doivent être insérés dans un ListAdapter et il faut aussi définir le gabarit qui sera utilisé pour afficher chaque élément du ListAdapter.3. i++) { tableau.setAdapter(tableau).montexte).getContext(). en orientation portrait: 3.. Démonstration (cf ANX_Listes-pour-des-items-texte) Liste de layouts plus complexes Développement sous Android .-F.4 Les listes Au sein d'un gabarit. Dans ce cas. Le gabarit suivant doit être placé dans montexte. Le XML du gabarit est donc: <LinearLayout .> </TextView> Le code de l'application qui créé la liste peut être: ListView list = (ListView)findViewById(R.id. on peut implanter une liste que l'on pourra dérouler si le nombre d'éléments est important...J. on créé un nouveau gabarit montexte et on ajoute dynamiquement un ArrayAdapter à la liste listView1. Si l'on souhaite faire une liste plein écran.4 Les listes Ensi de Bourges .xml: <TextView ... } list.Filière STI android:orientation="horizontal" android:layout_width="fill_parent" android:gravity="right" android:layout_gravity="center"> <Image .

-F. int resource.Interface résultat Ensi de Bourges . ArrayAdapter<String> tableau = new ArrayAdapter<String>( list. Avec le layout de liste suivant (ligne.monTexte). } list.> <TextView .. R. il faut utiliser un autre constructeur de ArrayAdapter (ci-dessous) où resource est l'id du layout à appliquer à chaque ligne et textViewResourceId est l'id de la zone de texte inclu dans ce layout complexe.id. A chaque entrée de la liste.J.getContext(). int textViewResourceId) Le code de l'exemple précédent doit être adapté comme ceci: ListView list = (ListView)findViewById(R.. i<40. i++) { tableau.id..Filière STI Lorsque les listes contiennent un layout plus complexe qu'un texte. R. android:id="@+id/monTexte"/> <LinearLayout> <ImageView /> </LinearLayout> </LinearLayout> Interface résultat (cf ANX_Listes-de-layouts-complexes) Développement sous Android . Lalande 15 / 79 . ArrayAdapter (Context context..setAdapter(tableau). for (int i=0.layout.ligne.maliste). la vue générée utilisera le layout complexe et la zone de texte contiendra la string passée en argument à la méthode add.add("coucou " + i).xml): <LinearLayout .

5 Les onglets La réalisation d'onglets permet de mieux utiliser l'espace réduit de l'écran. il faut suivre une structure très particulière pour le gabarit.3.5 Les onglets Ensi de Bourges .android.Filière STI 3.-F. Lalande 16 / 79 .J.com/apk/res/android"> <LinearLayout android:padding="5dp" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"> <TabWidget android:id="@android:id/tabs" android:layout_width="fill_parent" android:layout_height="wrap_content"/> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="fill_parent" android:layout_height="fill_parent" android:padding="5dp"/> </LinearLayout> </TabHost> Certains ids sont imposés lorsqu'on utilise des onglets: • TabHost : android:id= "@android:id/tabhost" • TabWidget : android:id= "@android:id/tabs" • FrameLayout : android:id= "@android:id/tabcontent" Développement sous Android . La définition du gabarit doit être la suivante: <TabHost android:id="@android:id/tabhost" android:layout_width="fill_parent" android:layout_height="fill_parent" xmlns:android="http://schemas. en associant à chaque onglet l'activité correspondante. Les différents onglets sont ensuite créé dynamiquement par le code. Pour réaliser les onglets.

// Choisir l'onglet par défaut tabHost. Intent intent. ActivityOnglet1.setContent(intent).setIndicator("Onglet 1").. dans sa partie du bas. setContentView(R.class)..> <!-.setClass(this.addTab(spec).. spec.setCurrentTab(0).onglet1).-F.layout.TabSpec spec. // Ajout de l'onglet . Lalande 17 / 79 . // Création dynamique d'une configuration pour l'onglet 1 spec = tabHost.layout. .onCreate(savedInstanceState). setContentView(R..newTabSpec("Onglet 1"). il faut donc définir une classe d'activité et lui associer son gabarit: public class ActivityOnglet1 extends Activity { public void onCreate(Bundle savedInstanceState) { super.. // Associer l'intent à l'onglet spec.xml --> <RatingBar android:id="@+id/ratingBar1" android:layout_width="wrap_content" android:layout_height="wrap_content"></RatingBar> <SeekBar android:layout_height="wrap_content" android:id="@+id/seekBar1" android:layout_width="fill_parent"></SeekBar> </LinearLayout> Créer les onglets Ensuite. une activité qu'il convient de créer.J. }} <LinearLayout . dans l'activité principale héritant de TabActivity.Activité et onglets Ensi de Bourges .onglet1.onCreate(savedInstanceState). // Ajouter un texte dans l'onglet tabHost. // Création de l'intent lancant l'activité de l'onglet intent = new Intent(). Le gabarit précédents est donc associé à une classe à définir héritant de TabActivity: public class AndroTabs2Activity extends TabActivity { @Override public void onCreate(Bundle savedInstanceState) { super. Pour chaque activité. il faut créer dynamiquement les onglets et les remplir: TabHost tabHost = getTabHost(). TabHost..Filière STI Activité et onglets L'activité qui gère l'ensemble des onglets est une activité spéciale héritant de TabActivity. Sans oublier de déclarer les activités dans le Manifest: Développement sous Android . Chaque onglet contient.main).

Filière STI <activity android:name="ActivityOnglet1"></activity> <activity android:name="ActivityOnglet2"></activity> Démonstration (cf ANX_Onglets) Développement sous Android .J.Démonstration Ensi de Bourges .-F. Lalande 18 / 79 .

une autre application ou une autre activité de l'application courante.1 Principe des Intents 4.4 Recevoir et filtrer les Intents Filtrage d'un Intent par l'activité Réception par un BroadcastReceiver Récepteur d'Intent dynamique Les messages natifs Principe de la Démonstration Démonstration 19 19 20 20 21 21 21 22 22 23 23 23 24 24 24 25 25 26 26 4. sous forme de chaine de caractères • les données: contenu MIME et URI • des données supplémentaires sous forme de paires de clef/valeur • une catégorie pour cibler un type d'application • des drapeaux (information supplémentaire) 4.4 Les Intents Ensi de Bourges .2 Utilisation d'Intents Retour d'une activité Résultat d'une activité Principe de la Démonstration Démonstration 4. Un objet Intent contient les information suivantes: • le nom du composant ciblé (facultatif) • l'action à réaliser.Filière STI 4 Les Intents 4. Le but des Intents est de déléguer une action à un autre composant. Lalande 19 / 79 .3 Gérer les actions Contrôler les actions permises Exemples de permissions Echec de permissions Démonstration Broadcaster des Intents informatifs 4.-F.J.2 Utilisation d'Intents Il y a plusieurs façons d'appeler un Intent: Développement sous Android .1 Principe des Intents Les Intents permettent de gérer l'envoi et la réception de messages afin de faire coopérer les applications.

} Résultat d'une activité Il est aussi possible de définir un résultat d'activité. l'activité courante prend fin et revient à l'activité précédente. une activité peut vouloir récupérer un code de retour de l'activité "enfant". GivePhoneNumber. telnumber). Button b = (Button)findViewById(R. Lalande 20 / 79 . il devient possible de filtrer le code de retour dans la méthode onActivityResult pour savoir si l'on revient ou pas de l'activité enfant. } })..ACTION_DIAL.-F.LENGTH_LONG). int resultCode. Sans oublier de déclarer la nouvelle activité dans le Manifest.J. .setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Uri telnumber = Uri. Toast.48).Filière STI • on crée l'objet et on passe la classe de l'activité ciblée par l'Intent: cela revient à passer la main à une autre activité de l'application.class). Retour d'une activité Lorsque le bouton retour est pressé. la méthode setResult permet d'enregistrer un code de retour qu'il sera Développement sous Android . startActivityForResult(login. startActivity(login). Lorsque l'activité parent reprend la main.Retour d'une activité Ensi de Bourges . } Le filtrage dans la classe parente permet de savoir qui avait appelé cette activité enfant: protected void onActivityResult(int requestCode.Button01). Intent data) { if (requestCode == 48) Toast. L'appel d'un Ident devient donc: public void onCreate(Bundle savedInstanceState) { Intent login = new Intent(getApplicationContext().class). b. startActivity(call). Cela permet par exemple de terminer son appel téléphonique et de revenir à l'interface ayant initié l'appel.parse("tel:0248484000"). "Code de requête récupéré (je sais d'ou je viens)". • on crée l'objet et on lui donne les données et l'URI cible: l'OS est chargé de trouver une application pouvant répondre à l'Intent. On utilise pour cela la méthode startActivityForResult qui envoie un code de retour à l'activité enfant..makeText(this. Intent call = new Intent(Intent.id.show(). Au sein d'une application. avant d'appeler explicitement la fin d'une activité avec la méthode finish(). GiveLogin. Intent login = new Intent(this. Dans ce cas.

}}).makeText(this.makeText(this. Lalande 21 / 79 . on met donc: Button finish = (Button)findViewById(R. "Code de requête récupéré (je sais d'ou je viens)".J.3 Gérer les actions Développement sous Android .finish). finish. Dans l'activité enfant.Filière STI aussi possible de filtrer dans l'activité parente. Toast.show(). int resultCode. Toast.Principe de la Démonstration Ensi de Bourges . } Principe de la Démonstration (cf ANX_Intents) Démonstration 4. Et la classe parente peut filtrer ainsi: protected void onActivityResult(int requestCode.-F. if (resultCode == 50) Toast.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { setResult(50).show(). finish().LENGTH_LONG). Intent data) { if (requestCode == 48) Toast.id. "Code de retour ok (on m'a renvoyé le bon code)".LENGTH_LONG).

Intent. emailIntent.com". emailIntent. Les autres actions sont: • ACTION_CALL (ANSWER.J.}.EXTRA_TEXT.content. si vous n'avez pas précisé à la création de projet de numéro de version minimum. permettant au développeur de déclarer les permissions requises par son application. "Message"). Lalande 22 / 79 . "". finish()..putExtra(android. L'utilisateur peut ainsi savoir.content.ACTION_VIEW qui permet d'appeler une application pour visualiser un contenu dont on donne l'URI.Intent. DIAL): passer/réceptionner/afficher un appel • ACTION_EDIT (DELETE): éditer/supprimer une donnée • ACTION_SEND: envoyer des données par SMS ou E-mail • ACTION_WEB_SEARCH: rechercher sur internet Voici un exemple d'envoi d'[E-mail]: Intent emailIntent = new Intent(android. recipients). emailIntent.EXTRA_SUBJECT.content. Exemples de permissions Ces exemples montrent deux applications n'ayant pas les mêmes permissions: Développement sous Android . les permissions READ_PHONE_STATE et WRITE_EXTERNAL_STORAGE sont accordées par défaut [SO]. Par contre. "Send mail.ACTION_SEND).setType("text/plain").permission. emailIntent.Intent.RECEIVE_SMS"/> Attention. Pour avoir le nombre minimal de permissions. La plus courante est l'action Intent.EXTRA_EMAIL. "Test").Contrôler les actions permises Ensi de Bourges .createChooser(emailIntent. au moment de l'installation. via un Intent. Attention à ne pas confondre permissions et délégation: ne pas donner la permission de passer un appel n'empêche pas l'application de déléguer. le passage d'un appel. String[] recipients = new String[]{"my@email. startActivity(Intent.putExtra(android. un appel direct depuis l'application n'est pas possible. Contrôler les actions permises Android propose un système de permissions.")).putExtra(android.Intent. les permissions demandées par l'application.-F. ce qui est particulièrement utile pour une application dans laquelle l'utilisateur n'a pas totalement confiance. Pour déclarer une permission. il faut ajouter au Manifest: <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="4" /> La liste complètes des permissions permet de contrôler finement les droits alloués à l'application.. il suffit de le préciser dans le Manifest: <uses-permission android:name="android.Filière STI Plusieurs actions natives existent par défaut sur Android.content.

Echec de permissions

Ensi de Bourges - Filière STI

Echec de permissions
Sans aucun test utilisateur, ne pas donner une permission provoque la levée d'une exception dans l'application, comme pour la permission CALL_PHONE dans cet exemple:

Démonstration Broadcaster des Intents informatifs
Il est aussi possible d'utiliser un objet Intent pour broadcaster un message à but informatif. Ainsi, toutes les applications pourront capturer ce message et récupérer l'information. Intent broadcast = new Intent("andro.jf.broadcast"); broadcast.putExtra("extra", "test"); sendBroadcast(broadcast);

Développement sous Android - J.-F. Lalande

23 / 79

4.4 Recevoir et filtrer les Intents

Ensi de Bourges - Filière STI

La méthode putExtra permet d'enregistrer un paire clef/valeur dans l'Intent. On peut récupérer les données à l'aide de la méthode getExtras dans l'objet Bundle qui est dans l'Intent: Bundle extra = intent.getExtras(); String val = extra.getString("extra");

4.4 Recevoir et filtrer les Intents
Etant donné la multitude de message d'Intent, chaque application doit pouvoir facilement "écouter" les Intents dont elle a besoin. Android dispose d'un système de filtres déclaratifs permettant de définir dans le Manifest des filtres. Un filtre peut utiliser plusieurs niveaux de filtrage: • action: identifie le nom de l'Intent. Pour éviter les collisions, il faut utiliser la convention de nommage de Java • category: permet de filtrer une catégorie d'action (DEFAULT, BROWSABLE, ...) • data: filtre sur les données du message par exemple en utilisant android:host pour filtrer un nom de domaine particulier

Filtrage d'un Intent par l'activité
En déclarant un filtre au niveau du tag activity, l'application déclare les types de messsage qu'elle sait gérer. <activity android:name=".Main" android:label="@string/app_name"> <intent-filter> <action android:name="andro.jf.nom_du_message" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> Ainsi, l'application répond à la sollicitation des Intents envoyés par: Button autoinvoc = (Button)findViewById(R.id.autoinvoc); autoinvoc.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent broadcast = new Intent("andro.jf.nom_du_message"); broadcast.putExtra("extra", "test"); startActivity(broadcast); }}); L'application est relancée (ou lancée). Elle peut récupérer les données de l'Intent dans son main: String data = getIntent().getDataString(); Uri uri = getIntent().getData();

Réception par un BroadcastReceiver

Développement sous Android - J.-F. Lalande

24 / 79

Récepteur d'Intent dynamique

Ensi de Bourges - Filière STI

Les messages broadcastés par les applications sont réceptionnées par une classe héritant de BroadcastReceiver. Cette classe doit surcharger la méthode onReceive. Dans le Manifest, un filtre doit être inséré dans un tag receiver qui pointe vers la classe se chargeant des messages. Dans le Manifest: <application android:icon="@drawable/icon" android:label="@string/app_name"> <receiver android:name="MyBroadcastReceiver"> <intent-filter> <action android:name="andro.jf.broadcast" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver> </application> La classe héritant de BroadcastReceiver: public final class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Bundle extra = intent.getExtras(); if (extra != null) { String val = extra.getString("extra"); Toast.makeText(context, "Broadcast message received: " + val, Toast.LENGTH_SHORT).show(); }}}

Récepteur d'Intent dynamique
Il est possible de créer un récepteur et d'installer le filtre correspondant dynamiquement. private MyBroadcastReceiverDyn myreceiver; public void onCreate(Bundle savedInstanceState) { // Broadcast receiver dynamique myreceiver = new MyBroadcastReceiverDyn(); IntentFilter filtre = new IntentFilter("andro.jf.broadcast"); registerReceiver(myreceiver, filtre); } Lorsque ce receiver n'est plus utile, il faut le désenregistrer pour ainsi libérer les ressources: unregisterReceiver(myreceiver); C'est particulièrement important puisque un filtre de réception reste actif même si l'application est éteinte (elle peut être relancée si un message la concernant survient).

Les messages natifs
Un certain nombre de messages sont diffusés par l'OS:

Développement sous Android - J.-F. Lalande

25 / 79

Tous les messages de broadcast se trouvent dans la documentation des Intents.-F. Principe de la Démonstration Démonstration (cf ANX_Receveur-de-broadcasts) Développement sous Android ..Principe de la Démonstration Ensi de Bourges . Lalande 26 / 79 .Filière STI • ACTION_BOOT_COMPLETED: diffusé lorsque le système a fini son boot • ACTION_SHUTDOWN: diffusé lorsque le système est en cours d'extinction • ACTION_SCREEN_ON / OFF: allumage / exctinction de l'écran • ACTION_POWER_CONNECTED / DISCONNECTED: connexion / perte de l'alimentation • ACTION_TIME_TICK: une notification envoyée toutes les minutes • ACTION_USER_PRESENT: notification recue lorsque l'utilisateur délock son téléphone • .J..

Android fournit aussi automatiquement. null).2 Préférences partagées Représentation XML d'un menu de préférences Activité d'édition de préférences Attributs des préférences Préférences: toggle et texte Préférences: listes Ecriture de préférences Démonstration 5.5 Persistance des données Ensi de Bourges .-F.getString("login".Filière STI 5 Persistance des données 5.J.3 Les fichiers 5.5 XML SAX parser DOM parser 27 27 28 28 29 29 30 30 31 31 31 32 32 33 33 5. 5. S'il est nécessaire de réaliser une sauvegarde plus personnalisée.MODE_PRIVATE). Long nom = prefs. ce qui le renvoie à la bonne activité lorsqu'il appuie sur la touche Retour.getLong("taille". la persistance du chemin de navigation de l'utilisateur.1 Différentes persistances 5.1 Différentes persistances Android fournit plusieurs méthodes pour faire persister les données applicatives: • la persistance des données de l'activité (cf Le SDK Android) • un mécanisme de sauvegarde clé/valeur. utilisé pour les fichiers de préférences (appelé préférences partages) • des entrées sorties de type fichier • une base de donnée basé sur SQLite La persistance des données des activités est géré par l'objet Bundle qui permet de restaurer les View qui possède un id. String nom = prefs. Lalande 27 / 79 . Développement sous Android . null). On récupère un tel objet par l'appel à getPreferences: SharedPreferences prefs = getPreferences(Context.4 BDD SQLite Lecture / Ecriture dans la BDD 5.2 Préférences partagées La classe SharedPreferences permet de gérer des paires de clé/valeurs associées à une activité. il suffit de recoder les méthodes onSaveInstanceState et onCreate et d'utiliser les méthodes qui permettent de lire/écrire des données sur l'objet Bundle.

android. other preferences here . Lalande 28 / 79 . addPreferencesFromResource(R.xml: <PreferenceScreen xmlns:android="http://schemas. on crée un bouton et un Intent correspondant.. Le mode MODE_PRIVATE restreint l'accès au fichier créé à l'application. L'intérêt d'utiliser le système de préférences prévu par Android réside dans le fait que l'interface graphique associé à la modification des préférences est déjà programmé: pas besoin de créer l'interface de l'activité pour cela. à stocker dans res/xml/preferences. int) à partir du nom de la classe de l'activité courante.J.. la classe permet d'afficher un écran composé de modificateurs pour chaque type de préférences déclarées. Les modes d'accès MODE_WORLD_READABLE et MODE_WORLD_WRITABLE permettent aux autres applications de lire/écrire ce fichier. Développement sous Android .-F.onCreate(savedInstanceState). </PreferenceScreen> </PreferenceScreen> Activité d'édition de préférences Pour afficher l'écran d'édition des préférences correspondant à sa description XML.. A partir d'une description XML des préférences.preferences). L'interface sera générée automatiquement par Android et peut ressembler par exemple à cela: Représentation XML d'un menu de préférences Une activité spécifique a été programmée pour réaliser un écran d'édition de préférences.Filière STI La méthode getPreferences(int) appelle en fait getPreferences(String.xml.com/apk/res/android" android:key="first_preferencescreen"> <CheckBoxPreference android:key="wifi enabled" android:title="WiFi" /> <PreferenceScreen android:key="second_preferencescreen" android:title="WiFi settings"> <CheckBoxPreference android:key="prefer wifi" android:title="Prefer WiFi" /> .. Il s'agit de PreferenceActivity. il faut créer une nouvelle activité qui hérite de PreferenceActivity et simplement appeler la méthode addPreferencesFromResource en donnant l'id de la description XML: public class MyPrefs extends PreferenceActivity { public void onCreate(Bundle savedInstanceState) { super. Voici un exemple de déclarations de préférences XML.Représentation XML d'un menu de préférences Ensi de Bourges . }} Pour lancer cette activité.

On peut faire dépendre une préférence d'une autre.getString("login". /> Préférences: toggle et texte Une case à cocher se fait à l'aide de CheckBoxPreference <CheckBoxPreference android:key="wifi" android:title="Utiliser le wifi" android:summary="Synchronise l'application via le wifi. Par exemple. par exemple android:summaryOn pour les cases à cocher qui donne la chaine à afficher lorsque la préférence est cochée." android:summaryOff="L'application ne se synchronise pas. on utiliser la méthode getDefaultSharedPreferences sur la classe PreferenceManager. C'est la clef spécifiée par l'attribut android:key qui est utilisée pour récupérer la valeur choisie par l'utilisateur." android:summaryOn="L'application se synchronise via le wifi..getDefaultSharedPreferences(getApplicationContext()). à l'aide de l'attribut android:dependency.Filière STI Attributs des préférences Les attributs suivants sont utiles: • android:title: La string apparaissant comme nom de la préférence • android:summary: Une phrase permettant d'expliciter la préférence • android:key: La clef pour l'enregistrement de la préférence Pour accéder aux valeurs des préférences. String login = prefs. Lalande 29 / 79 .J. Des attributs spécifiques à certains types de préférences peuvent être utilisés.-F.""). SharedPreferences prefs = PreferenceManager." /> Un champs texte est saisi via EditTextPreference: <EditTextPreference android:key="login&" android:title="Login utilisateur" android:summary="Renseigner son login d'authentification. On revient alors à la lecture des préférences comme pour celles qui sont associées à une activité.. on peut spécifier dans cet attribut le nom de la clef d'une préférence de type case à cocher: <CheckBoxPreference android:key="wifi" . /> <EditTextPreference android:dependency="wifi" .." android:dialogTitle="Veuillez saisir votre login" /> Développement sous Android ..Attributs des préférences Ensi de Bourges .

putString("login". Editor editor = prefs. par exemple si une action fait changer le paramétrage de l'application ou si l'on recoit le paramétrage par le réseau. Moyenne=5.commit().Petite=1.-F. Ecriture de préférences Il est aussi possible d'écraser des préférences par le code.edit(). "jf"). editor. editor.Filière STI Préférences: listes Une entrée de préférence peut être liée à une liste de paires de clef-valeur dans les ressources: <resources> <array name="key"> <!-. la préférence vitesse est associée à "1".MODE_PRIVATE). Développement sous Android .J.Préférences: listes Ensi de Bourges . Lalande 30 / 79 . L'écriture de préférences est plus complexe: elle passe au travers d'un éditeur qui doit réaliser un commit des modifications (commit atomique. pour éviter un mix entre plusieurs écritures simultanées): SharedPreferences prefs = getPreferences(Context. Grande=20 --> <item>"Petite"</item> <item>"Moyenne"</item> <item>"Grande"</item> </array> <array name="value"> <item>"1"</item> <item>"5"</item> <item>"20"</item> </array> </resources> qui se déclare dans le menu de préférences: <ListPreference android:title="Vitesse" android:key="vitesse" android:entries="@array/key" android:entryValues="@array/value" android:dialogTitle="Choisir la vitesse:" android:persistent="true"> </ListPreference> Lorsque l'on choisit la préférence "Petite".

Filière STI Il est même possible de réagir à un changement de préférences en installant un écouteur sur celles-ci: prefs. } Les ressources permettent aussi de récupérer un fichier embarqué dans l'application: Resources res = getResources().registerOnSharedPreferenceChangeListener( new OnSharedPreferenceChanged () { . Démonstration (cf ANX_Gestion-des-préférences) 5.-F.fichier).. Mieux vaut alléger l'application au maximum et prévoir le téléchargement de ressources nécessaires et l'utilisation de fournisseurs de contenu. . [DBSQL] présente un exemple de création de base de données.".. Le choix de Google est de s'appuyer sur les classes classiques de Java EE tout en simplifiant la gestion des permissions et des fichiers embarqués dans l'application. ce qui se fait en héritant de SQLiteOpenHelper: public class DictionaryOpenHelper extends SQLiteOpenHelper { private static final int DATABASE_VERSION = 2. 5.J.3 Les fichiers Android fournit aussi un accès classique au système de fichier pour tous les cas qui ne sont pas couverts par les préférences ou la persistance des activités.. Même si la base doit être utilisée avec parcimonie. on retrouve comme pour les préférences les constantes MODE__PRIVATE et MODE_WORLD_READABLE/WRITABLE à passer en paramètre de l'ouverture du fichier. }).. MODE_PRIVATE).raw. Lalande 31 / 79 . " + KEY_DEFINITION + " TEXT). private static final String DICTIONARY_TABLE_CREATE = "CREATE TABLE " + DICTIONARY_TABLE_NAME + " (" + KEY_WORD + " TEXT. cela fournit un moyen efficace de gérer une petite quantité de donnée. c'est à dire de nombreux cas. Pour la gestion des permissions.4 BDD SQLite Android dispose d'une base de donnée relationelle basée sur SQLite. private static final String DICTIONARY_TABLE_NAME = "dictionary". try { FileOutputStream out = openFileOutputStream("fichier". on peut ajouter | MODE_APPEND pour ajouter des données.. A noter: les fichier sont à éviter. } catch (FileNotFoundException e) { . Développement sous Android . En utilisant ces constantes comme un masque.openRawResource(R. InputStream is = res.Démonstration Ensi de Bourges ..

-F. Voici un exemple simple: String s = new String("<plop><blup attr=\"45\">Coucou !</blup></plop>"). Toast. Cependant. Développement sous Android . String orderBy. InputStream f = new ByteArrayInputStream(s. parser. Sur cet objet. String limit) L'objet de type Cursor permet de traiter la réponse (en lecture ou écriture).getName(). DATABASE_NAME.J. try { // auto-detect the encoding from the stream parser. Toast.LENGTH_LONG). par exemple: • getCount(): nombre de lignes de la réponse • moveToFirst(): déplace le curseur de réponse à la première ligne • getInt(int columnIndex): retourne la valeur (int) de la colonne passée en paramètre • getString(int columnIndex): retourne la valeur (String) de la colonne passée en paramètre • moveToNext(): avance à la ligne suivante • getColumnName(int): donne le nom de la colonne désignée par l'index • .newPullParser(). } @Override public void onCreate(SQLiteDatabase db) { db. null. String table. Toast.show(). une librairie équivalente est disponible: XmlPullParser. Document parser. } } Lecture / Ecriture dans la BDD Pour réaliser des écritures ou lectures..LENGTH_LONG).. Android fournit plusieurs parsers XML (Pull parser.getName().execSQL(DICTIONARY_TABLE_CREATE). SAX et DOM sont disponibles. Push parser). on utilise les méthodes getWritableDatabase() et getReadableDatabase() qui renvoient une instance de SQLiteDatabase. une requête peut être exécutée au travers de la méthode query(): public Cursor query (boolean distinct. String having. null).next().getBytes()).makeText(this.makeText(this. 5. String selection.5 XML [XML] Sans surprise. String groupBy. L'API de streaming (StAX) n'est pas disponible (mais pourrait le devenir).show(). Toast.next(). parser. String[] selectionArgs.Filière STI DictionaryOpenHelper(Context context) { super(context. parser.setInput(f. Lalande 32 / 79 . parser. DATABASE_VERSION). String[] columns. XmlPullParser parser = Xml.Lecture / Ecriture dans la BDD Ensi de Bourges .

getXMLReader().newInstance(). parser.show(). SAXParser sp = spf.SAX parser Ensi de Bourges . if (localName.item(i). Toast.getElementsByTagName("monitor").parse(source). String qName. for (int i=0. SAX parser SAX s'utilise très classiquement. DOM parser Enfin.i<items. Toast.-F.J.getDocumentElement().getAttributeValue(null. "attr"). le paser DOM permet de naviguer assez facilement dans la représentation arborescente du document XML.getValue("displayname")).getEntity(). SAXParserFactory spf = SAXParserFactory.parse(source). Document dom = builder.newSAXParser(). parser. XMLReader xr = sp. try { DocumentBuilder builder = factory. comme en Java SE.getLength(). xr. Développement sous Android .add(attributes. InputSource source = new InputSource(stream). L'exemple suivant permet de chercher tous les tags "monitor" dans les sous-tags de la racine. // Récupère tous les tags du descendant de la racine s'appelant monitor NodeList items = root. HttpGet httpget = new HttpGet(u). DefaultHttpClient httpclient = new DefaultHttpClient(). Attributes attributes) throws SAXException { Vector monitors = new Vector().com/api/xml/list?apikey=845ef"). Toast. }} }. } .equals("monitor")) { monitors.show(). String localName. DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(). DefaultHandler handler = new DefaultHandler() { @Override public void startElement(String uri.makeText(this.LENGTH_LONG).newDocumentBuilder(). InputStream stream = entity.Filière STI Toast. HttpEntity entity = response. Lalande 33 / 79 . xr.execute(httpget).LENGTH_LONG).makeText(this. HttpResponse response = httpclient. parser.getContent().setContentHandler(handler). Element root = dom..i++){ Node item = items.getText(). Voici un exemple attaquant du XML au travers d'une connexion http: URI u = new URI("https://www.site..nextText().

SAX parser Ensi de Bourges .getNodeName() // item.getTextContent() // item.getAttributes() .J..Filière STI // Traitement: // item...) Développement sous Android . Lalande 34 / 79 . } } catch (.-F..

}} Le service se déclare dans le Manifest dans le tag application: <service android:name=". La nouvelle méthode qu'il faut implémenter ici est onBind qui permet aux IPC de faire des appels à des méthodes distantes. processus et threads La structure d'une classe de service ressemble à une activité. } Développement sous Android . Lalande 35 / 79 .setOnClickListener(new OnClickListener() { public void onClick(View v) { Intent startService = new Intent("andro. Un bouton de démarrage d'un service local est simple à coder: Button b = (Button) findViewById(R.jf. on hérite de la classe Service et on implémente les méthodes de création/démarrage/arrêt du service. On dit alors que le service est local à l'application: il va même s'exécuter dans le thread de l'application. b.6 Les services. int startId) { // Démarrage du service } public int onStartCommand(Intent intent.-F.MyService"/> 6. startService(startService). public class MyService extends Service { public void onCreate() { // Création du service } public void onDestroy() { // Destruction du service } @deprecated // utile pour les versions antérieures d'Android 2.class)). int startId) { // Démarrage du service return START_STICKY. Ces méthodes sont à invoquer dans l'activité de l'application développée.J.manageServiceAction"). stopService(new Intent(this. int flags. } public IBinder onBind(Intent arg0) { return null. AndroService. Pour réaliser un service.button1).class)). processus et threads Ensi de Bourges .jf.1 Démarrer / Arrêter un service Les Intents fournissent un moyen de démarrer/arrêter un service en utilisant le nom de la classe du service ou un identifiant d'action: startService(new Intent(this.Filière STI 6 Les services. AndroService.id. startService(new Intent("andro.0 public void onStart(Intent intent.manageServiceAction")).

• START_NOT_STICKY: le service n'est pas redémarré en cas de kill du système. Intent intent) { Intent startServiceIntent = new Intent(context. C'est utile lorsque le service réagit à l'envoi d'un Intent unique.class). La méthode onStartCommand() est très similaire à onStart() mais cette méthode renvoie un entier qui permet d'indiquer au système ce qu'il faut faire au moment de la ré-instanciation du service. il est ensuite redémarré. le service démarré exécute onCreate() puis onStart() qui va par exemple lancer un Thread pour réaliser la tâche de fond. si celui-ci a dû être arrété.J. seule la méthoe onCreate() est appelée [API-service]. Pour cela.intent.AutoStart"> <intent-filter> <action android:name="android. context. par exemple une alarme qui envoie un Intent toutes les 15 minutes. ce qui permet au service qu'il vient d'être démarré après un kill du système.AndroService"></service> <receiver android:name=". Typiquement. une nouvelle méthode a fait son apparition dans les versions ultérieures à la version 5 de l'API. Cependant.-F. puis est récréé. le système prend soin d'appeler à nouveau la méthode onStartCommand(Intent intent) avec un intent null. Auto démarrage d'un service Il est aussi possible de faire démarrer un service au démarrage du système. } } onStartCommand() Un problème subsiste après que l'application ait démarré le service. Afin de résoudre le problème précédent. Si le service est tué. La méthode peut renvoyer (cf [API-service]): • START_STICKY: le comportement est similaire aux API<5. AndroService.Auto démarrage d'un service Ensi de Bourges . il faut créer un BroadcastReceiver qui va réagir à l'action BOOT_COMPLETED et lancer l'Intent au travers de la méthode startService.action. Le Manifest contient donc: <application android:icon="@drawable/icon" android:label="@string/app_name"> <service android:name=".Filière STI }).startService(startServiceIntent). Si le service doit être détruit par la plate-forme (pour récupérer de la mémoire). Lalande 36 / 79 . • START_REDELIVER_INTENT: similaire à START_NOT_STICKY avec en plus le rejoue d'un Intent si le service n'a pas pu finir de le traiter et d'appeler stopSelf(). Développement sous Android .BOOT_COMPLETED" /> </intent-filter> </receiver> </application> Et la classe AutoStart doit être: public class AutoStart extends BroadcastReceiver { public void onReceive(Context context.

par exemple si l'on souhaite faire: boolean blop = true. } }.this. Attention à bien détruire la tâche programmée lorsqu'on détruit le service ! (sinon. on peut utiliser le flag START_STICKY_COMPATIBILITY. } }). final Handler handler = new Handler(). S'il s'agit de faire une tâche répétitive qui ne consomme que très peu de temps CPU à interfals régulier.Thread d'exécution Ensi de Bourges . comme évoqué plus loin. while (blop == true) . suffit. task = new TimerTask() { public void run() { handler. On obtient: Tâche régulière Une façon de résoudre le problème précédent est de séparer le processus de l'application et celui du service.schedule(task.makeText(AndroService.show(). Toast. un simple appel à new TimerTask() { public void run() { code. Lalande 37 / 79 .-F. une solution ne nécessitant pas de processus à part consiste à programmer une tâche répétitive à l'aide d'un TimerTask. 5000). le thread associé à la tâche programmé rest actif).Filière STI Dans les versions ultérieures à l'API 5. Développement sous Android . la commande onStart() se comporte comment onStartCommand() avec un ré-appel de la méthode avec un intent null.LENGTH_SHORT). Cela peut être particulièrement ennuyeux si les traitements à effectuer sont couteux. }}. Pour conserver le comportement obsolète de l'API (pas d'appel à onStart()). La création du TimerTask est particulièrement tarabiscotée mais il s'agissait de résoudre le problème du lancement du Toast. Thread d'exécution Le service invoqué par l'Intent de l'application s'exécute dans le thread de l'application.post(new Runnable() { public void run() { Toast. Pour des cas sans toast. 0. "plop !". timer.J.

cancel(). les deux services sont stoppés. Enfin. } Démonstration Dans cette démonstration.J. deux services s'exécutent en même temps. // La donnée à transmettre à l'activité private class MonServiceBinder extends Binder implements AndroServiceInterface { // Cette classe qui hérite de Binder implémente une méthode // définie dans l'interface AndroServiceInterface public int getInfo() { return infoOfService. En cliquant une seconde fois sur le bouton start. La définition de l'interface permettra à l'outil aidl de générer les stubs de communication inter processus permettant de lier une activité et un service. l'application aura accès au service. L'activité peut être en charge de (re)démarrer le service. } } Développement sous Android . Lier activité et service: IBinder [BAS] Pour associer un service à une activité. } et on crée une nouvelle classe MyServiceBinder. etc. de préférence classe interne à l'activité. Au travers de l'objet IBinder. qui renvoie la classe associée au service: private int infoOfService = 0. Lalande 38 / 79 . Cependant. le service local à l'application est lancé: il affiche toutes les 5 secondes un toast à l'aide d'un TimerTask. Pour deux processus séparés.Filière STI public void onDestroy() { // Destruction du service timer. Il est visible dans la section running services.-F. On implémente donc dans le service: private final IBinder ib = new MonServiceBinder(). Trois possibilités sont offertes pour faire coopérer service et activité frontend: • utiliser des Intents pour envoyer des informations • lier service et activité d'un même processus • définir une interface de service en utilisant le langage AIDL Lier le service et l'activité peut se faire assez simplement en définissant une interface (le contrat de communication entre le service et l'activité) et en utilisant la classe Binder.Démonstration Ensi de Bourges . il faut passer par la définition de l'interface en utilisant le langage AIDL. public IBinder onBind(Intent intent) { return ib.2 Faire coopérer service et activité Un service est souvent paramétré par une activité. le service doit implémenter la méthode onBind() qui renverra à l'application un objet de type IBinder. d'afficher les résultats du service. (cf ANX_Demarrage-de-services) 6. cela n'est réalisable que si le service et l'activité font partie du même processus.

un objet de la classe ServiceConnexion doit être instancié à partir de l'application.makeText(getApplicationContext().-F. à définir. unbindService(maConnexion). Toast. une autre application android pourra appeler le service au travers d'IPC. Il s'agit de définir ce que les service est capable de faire et donc. la définition de l'interface permettra à l'outil AIDL de générer des stubs de communication pour les IPC. l'activité se connecte au service pour toaster le résultat.J.class).show(). on initie la connexion: Intent intentAssociation = new Intent(AndroServiceBindActivity.getInfo(). dans le code de l'activité. private ServiceConnection maConnexion = new ServiceConnection() { public void onServiceConnected(ComponentName name. Toast. déclare les méthodes qui seront accessibles à l'application: public interface AndroServiceInterface { public int getInfo(). Puis.LENGTH_SHORT). Context. quelles méthodes peuvent être appelées. par exemple un pointeur d'attribut de classe sur l'objet Binder. Un peu comme un web service. (cf ANX_Binding-entre-service-et-activite-pour-un-meme-processus) Service au travers d'IPC AIDL Le langage AIDL est très proche de la définition d'interfaces Java EE (mais pas tout à fait identique). Deux méthodes de cet objet doivent être surchargées: • onServiceConnected qui sera appelé lorsque le service sera connecté à l'application et qui permettra de récupérer un pointeur sur le binder.Lier activité et service: ServiceConnexion Ensi de Bourges . IBinder service) { AndroServiceInterface myBinder = (AndroServiceInterface)service. bindService(intentAssociation. "Info lue dans le service: " + infoFromService. } Lier activité et service: ServiceConnexion Pour lier l'activité au service.this. Ainsi. un service lance un thread qui attendra 4 secondes avant d'enregistrer la valeur 12 comme résultat du service. private int infoFromService = 0.BIND_AUTO_CREATE). infoFromService = myBinder. A chaque click du bouton.Filière STI L'interface. AndroService. } public void onServiceDisconnected(ComponentName name) { } }. C'est cet objet qui fera le lien entre l'activité et le service. Lalande 39 / 79 . maConnexion. Démonstration Dans cette démonstration. • onServiceDisconnected qui permet de nettoyer les choses créées à la connexion. Une interface en AIDL peut par exemple ressembler à: Développement sous Android .

public interface AndroServiceInterface extends android. DO NOT MODIFY.3 Processus Développement sous Android . on utilise alors l'implémentation générée par aidl pour implémenter ce que fait le service dans la méthode déclarée par l'interface: private int infoOfService = 0. Service exposant l'interface Dans le service.J.jf. 6.Service exposant l'interface Ensi de Bourges . il faut aussi utiliser le code de la stub pour récupérer l'objet implémentant l'interface (cf ANX_Binding-inter-processus-utilisant-le-langage-AIDL): private ServiceConnection maConnexion = new ServiceConnection() { public void onServiceConnected(ComponentName name.AndroServiceInterface". Processus). public IBinder onBind(Intent arg0) { return ib.Filière STI package andro.String DESCRIPTOR = "andro.Binder implements andro..asInterface(service).AndroServiceInterface { private static final java.Stub ib = new AndroServiceInterface.jf. package andro.Stub() { @Override public int getInfo() throws RemoteException { return infoOfService. } }.e. } Il devient alors possible de déclarer le service comme appartenant à un autre processus que celui de l'activité. } L'outil aidl va générer les stubs correspondants à l'interface dans un fichier . try { // stockage de l'information provevant du service infoFromService = myBinder.java portant le même nom que le fichier aidl.-F. dans le répertoire gen.jf.os. // La donnée à transmettre à l'activité private AndroServiceInterface.jf.os. // This file is auto-generated. . interface AndroServiceInterface { int getInfo().IInterface { public static abstract class Stub extends android. android:process=":p2" (cf.. Lalande 40 / 79 .lang. IBinder service) { AndroServiceInterface myBinder = AndroServiceInterface.getInfo(). } Du côté de l'application.Stub. i.

Processus tâche de fond (activité non visible (onStop() a été appelé) 5. Bien que cela soit inutile. <service>. on peut le préciser dans le Manifest à l'aide de l'attribut android:process. Ainsi.-F. L'avantage est double: l'interface graphique pourra s'afficher correctement et si le service plante. L'interface graphique s'exécute elle aussi au travers de ce processus principal. par exemple: <application android:process="andro. Par exemple. Il est possible de séparer les composants d'une application en plusieurs processus.AndroService" android:process=":p2"> Pour cet exemple. <receiver>. le processus dit "principal". Processus visible: il n'interagit pas avec l'utilisateur mais peut influer sur ce que l'on voit à l'écran (activité ayant affiché une boite de dialogue (onPause() a été appelé). 6.Filière STI [PT] Par défaut. une application android s'exécute dans un processus unique. Ces composants sont les codes attachés aux tags <activity>. Démonstration Dans cette démonstration. par exemple: <service android:name=". une hiérarchie d'importance permet de classer le niveau d'importance des processus: 1. On garantit ainsi d'avoir le niveau 3 pour cette opération.jf"> Les sous-tags du manifest.J. Ainsi. le processus affecté à une activité qui n'est plus visible a de grandes chances d'être détruite. Processus de service 4. plus la gestion de la programmation devient délicate car on peut arriver très rapidemment à des problèmes de blocage de l'interface graphique par exemple. Si l'on souhaite créer un nouveau process indépendant. cela n'impactera pas le processus principal. le service et l'application sont indépendants. service attaché à cette activité. l'application (tag <application>) s'exécute dans le processus principal portant le même nom que le package de l'application.Démonstration Ensi de Bourges . s'exécute dans un processus indépendant de l'interface principale. Vie des processus Android peut devoir arrêter un processus à cause d'autres processus qui requiert plus d'importance. ce qui n'affectera pas l'application et son interface (cf ANX_Processus-indépendants). plus une application réalisera de traitement. le service. Android va tuer le service qui occupe trop de temps processeur. 3. Lalande 41 / 79 . BroadCastReceiver exécutant onReceive()) 2. Par défaut. on peut utiliser l'attribut android:process en préfixant le nom du processus par ":". service lié à ces activité "visibles"). Processus en avant plan (activité en intéraction utilisateur. gardés pour des raisons de cache) Ainsi. qui comporte une boucle infinie.4 Threads Développement sous Android . Plus classiquement. même si l'utilisteur quitte l'application ayant initié l'upload. il faut préférer l'utilisation d'un service à la création d'un thread pour accomplir une tâche longue. un service peut être détruit car une application gourmande en ressources (navigateur web) occupe de plus en plus de mémoire. et <provider> de l'application. c'est-à-dire les composants de l'applications héritent de cet attribut et s'exécutent donc dans le même process. Processus vides (ne comporte plus de composants actifs. par exemple l'upload d'une image.

start(). } }).Filière STI Dans le processus principal.post(new Runnable() { public void run() { mImageView. Il est. un appel à la méthode View. Cependant. Ainsi.-F. ce qui se résume dans la documentation sur les threads par deux règles: • Do not block the UI thread • Do not access the Android UI toolkit from outside the UI thread L'exemple à ne pas faire est donné dans la documentation et recopié ci-dessous. responsable de l'interface graphique et des messages/notifications/evénements entre composants graphiques. pour l'exemple précédent: public void onClick(View v) { new Thread(new Runnable() { public void run() { final Bitmap bitmap = loadImageFromNetwork("http://example. entre autre. long) 6. } Threads et interface graphique Android fournit des méthodes pour résoudre le probème précédemment évoqué.setImageBitmap(b). } La documentation donne les quelques méthodes utiles pour ces cas délicats: • Activity.post(Runnable) permet de réaliser cela et donne. Il s'agit de créer des objets exécutables dont la partie affectant l'interface graphique n'est pas exécutée mais déléguée à l'UI thread pour exécution ultérieure. il est interdit d'effectuer des opérations sur l'interface graphique en dehors du thread principal (aussi appelé UI thread). public void onClick(View v) { // DO NOT DO THIS ! new Thread(new Runnable() { public void run() { Bitmap b = loadImageFromNetwork("http://example. mImageView.J.com/image.setImageBitmap(bitmap).com/image. si l'application doit effectuer des traitements longs. Par exemple. mImageView.start(). Par exemple. le système créé un thread d'exécution pour l'application: le thread principal.png"). Lalande 42 / 79 . elle doit éviter de les faire dans ce thread.5 Bilan: services.post(Runnable) • View.postDelayed(Runnable. } }). l'événement générant l'exécution de onKeyDown() s'exécute dans ce thread. processus et threads Développement sous Android .runOnUiThread(Runnable) • View. Le comportement est imprévisible car le toolkit graphique n'est pas thread-safe.png").Threads et interface graphique Ensi de Bourges . } }).

Ravichandran> I have a need to create a background service that starts up during the system boot up. Having your application do this is going to going to take resources from other things that at any particular point in time would be better used elsewhere. We have lots of facilities for implementing applications so they don't need to do this. tel que l'upload d'un post google+. Having a service run forever is pretty close to the side of evil.stay running all of the time. Les services comme démons ? Je ne résiste pas au plaisir de vous citer une discussion du googlegroup android-developer [LSAS]. you can be guaranteed that your service will -not. dans l'UI thread. dans un tel cas. Please please please use them if at all possible. est-il vraiment nécessaire de réaliser un service ? Un thread aurait suffit. but realize -On current Android devices. Dianne Hackborn> Mark answered how to do this. fine. And in fact. this what you want to do. Cependant. we can keep only a small handful of applications running at the same time. un service s'exécute donc dans le même processus de l'application. such as alarms. un service ayant un processus indépendant est approprié. Il faut alors utiliser les mécanismes prévus dans Android pour faire coopérer ces deux entités à l'aide de l'objet IBinder ou d'IPC AIDL. Développement sous Android . sauf s'il s'agit d'un service très court dont le but est de libérer l'interface graphique. Garder le service dans le processus de l'application en le changeant de thread apparait peut censé. and keeps running until the device is powered down. La difficulté de la séparation du service et de l'application réside dans la difficulté de l'interaction de ces deux composants. survivant à la fermeture de l'application principale. Lalande 43 / 79 . or needs more memory for what the user is actually doing (running the web browser on complicated web pages is a great way to kick background stuff out of memory). and various broadcasts from events going on in the system. because there is so much other stuff that wants to run (other background services that are only running when needed will be prioritized over yours). à propos des services persistants. And think once more. ressemblant à des démons systèmes: R. And if you are really really absolutely positively sure.Filière STI Par défaut.-F. but please: think again about whether you really need to do this.J. Pour une tâche longue. There is no UI or Activity associated with this. Then think another time.Les services comme démons ? Ensi de Bourges .

permission. il est nécessaire de disposer de la permission android. Pour lire ces informations.. String numero) { super.1 Téléphonie Les fonctions de téléphonie sont relativement simples à utiliser.CALL_STATE_IDLE) // RAS if (etat == TelephonyManager. Lalande 44 / 79 . if (etat == TelephonyManager.TELEPHONY_SERVICE).1 Téléphonie Passer ou filtrer un appel Envoyer et recevoir des SMS 7. d'être notifié lors d'un changement d'état. int etat = tel.Filière STI 7 Connectivité 7.3 Bluetooth S'associer en bluetooth Utiliser le réseau 7. ..4 Localisation Coordonnées Alerte de proximité 7.getSimSerialNumber().5 Capteurs Lecture des données 44 44 45 46 46 46 47 47 48 48 49 49 49 7. TelephonyManager tel = (TelephonyManager)getSystemService(Context. et l'état du téléphone. numero) if (etat == TelephonyManager.2 Réseau Gérer le réseau Wifi/Mobile 7.getCallState().CALL_STATE_RINGING) // Le téléphone sonne String SIMnb = tel. du téléphone.CALL_PHONE (cf Contrôler les actions permises).CALL_STATE_OFFHOOK) // Le téléphone est utilisé } Passer ou filtrer un appel Développement sous Android .J.onCallStateChanaged(etat. L'état de la téléphonie est géré par la classe TelephonyManager qui permet de récupérer le nom de l'opérateur. de passer des appels et de gérer l'envoi et réception de SMS. appel entrant. Elles permettent de récupérer l'état de la fonction de téléphonie (appel en cours. Il est aussi possible d'être notifié d'un changement d'état en utilisant un écouteur: public class Ecouteur extends PhoneStateListener { public void onCallStateChanged(int etat.-F.7 Connectivité Ensi de Bourges .).

provider.SMS_RECEIVED: <receiver android:name=".get("pdus").ACTION_DIAL.permission.Telephony. Intent call = new Intent(Intent.getString("extra").Envoyer et recevoir des SMS Ensi de Bourges . ces deux actions étant réalisées avec un Intent (attention aux permissions): Uri telnumber = Uri.getAction().provider.provider. le début du code du broadcast receiver est par exemple: public final class MyBroadcastReceiver extends BroadcastReceiver { public void onReceive(Context context.length.parse("tel:0248484000"). startActivity(call). Object[] pdus = (Object[]) intent. telnumber).ACTION_CALL.Telephony. (TODO: à revoir) Pour filtrer l'appel.SMS_RECEIVED"/> </intent-filter> </receiver> Puis.sendTextMessage("02484840000".parse("tel:0248484000"). null). null.SMSBroadcastReceiver"> <intent-filter> <action android:name="android.Filière STI Il est bien sûr possible de passer un appel ou de déléguer l'appel.ACTION_CALL"/> </intent-filter> </receiver> Envoyer et recevoir des SMS Si la permission android. for (int i=0. Intent intent) { if (intent. il est possible d'envoyer des SMS au travers de SmsManager: SmsManager manager = SmsManager. manager. Lalande 45 / 79 .SMS_RECEIVED)) { String val = extra. telnumber).Telephony.createFromPdu((byte[]) pdus[i]).getDeault().-F.J. i++) messages[i] = SmsMessage. Inversement.length].getExtras(). il faut poser un intent filter: <receiver android:name=". SmsMessage[] messages = new SmsMessage[pdus. "Coucou !". L'action à préciser dans le filtre d'Intent du receveur est android.SEND_SMS est disponible.equals(android. Intent call = new Intent(Intent. i < pdus. dans le but de loguer une information ou gérer l'appel. il est possible de créer un filtre d'Intent pour recevoir un SMS qui sera géré par un broadcast receiver. Uri telnumber = Uri. }}} Développement sous Android . null.ClasseGerantLAppel"> <intent-filter> <action android:name="Intent. startActivity(call).

L'accès au réseau wifi est gérable depuis une application..permission. Si la permission android. SUSPENDED. DISCONNECTED.Filière STI 7. Lalande 46 / 79 .getScanResults() 7.2 Réseau (cf ANX_Réseau) Le réseau peut être disponible ou indisponible. Les caractéristiques de la connexion Wifi sont accessibles par des appels statiques à des méthodes de WifiManager: • force du signal projeté sur une échelle [0.getType().TYPE_MOBILE: connexion mobile • ConnectivityManager. On utilie alors la méthode setNetworkPreference sur l'objet ConnectivityManager pour lui donner l'entier correspondant au type de connexion voulu. L'objet WifiManager permet de réaliser cela. DISCONNECTING. 3G. if (net. bluetooth.7. au wifi ou à une connexion de type mobile (GPRS.TYPE_WIFI: wifi Gérer le réseau Wifi/Mobile Le basculement entre les types de connexion est possible si la permission WRITE_SECURE_SETTINGS est disponible. levels) • vitesse du lien réseau: info. • ConnectivityManager.2 Réseau Ensi de Bourges . etc. Le type est un entier correspondant. CONNECTED. suivant que le téléphone utilise une connexion Wifi. cette classe permet de gérer les autres appareils bluetooth et d'initier les communications avec ceux-ci. la classe NetworkInfo (depuis ConnectivityManager) permet de lire l'état de la connexion réseau parmis les constantes de la classe State: CONNECTING. if (!wifi.WIFI_SERVICE). UNKNOWN. .). ce qui permet d'allumer ou de couper le wifi.getActiveNetworkInfo().calculateSignalLelvel(RSSI ?. Développement sous Android .TYPE_WIFI). NetworkInfo net = manager.getLinkSpeed() • les points d'accès disponibles: List<ScanResult> pa = manager.compareTo(State. pour l'instant. Par exemple: manager. WifiManager wifi = (WifiManager)getSystemService(Context.-F.3 Bluetooth Le bluetooth se gère au travers de principalement 3 classes: • BluetoothAdapter: similaire au WifiManager.J.CONNECTED) // Connecté Il est possible de connaître le type de la connexion: int type = net..ACCESS_NETWORK_STATE est déclarée.setNetworkPreference(ConnectivityManager. ConnectivityManager manager = (ConnectivityManager)getSystemService(CONNECTIVITY_SERVICE).setWifiEnabled(true). 3G.levels]: WifiManager.isWifiEnabled()) wifi.getState().

startActivity(launchBluetooth). • BluetoothSocket et BluetoothServerSocket: gère une connexion établie.putExtra(BluetoothAdapter. Utiliser le réseau De nombreuses méthodes de développement permettent d'exploiter le réseau.ACTION_REQUEST_ENABLE). BluetoothDevice appareil = null. if (!bluetooth. SoapSerializationEnvelope • REST: • JSON: JSONObject • XML: DocumentBuilder Développement sous Android .getDefaultAdapter(). 60).-F.J. il faut que l'un d'eux soit accessible (s'annonce) aux autres appareils.makeText(getApplicationContext(). si un appareil externe diffuse une annonce de découverte.ACTION_FOUND)) appareil = (BluetoothDevice)intent. for (BluetoothDevice ap : s) Toast. BluetoothAdapter bluetooth = BluetoothAdapter. il faut activer les persmissions android.getName(). A l'inverse. Pour pouvoir utiliser les fonctionnalités bluetooth.isEnabled()) { Intent launchBluetooth = new Intent(BluetoothAdapter. L'application doit donc le demander explicitement via un Intent: Intent discoveryMode = new Intent(BluetoothAdapter. "" + ap. Pour cela.BLUETOOTH et android.EXTRA_DISCOVERABLE_DURATION.getParcelableExtra(BluetoothDevice. l'utilisateur doit autorisé le mode "découverte".EXTRA_DEVICE).show(). Intent intent) { String action = intent. discoveryMode. Lalande 47 / 79 .LENGTH_LONG). Toast.getAction(). il faut capturer les intents recus en broadcast dans le mobile: public final class BluetoothBroadcastReceiver extends BroadcastReceiver { public void onReceive(Context context. Elles ne sont pas rapellées en détail ici (ce n'est pas l'objet du cours) et elles sont de toutes façons déjà connues: • HTTP: HttpClient.ACTION_REQUEST_DISCOVERABLE).getBondedDevices(). } S'associer en bluetooth Pour pouvoir associer deux appareils en bluetooth. if (action. } } Enfin les appareils associés se parcourent à l'aide d'un Set<BluetoothDevice>: Set<BluetoothDevice> s = bluetooth.permission.BLUETOOTH_ADMIN pour pouvoir chercher des appareils ou changer la configuration bluetooth du téléphone.S'associer en bluetooth Ensi de Bourges . startActivity(discoveryMode).equals(BluetoothDevice.Filière STI • BluetoothDevice: objet représentant l'appareil distant. httpResponse • SOAP: SoapObjet.permission.

Toast.LENGTH_SHORT). "Lattitude" + localisation.7. Android permet d'utiliser plusieurs moyens de localisation.ACCESS_FINE_LOCATION via le GPS • android.LENGTH_SHORT). new LocationListener() { public void onStatusChanged(String provider.show(). } Les permissions associées pour la localisation sont: • android. ServerSocket • Bluetooth: BluetoothSocket. if (f.makeText(getApplicationContext().makeText(getApplicationContext().requestLocationUpdates("gps". La classe LocationManger permet de gérer ces différents fournisseurs de position. Toast. Toast. 100.LENGTH_SHORT).permission. Toast. Il est possible de réagir à un changement de position en créant un écouteur qui sera appelé à intervals réguliers et pour une distante minimum donnée: manager.getAllProviders(). "" + f. "Longitude" + localisation.J. Location localisation = manager.getLastKnownLocation("gps"). il est possible d'interroger la dernière localisation en utilisant l'objet Location.NETWORK_PROVIDER: fournisseur basé réseau La liste de tous les fournisseurs s'obtient au travers de la méthode getAllProviders() ou getAllProviders(true) pour les fournisseurs activés: LocationManager manager = (LocationManager)getSystemService(Context. List<String> fournisseurs = manager. BluetoothServerSocket 7.show().4 Localisation (cf ANX_Localisation) Comme pour le réseau.permission. 6000. int status.GPS_PROVIDER: fournisseur GPS • LocationManager. for (String f : fournisseurs) { Toast.(LocationManager.ACCESS_COARSE_LOCATION via le réseau Coordonnées A partir du nom d'un fournisseur de position actif.getLongitude().show().getLatitude(). • LocationManager. Bundle extras) { } public void onProviderEnabled(String provider) { } public void onProviderDisabled(String provider) { } public void onLocationChanged(Location location) { // TODO Auto-generated method stub } }).GPS_PROVIDER)) .makeText(getApplicationContext(). Cela permet de rendre transparent l'utilisation du GPS ou des antennes GSM ou des accès au Wifi. Développement sous Android .-F.Filière STI • Sockets: Socket..LOCATION_SERVICE).4 Localisation Ensi de Bourges . Toast. Lalande 48 / 79 ..equals.

unregisterListener( pointeur ecouteur . La signature de cette méthode est: addProximityAlert(double latitude. Cela va du plus rapide possible pour le système à une fréquence faible: SENSOR_DELAY_FASTEST < SENSOR_DELAY_GAME < SENSOR_DELAY_NORMAL < SENSOR_DELAY_UI. manager. des capteurs présents.getDefaultSensor(Sensor. Boolean entering = intent.TYPE_ACCELEROMETER) . En fonction. Cette alerte possède une durée d'expiration qui la désactive automatiquement. }} 7.5 Capteurs (cf ANX_Capteurs) Android introduit la gestion de multiples capteurs.getDefaultSensor(Sensor. } Quand l'acceléromètre n'est plus utilisé. du gyroscope (position angulaire). manager. Lalande 49 / 79 . la classe SensorManager permet d'accéder aux capteurs disponibles. Lecture des données Développement sous Android . La fréquence d'interrogation influe directement sur l'énergie consommée. de la luminosité ambiante. pour l'acceléromètre: SensorManager manager = (SensorManager)getSystemService(SENSOR_SERVICE).registerListener( new SensorEventListener() { public void onSensorChanged(SensorEvent event) { // TODO Auto-generated method stub } public void onAccuracyChanged(Sensor sensor. La classe gérant l'alerte est alors: public class MyProximityAlertReceiver extends BroadcastReiceiver { public void onReceive(Context context. de la pression ou température. Par exemple.Filière STI Alerte de proximité Il est possible de préparer un évenement en vu de réagir à la proximité du téléphone à une zone. des champs magnétiques. double longitude. registerReceiver(new MyProximityAlertReceiver(). il faut utiliser la méthode addProximityAlert de LocationManager qui permet d'enregistrer un Intent qui sera envoyé lorsque des conditions de localisation sont réunies. long expiration. PendingIntent intent) Il faut ensuite filtrer l'intent préparé: IntentFilter filtre = new IntentFilter(PROXIMITY_ALERT).Alerte de proximité Ensi de Bourges . il doit être désenregistré à l'aide de: manager.TYPE_ACCELEROMETER) ). int accuracy) { // TODO Auto-generated method stub }} .getBooleanExtra(key.J. Il peut s'agir de l'acceléromètre. Pour cela. manager.KEY_PROXIMITY_ENTERING. SensorManager.-F. false). float radius. filtre).SENSOR_DELAY_UI ). etc. Intent intent) { String key = LocationManager.

roll.-F. on se limite à une seule valeur: float lumiere. z = event. lumiere = event.getType() == Sensor. Lalande 50 / 79 . pitch = event.TYPE_ACCELEROMETER) { float x.values[1].Filière STI Les valeurs sont récupérées au travers de la classe SensorEvent.values[2]. y = event. Toutes les données sont dans un tableau.values[0].values[0].sensor. dont la taille dépend du type de capteur utilisé. x = event. Développement sous Android .z. Par exemple pour l'acceléromètre: if (event. } Pour le capteur de lumière.values[0].Alerte de proximité Ensi de Bourges . azimuth = event. pitch.values[2].J.values[1].y. roll = event. } Le code est similaire pour le capteur d'orientation: float azimuth.

J.x. à travers une connexion réseau.x. le SDK fournit un moyen de visualiser les logs du système et de les filtrer au travers de l'outils adb situé dans le répertoire platform-tools du SDK.x.jar) • Ajoutez bin/sensorsimulator-lib-x.err( W/System. tester la connexion entre l'outil externe et l'outil de configuration interne de Sensor Simulator.1 Debugging Les commandes classiques System.BlockGuard$WrappedFileSystem.apk à votre device virtuel (utiliser la commande adb install SensorSimulatorSettings-x.xml (No such file or directory) 483): at org. On obtient par exemple pour une exception: .err( W/System.err( W/System.w("myApp".FileNotFoundException: /test. 8.OSFileSystem.io.v (verbose). les appels à la lecture des senseurs appelera la librairie du simulateur qui.-F. Pour réaliser cela il faut: • Téléchargez Sensor Simulator • Lancez l'outil bin/sensorsimulator-x. .apk) Vous pouvez.err( 483): debugger has settled (1441) 483): java.jar (java -jar bin/sensorsimulator-x.platform.java:132) 483): at andro.<init>(FileInputStream.system. L'outil permet de se connecter à l'émulateur et d'obtenir les messages de logs.1 Debugging 8.e (error).x.io.FileInputStream. pour commencer. Vous pouvez ensuite faire bouger le device virtuel dans le client java (représentation du téléphone en haut à gauche).Filière STI 8 Divers 8. "Mon message à logguer").w (warning). Il faut donc débugguer autrement.out( W/System.java:80) 483): at java. Lalande 51 / 79 .onCreate(Andro7x24Activity. Adaptation de votre application au simulateur Le principe du simulateur est de remplacer la déclaration de votre SensorManager par celui du simulateur dans le code de votre application.x.8 Divers Ensi de Bourges . .AndroJFLActivity.open(Native Method) 483): at dalvik. etc.java:38) Il est aussi possible de générer des logs à l'aide de la classe Log est des méthodes statiques .jar à votre projet Eclipse • Ajoutez bin/SensorSimulatorSettings-x.<init>(FileInputStream.err( W/System.xxx sont redirigées vers /dev/null.x.x.harmony.open(BlockGuard.d (debug). Remplacez votre déclaration ainsi: Développement sous Android .io. lancez Sensor Simulator et après avoir vérifié la concordance entre les adresses IP.FileInputStream.x.x. Par exemple: Log. vous pouvez utiliser l'outil Sensor Simulator qui permet de simuler le changement de valeurs des senseurs à partir d'un client Java en dehors de l'émulateur./adb logcat I/System.x.2 Simuler des sensors Adaptation de votre application au simulateur 51 51 51 8.apache. ira lire les données dans le client externe Java. Dans l'émulateur.luni. Pour ce faire.err( W/System. .jfl.java:232) 483): at java. vous pouvez connecter les deux outils depuis l'onglet Testing. Ainsi.out.2 Simuler des sensors SS (cf ANX_Capteurs) Afin de simuler les sensors.

SENSOR_SERVICE).. Quelques adaptations du code peuvent être nécessaires.getSystemService(this.Filière STI // SensorManager manager = (SensorManager)getSystemService(SENSOR_SERVICE).-F. Développement sous Android . SensorManagerSimulator manager = SensorManagerSimulator. par défaut.sensorsimulator. Lalande 52 / 79 .8 Divers Ensi de Bourges .hardware.hardware et doivent désormais utiliser org.openintents. importent les classes de android. Ne pas oublier non plus d'ajouter la permission permettant à votre application d'accéder à internet.J.connectSimulator(). manager. Attention aux directives import qui..

setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast. b.5 Intents 9. texte.6 Receveur de broadcasts 9.setBackgroundColor(Color.J. "Stop !". } } Développement sous Android .show().4 Onglets 9.13 Localisation 9.id.9 Binding entre service et activite pour un meme processus 9.14 Capteurs 53 54 55 56 58 61 62 63 65 67 70 71 73 74 9.9 Annexes Ensi de Bourges .8 Demarrage de services 9.makeText(v. l.Button01).id. Toast. Toast.11 Processus indépendants 9.GRAY).1 Interfaces graphiques Main.makeText(this.id.12 Réseau 9. "SAUVEGARDE".10 Binding inter processus utilisant le langage AIDL 9.LENGTH_LONG).setText("Here we go !").-F.layout.acceuil).2 Listes pour des items texte 9.7 Gestion des préférences 9. // Changing layout LinearLayout l = (LinearLayout)findViewById(R. } }).onCreate(savedInstanceState).Filière STI 9 Annexes 9.java public class Main extends Activity { @Override protected void onSaveInstanceState(Bundle outState) { super. } public void onCreate(Bundle savedInstanceState) { setContentView(R. TextView texte = (TextView)findViewById(R. Button b = (Button)findViewById(R. Lalande 53 / 79 .3 Listes de layouts complexes 9.le_texte).accueilid). Toast. super.LENGTH_LONG).1 Interfaces graphiques 9.getContext().onSaveInstanceState(outState).show().

setText("Edit me").setOrientation(LinearLayout.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center" android:id="@+id/accueilid"> <TextView android:id="@+id/le_texte" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello" android:layout_gravity="center" /> <EditText android:text="" android:id="@+id/EditText01" android:layout_width="fill_parent" android:layout_height="wrap_content"></EditText> <EditText android:text="" android:layout_width="fill_parent" android:layout_height="wrap_content"></EditText> <ImageView android:id="@+id/logoEnsi" android:src="@drawable/ensi" android:layout_width="100px" android:layout_height="wrap_content" android:layout_gravity="center_horizontal"></ImageView> <Button android:text="Go !" android:id="@+id/Button01" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> </LinearLayout> Activity2. Lalande 54 / 79 .java public class Activity2 extends Activity { public void onCreate(Bundle savedInstanceState) { super. edit.drawable. // Zone de texte EditText edit = new EditText(this).addView(image).setGravity(Gravity.android.Filière STI Layout <?xml version="1. gabarit. setContentView(gabarit).2 Listes pour des items texte Développement sous Android .onCreate(savedInstanceState). LinearLayout gabarit = new LinearLayout(this).-F. texte.addView(texte). gabarit. image. // Label TextView texte = new TextView(this). } } 9.setText("Programming creation of interface !").setImageResource(R.J. gabarit. gabarit.VERTICAL).CENTER). // Image ImageView image = new ImageView(this).addView(edit). gabarit.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.ensi).Layout Ensi de Bourges .

onCreate(savedInstanceState). } list. R. ArrayAdapter<String> tableau = new ArrayAdapter<String>( list.montexte).java Ensi de Bourges .getContext().Filière STI AndroListsSimpleActivity.3 Listes de layouts complexes AndroListsActivity. i++) { tableau.setAdapter(tableau).layout.onCreate(savedInstanceState).android.-F.AndroListsSimpleActivity.layout. } } Layout <?xml version="1. Lalande 55 / 79 .java public class AndroListsActivity extends Activity { /** Called when the activity is first created.main).J.android.com/apk/res/android"></TextView> 9. for (int i=0.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <ListView android:layout_height="fill_parent" android:id="@+id/maliste" android:layout_width="fill_parent"></ListView> </LinearLayout> Layout d'une ligne de liste <?xml version="1. */ @Override public void onCreate(Bundle savedInstanceState) { super. i<40.id.id.java public class AndroListsSimpleActivity extends Activity { /** Called when the activity is first created.0" encoding="utf-8"?> <TextView android:text="TextView" android:id="@+id/montexte" android:layout_width="wrap_content" android:layout_height="wrap_content" xmlns:android="http://schemas. setContentView(R. ListView list = (ListView)findViewById(R.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.maliste).maliste). ListView list = (ListView)findViewById(R. setContentView(R.add("coucou " + i).main).layout. Développement sous Android . */ @Override public void onCreate(Bundle savedInstanceState) { super.

4 Onglets AndroTabs2Activity.Filière STI ArrayAdapter<String> tableau = new ArrayAdapter<String>( list.java public class AndroTabs2Activity extends TabActivity { /** Called when the activity is first created.android.Layout Ensi de Bourges .J.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas. i++) { tableau.-F. R. TabHost tabHost = getTabHost(). for (int i=0.id.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.layout.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <ListView android:layout_height="fill_parent" android:id="@+id/maliste" android:layout_width="fill_parent"></ListView> </LinearLayout> Layout d'une ligne de liste <?xml version="1.getContext().ligne.android.add("coucou " + i).onCreate(savedInstanceState). R.setAdapter(tableau). Développement sous Android . */ @Override public void onCreate(Bundle savedInstanceState) { super. } list. i<40. } } Layout <?xml version="1.layout.main). setContentView(R.com/apk/res/android" android:layout_height="fill_parent" android:orientation="horizontal" android:layout_width="fill_parent" android:baselineAligned="true" android:gravity="center"> <TextView android:layout_height="wrap_content" android:id="@+id/monTexte" android:text="TextView" android:textAppearance="?android:attr/textAppearanceLarge" android:layout_width="wrap_content"></TextView> <LinearLayout android:layout_height="wrap_content" android:orientation="horizontal" android:layout_width="fill_parent" android:gravity="right" android:layout_gravity="center"> <ImageView android:id="@+id/monImage" android:layout_height="wrap_content" android:layout_width="wrap_content" android:src="@android:drawable/ic_menu_compass"></ImageView> </LinearLayout> </LinearLayout> 9.monTexte). Lalande 56 / 79 .

setIndicator("Onglet 1"). spec. tabHost. intent = new Intent().class). } } Layout du Main <?xml version="1.addTab(spec).0" encoding="utf-8"?> <TabHost xmlns:android="http://schemas.setContent(intent). spec.J.android.layout.onglet1). spec.setClass(this.onCreate(savedInstanceState).setIndicator("Onglet 2").class).TabSpec spec.newTabSpec("Onglet 1").setCurrentTab(0). Lalande 57 / 79 . spec = tabHost.setClass(this.newTabSpec("Onglet 2"). setContentView(R.setContent(intent). spec. tabHost. } } Développement sous Android . ActivityOnglet1.Layout du Main Ensi de Bourges .com/apk/res/android" android:id="@android:id/tabhost" android:layout_width="fill_parent" android:layout_height="fill_parent"> <LinearLayout android:id="@+id/linearLayout1" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"> <TabWidget android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@android:id/tabs"> </TabWidget> <FrameLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@android:id/tabcontent"> </FrameLayout> </LinearLayout> </TabHost> Onglet 1 public class ActivityOnglet1 extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super. // Création dynamique d'une configuration pour l'onglet 1 spec = tabHost.addTab(spec).Filière STI TabHost. // Création de l'intent lancant l'activité de l'onglet intent = new Intent(). // Choisir l'onglet par défaut tabHost. Intent intent.-F. ActivityOnglet2.

layout.onCreate(savedInstanceState).layout.onglet2).main).call).0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas. */ @Override public void onCreate(Bundle savedInstanceState) { super.id. setContentView(R. call.J.onCreate(savedInstanceState).-F.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <RatingBar android:id="@+id/ratingBar1" android:layout_width="wrap_content" android:layout_height="wrap_content"></RatingBar> <SeekBar android:layout_height="wrap_content" android:id="@+id/seekBar1" android:layout_width="fill_parent"></SeekBar> <TextView android:text="TextView" android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView> </LinearLayout> Onglet 2 public class ActivityOnglet2 extends Activity { /** Called when the activity is first created.Filière STI Layout de l'onglet 1 <?xml version="1.android. setContentView(R.Layout de l'onglet 1 Ensi de Bourges . public void onCreate(Bundle savedInstanceState) { super. } } Layout de l'onglet 2 <?xml version="1. Lalande 58 / 79 .5 Intents Main.setOnClickListener(new OnClickListener() { Développement sous Android .android.java public class Main extends Activity { private MyBroadcastReceiverDyn myreceiver.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <ProgressBar android:layout_height="wrap_content" style="?android:attr/progressBarStyleLarge" android:layout_width="wrap_content" android:id="@+id/progressBar1"></ProgressBar> <QuickContactBadge android:id="@+id/quickContactBadge1" android:layout_width="wrap_content" android:layout_height="wrap_content"></QuickContactBadge> <TextView android:textAppearance="?android:attr/textAppearanceMedium" android:text="TextView" android:layout_height="wrap_content" android:layout_width="wrap_content" android:id="@+id/textView1"></TextView> </LinearLayout> 9. Button call = (Button)findViewById(R.

Button info = (Button)findViewById(R.toString()). if (resultCode == 50) Toast. Uri telnumber = Uri.broadcast"). // Bouton ok Button b = (Button)findViewById(R. Toast.Layout du Main Ensi de Bourges . GivePhoneNumber.id. "Code de requête récupéré (je sais d'ou je viens)".makeText(this. setContentView(R.48).android. Développement sous Android . IntentFilter filtre = new IntentFilter("andro. filtre). startActivityForResult(login. }}).LENGTH_LONG).id. registerReceiver(myreceiver.show().id.ok). GivePhoneNumber.setOnClickListener(new OnClickListener() { public void onClick(View v) { EditText number = (EditText)findViewById(R.show().givephonenumber).LENGTH_LONG).J. Intent data) { if (requestCode == 48) Toast.-F.makeText(this. info. // Broadcast receiver dynamique myreceiver = new MyBroadcastReceiverDyn().jf.getText(). Toast. b. "Code de retour ok (on m'a renvoyé le bon code)".info). startActivity(login).com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Main Activity:" /> <Button android:text="Call" android:id="@+id/call" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="Call with result" android:id="@+id/info" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> </LinearLayout> Seconde activité public class GivePhoneNumber extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super. Lalande 59 / 79 .class).class).layout.onCreate(savedInstanceState).number). } } Layout du Main <?xml version="1.Filière STI @Override public void onClick(View v) { Intent login = new Intent(getApplicationContext().parse("tel:" + number. }}). int resultCode.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas. } @Override protected void onActivityResult(int requestCode.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent login = new Intent(getApplicationContext().

nom_du_message"). finish.jf.id.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:text="Renseignez votre login:" android:id="@+id/TextView01" android:layout_width="wrap_content" android:layout_height="wrap_content" xmlns:android="http://schemas.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas. startActivity(call).id. Intent call = new Intent(Intent.com/apk/res/android"></TextView> <EditText android:id="@+id/number" android:layout_width="fill_parent" android:layout_height="wrap_content"></EditText> Développement sous Android . // Bouton pour s'auto appeler Button autoinvoc = (Button)findViewById(R.id.jf. finish(). autoinvoc.putExtra("extra".autoinvoc).-F.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent call = new Intent("andro. } }).Filière STI Intent call = new Intent(Intent. Lalande 60 / 79 .J. startActivity(call).ACTION_DIAL. direct. }}).direct).toString()). // Bouton appel direct Button direct = (Button)findViewById(R.id. "test"). }}).broadcast). broad.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { EditText number = (EditText)findViewById(R.id.Layout de la seconde activité Ensi de Bourges .parse("tel:" + number.setOnClickListener(new OnClickListener() { public void onClick(View v) { setResult(50).number). call. broadcast.finish). } }).ACTION_CALL.putExtra("extra". startActivity(call). // Bouton broadcast Button broad = (Button)findViewById(R.android. telnumber). } } Layout de la seconde activité <?xml version="1.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent broadcast = new Intent("andro. // Bouton finish Button finish = (Button)findViewById(R. Uri telnumber = Uri.getText(). }}). "test").broadcast"). sendBroadcast(broadcast).android. telnumber).

if (extra != null) { String val = extra.getString("extra").jf.J. Intent intent) { Bundle extra = intent.show().6 Receveur de broadcasts Ensi de Bourges .GivePhoneNumber" android:label="Renseigner le login"/> <receiver android:name="MyBroadcastReceiver"> <intent-filter> Développement sous Android .MAIN" /> <category android:name="android. "Broadcast message received: " + val.9.getExtras(). Lalande 61 / 79 .0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".android.Filière STI <Button android:text="Ok" android:id="@+id/ok" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="Finish" android:id="@+id/finish" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="Direct Call" android:id="@+id/direct" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="Broadcast" android:id="@+id/broadcast" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="Auto call" android:id="@+id/autoinvoc" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> </LinearLayout> 9.com/apk/res/android" package="andro.6 Receveur de broadcasts MyBroadcastReceiver.Main" android:label="@string/app_name"> <intent-filter> <action android:name="android.category.intent. Toast.category. Toast.-F.intent.DEFAULT" /> </intent-filter> </activity> <activity android:name=".intent.jf" android:versionCode="1" android:versionName="1.makeText(context.LENGTH_SHORT).0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.java public class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context.nom_du_message" /> <category android:name="android.action. } } } Manifest <?xml version="1.LAUNCHER" /> </intent-filter> <intent-filter> <action android:name="andro.

*/ @Override public void onCreate(Bundle savedInstanceState) { super. Toast.-F.getString("login".java public class Main extends Activity { /** Called when the activity is first created. Toast.makeText(getApplicationContext().show().jf.category.OnClickListener() { @Override public void onClick(View v) { Intent go_to_prefs = new Intent(getApplicationContext(). "Recup login: " + prefs. "Recup vitesse: " + prefs.9.setOnClickListener( new View.class). } } MesPreferences.makeText(getApplicationContext().J.""). } }).7 Gestion des préférences Ensi de Bourges .id.LENGTH_SHORT).OnClickListener() { @Override public void onClick(View v) { SharedPreferences prefs = PreferenceManager.show(). Lalande 62 / 79 .MesPreferences.onCreate(savedInstanceState). // Passé à l'activité des préférences View button = findViewById(R.LENGTH_SHORT).Filière STI <action android:name="andro.main).id. // Récupération d'une valeur de préférence View recup_login = findViewById(R. Toast.setOnClickListener( new View. button.layout. Toast.java public class MesPreferences extends PreferenceActivity { @Override Développement sous Android .CALL_PHONE"/> <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="4" /> </manifest> 9.prefs).RECEIVE_SMS"/> <uses-permission android:name="android. recup_login.intent.getString("vitesse".DEFAULT" /> </intent-filter> </receiver> </application> <uses-permission android:name="android.permission.broadcast" /> <category android:name="android.7 Gestion des préférences Main. startActivity(go_to_prefs).permission."").getDefaultSharedPreferences( getApplicationContext()). } }).recup_login). setContentView(R.

8 Demarrage de services AutoStart.jf" android:versionCode="1" android:versionName="1. } } Preferences XML <?xml version="1. Lalande 63 / 79 . } } Manifest <?xml version="1.-F.com/apk/res/android" package="andro.Filière STI protected void onCreate(Bundle savedInstanceState) { super. addPreferencesFromResource(R.startService(startServiceIntent).java public class AutoStart extends BroadcastReceiver { public void onReceive(Context context.android.intent. Intent intent) { Intent startServiceIntent = new Intent(context.LocalStartUI" android:label="@string/app_name"> <intent-filter> <action android:name="android. AndroService.prefs).LAUNCHER" /> </intent-filter> </activity> Développement sous Android .intent.category.class).xml.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.0"> <uses-sdk android:minSdkVersion="10" /> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".Preferences XML Ensi de Bourges .MAIN" /> <category android:name="android.action.onCreate(savedInstanceState). context.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.J.com/apk/res/android" android:key="prefs"> <CheckBoxPreference android:title="Diffusion du login" android:summaryOff="Ne pas diffuser" android:summaryOn="Diffuser" android:key="diffuser"></CheckBoxPreference> <EditTextPreference android:summary="login d'authentification" android:title="Login" android:key="login" android:dependency="diffuser"></EditTextPreference> <EditTextPreference android:title="Password" android:key="password" android:dependency="diffuser"></EditTextPreference> <ListPreference android:title="Vitesse" android:key="vitesse" android:entries="@array/key" android:entryValues="@array/value" android:dialogTitle="Choisir la vitesse:" android:persistent="true"></ListPreference> </PreferenceScreen> 9.android.

java Ensi de Bourges .jf.button1). b. setContentView(R.jf.J. } } AndroService.action.button2).id. b2.jf.onCreate(savedInstanceState). Button b2 = (Button) findViewById(R.java public class LocalStartUI extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super. stopService(stopService).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent stopService = new Intent("andro.layout.java public class AndroService extends Service { Développement sous Android .LocalStartUI.id.AutoStart"> <intent-filter> <action android:name="android.manageServiceAction").manageServiceAction"). Button b = (Button) findViewById(R.main). } }).intent.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent startService = new Intent("andro. Lalande 64 / 79 .Filière STI <service android:name=". } }).AndroService"> <intent-filter> <action android:name="andro.manageServiceAction" /> </intent-filter> </service> <receiver android:name=". startService(startService).-F.BOOT_COMPLETED" /> </intent-filter> </receiver> </application> </manifest> LocalStartUI.

9 Binding entre service et activite pour un meme processus AndroService.9 Binding entre service et activite pour un meme processus Ensi de Bourges . "Démarrage du service". Toast.Filière STI Timer timer.9. "Destruction du service". task = new TimerTask() { public void run() { handler.show(). } }).-F. Toast. } } // La donnée à transmettre à l'activité MonServiceBinder(). TimerTask task. "Service Created". /* boolean blop = true.LENGTH_SHORT). public void onCreate() { // Création du service Toast.cancel(). 0. } public IBinder onBind(Intent arg0) { return null. Lalande 65 / 79 . private final IBinder ib = new private class MonServiceBinder // Cette classe qui hérite public int getInfo() { return infoOfService.show(). // Vraiment pas une bonne idée ! while (blop == true) . 5000). // L'implémentation de l'objet de binding extends Binder implements AndroServiceInterface { de Binder implémente une méthode définie dans l'interface public IBinder onBind(Intent arg0) { return ib.LENGTH_SHORT).makeText(AndroService.LENGTH_SHORT). return START_STICKY. timer.show().this. int flags.J. int startId) { // Démarrage du service Toast. } } 9.schedule(task. */ final Handler handler = new Handler().LENGTH_SHORT).show(). timer. } public int onStartCommand(Intent intent. Toast. "plop !".makeText(this. } public void onDestroy() { // Destruction du service Toast.makeText(this.post(new Runnable() { public void run() { Toast. timer = new Timer().makeText(this. } }. Toast. } Développement sous Android .java public class AndroService extends Service { /* For binding this service */ private int infoOfService = 0.

* Le service va créer un objet de type Binder qui implémente cette interface.AndroServiceInterface. "Service Created". } public void onDestroy() { // Destruction du service } public int onStartCommand(Intent intent.show(). Toast.java public class AndroServiceBindActivity extends Activity { /** Called when the activity is first created. */ Développement sous Android .J.jf. int flags. } }). } catch (InterruptedException e) { e.java package andro. } infoOfService = 12. * L'activité va recevoir cet objet (sans connaitre son type) mais sait qu'il * implémentera cette interface. } } AndroServiceInterface.-F. /* C'est l'interface utilisée de part et d'autre (activité / service). int startId) { // Démarrage du service Thread t = new Thread(new Runnable() { @Override public void run() { try { Thread. Lalande 66 / 79 .Filière STI /* Service stuff */ public void onCreate() { // Création du service Toast. */ public interface AndroServiceInterface { public int getInfo().makeText(this. } AndroServiceBindActivity.start(). return START_STICKY. t.LENGTH_SHORT).sleep(4000).java Ensi de Bourges .printStackTrace().

AndroService. Lalande 67 / 79 .AndroService. // stockage de l'information provevant du service } public void onServiceDisconnected(ComponentName name) { } }.BIND_AUTO_CREATE).layout.0"> <uses-sdk android:minSdkVersion="10" /> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".AndroServiceBindActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.this.button1).category. Button b = (Button)findViewById(R.LAUNCHER" /> </intent-filter> </activity> <service android:name=".getInfo().com/apk/res/android" package="andro.show().Filière STI private int infoFromService = 0. } } Manifest <?xml version="1.class). IBinder service) { AndroServiceInterface myBinder = (AndroServiceInterface)service. Toast. } }). /* On se déconnecte du service */ unbindService(maConnexion).LENGTH_SHORT).main). "Info lue dans le service: " + infoFromService.class).id. @Override public void onCreate(Bundle savedInstanceState) { super. Intent serv = new Intent(this. startService(serv). /* Code disponible lorsque l'activité sera connectée au service */ private ServiceConnection maConnexion = new ServiceConnection() { /* Code exécuté lorsque la connexion est établie: */ public void onServiceConnected(ComponentName name. Toast. maConnexion.action.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.AndroService" /> <!-.onCreate(savedInstanceState).makeText(getApplicationContext().-F.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { /* Intent pour la connexion au service */ Intent intentAssociation = new Intent(AndroServiceBindActivity.android.Manifest Ensi de Bourges . b.not possible: android:process=":p2" --> </application> </manifest> 9. setContentView(R.java Développement sous Android .J.jf" android:versionCode="1" android:versionName="1. /* Bind au service: à refaire à chaque appui sur le bouton pour rafraichir la valeur */ bindService(intentAssociation. infoFromService = myBinder. Context.intent.intent.10 Binding inter processus utilisant le langage AIDL AndroService.MAIN" /> <category android:name="android.

Filière STI public class AndroService extends Service { /* For binding this service */ private int infoOfService = 0. int flags.Stub ib = new AndroServiceInterface.aidl Ensi de Bourges .start(). interface AndroServiceInterface { int getInfo(). } }). int startId) { // Démarrage du service Thread t = new Thread(new Runnable() { @Override public void run() { try { Thread.aidl package andro. Toast. } AndroServiceInterface. "Service Created". Lalande 68 / 79 .sleep(4000). } } AndroServiceInterface.-F.printStackTrace(). } public void onDestroy() { // Destruction du service } public int onStartCommand(Intent intent. } /* Service stuff */ public void onCreate() { // Création du service Toast. Développement sous Android .Stub() { @Override public int getInfo() throws RemoteException { // Cette classe qui hérite de Binder implémente une méthode définie dans l'interface return infoOfService. t. return START_STICKY.makeText(this. // La donnée à transmettre à l'activité private AndroServiceInterface.show(). public IBinder onBind(Intent arg0) { return ib. } infoOfService = 12.AndroServiceInterface. } catch (InterruptedException e) { e.LENGTH_SHORT).J.java généré /* * This file is auto-generated. DO NOT MODIFY. } }.jf.

os.jf. data.Filière STI * Original file: /home/jf/workspace/AndroServiceBind2/src/andro/jf/AndroServiceInterface. } /** * Cast an IBinder object into an andro. int _result = this. } private static class Proxy implements andro.os.java Ensi de Bourges . 0). } return new andro. return true.os. */ public static andro.IBinder asBinder() { return this.writeString(DESCRIPTOR).AndroServiceInterface))) { return ((andro.jf.RemoteException { android.aidl */ package andro.Parcel. */ public Stub() { this. android. reply. reply.Parcel data. } public int getInfo() throws android.Parcel reply.AndroServiceBind2Activity.FIRST_CALL_TRANSACTION + 0).IBinder obj) { if ((obj==null)) { return null.jf.lang. } return _result.writeInt(_result).obtain(). _result = _reply. } } static final int TRANSACTION_getInfo = (android.os.os. _reply. } public int getInfo() throws android.AndroServiceInterface { private android.jf.AndroServiceInterface interface. if (((iin!=null)&&(iin instanceof andro.recycle().os.jf.onTransact(code.IInterface { /** Local-side IPC implementation stub class.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply. } public java. public interface AndroServiceInterface extends android.transact(Stub.AndroServiceInterface.getInfo().recycle().os.os.attachInterface(this.-F.readInt().String getInterfaceDescriptor() { return DESCRIPTOR.Stub. Lalande 69 / 79 .os. * generating a proxy if needed.AndroServiceInterface)iin).jf.jf.IInterface iin = (android.os.IBinder.os. } AndroServiceBind2Activity. } finally { _reply.IBinder asBinder() { return mRemote.AndroServiceInterface asInterface(android.jf.os.readException().RemoteException.IBinder mRemote.Parcel. } public android. reply.AndroServiceInterface".jf. _reply. } case TRANSACTION_getInfo: { data.java Développement sous Android . _data.TRANSACTION_getInfo. } android. */ public static abstract class Stub extends android.enforceInterface(DESCRIPTOR). return true. /** Construct the stub at attach it to the interface. DESCRIPTOR).os.os. mRemote. _data.queryLocalInterface(DESCRIPTOR).J.IInterface)obj. int flags) throws android. android. } } return super.os.Proxy(obj). flags). int _result. try { _data.writeNoException().String DESCRIPTOR = "andro. } public android.Parcel _data = android.IBinder remote) { mRemote = remote. } @Override public boolean onTransact(int code.os.os. android.obtain().Binder implements andro. Proxy(android.lang.Parcel _reply = android.os.AndroServiceInterface { private static final java.writeInterfaceToken(DESCRIPTOR).os.

BIND_AUTO_CREATE). startService(serv).Filière STI public class AndroServiceBind2Activity extends Activity { private int infoFromService = 0. /* Code disponible lorsque l'activité sera connectée au service */ private ServiceConnection maConnexion = new ServiceConnection() { /* Code exécuté lorsque la connexion est établie: */ public void onServiceConnected(ComponentName name. /* On se déconnecte du service */ unbindService(maConnexion).action. } catch (RemoteException e) { e.jf" android:versionCode="1" android:versionName="1. try { // stockage de l'information provevant du service infoFromService = myBinder. maConnexion.layout.-F.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { /* Intent pour la connexion au service */ Intent intentAssociation = new Intent(AndroServiceBind2Activity.LAUNCHER" /> </intent-filter> </activity> <service android:name=".AndroService. Button b = (Button)findViewById(R.id.Now possible --> </application> </manifest> 9. Context.intent.J. AndroService. Lalande 70 / 79 .0"> <uses-sdk android:minSdkVersion="10" /> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".printStackTrace(). b.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas. Toast.this.LENGTH_SHORT). /* Bind au service: à refaire à chaque appui sur le bouton pour rafraichir la valeur */ bindService(intentAssociation. } }).intent.getInfo(). Toast.main).show().class).Stub.AndroServiceBind2Activity" android:label="@string/app_name"> <intent-filter> <action android:name="android. "Info lue dans le service: " + infoFromService.com/apk/res/android" package="andro.category.asInterface(service).makeText(getApplicationContext().android. } } public void onServiceDisconnected(ComponentName name) { } }.AndroService" android:process=":p2" /> <!-. Intent serv = new Intent(this.button1). setContentView(R.MAIN" /> <category android:name="android. @Override public void onCreate(Bundle savedInstanceState) { super.11 Processus indépendants Développement sous Android .Manifest Ensi de Bourges . IBinder service) { AndroServiceInterface myBinder = AndroServiceInterface.class).onCreate(savedInstanceState). } } Manifest <?xml version="1.

intent. int flags.MAIN" /> <category android:name="android.LAUNCHER" /> </intent-filter> </activity> <service android:name=".com/apk/res/android" package="andro. return START_STICKY.onCreate(savedInstanceState).java public class Main extends Activity { public void onCreate(Bundle savedInstanceState) { super.AndroService" android:process=":p2"> </service> </application> </manifest> AndroService.Filière STI Manifest <?xml version="1. Lalande 71 / 79 .intent.jf"> <activity android:name=".getActiveNetworkInfo().0"> <uses-sdk android:minSdkVersion="7" /> <application android:icon="@drawable/icon" android:label="@string/app_name" android:process="andro.category.java public class AndroService extends Service { public void onCreate() { } public void onDestroy() { } public int onStartCommand(Intent intent. Développement sous Android .jf" android:versionCode="1" android:versionName="1.12 Réseau Main. } public IBinder onBind(Intent arg0) { return null. setContentView(R. NetworkInfo net = manager. } } 9. while (b) .main).-F. // Réseau ConnectivityManager manager = (ConnectivityManager)getSystemService(CONNECTIVITY_SERVICE).0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.AndroProcessActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.android.action.Manifest Ensi de Bourges .layout.J. int startId) { // Démarrage du service boolean b = true.

isWifiEnabled()) wifi.ACCESS_NETWORK_STATE"></uses-permission> android:name="android.makeText(getApplicationContext().putExtra(BluetoothAdapter.MAIN" /> <category android:name="android.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=". if (!bluetooth.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.ACCESS_COARSE_LOCATION"></uses-permission> android:name="android.show().jf" android:versionCode="1" android:versionName="1. for (BluetoothDevice ap : s) Toast. } } Manifest <?xml version="1. "" + net.-F. // Wifi WifiManager wifi = (WifiManager)getSystemService(Context. startActivity(launchBluetooth).ACTION_CALL"/> </intent-filter> </receiver> </activity> </application> <uses-sdk android:minSdkVersion="5" /> <uses-permission <uses-permission <uses-permission <uses-permission <uses-permission </manifest> android:name="android.WRITE_SETTINGS"></uses-permission> android:name="android.ClasseGerantLAppel"> <intent-filter> <action android:name="Intent.android.setNetworkPreference(ConnectivityManager. manager.LAUNCHER" /> </intent-filter> <receiver android:name=". Toast.getBondedDevices().com/apk/res/android" package="andro.action.permission. } Intent discoveryMode = new Intent(BluetoothAdapter.permission.isEnabled()){ Intent launchBluetooth = new Intent(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION. startActivity(discoveryMode). 60). "" + ap.permission. discoveryMode. Lalande 72 / 79 .show(). // Bluetooth BluetoothAdapter bluetooth = BluetoothAdapter.WRITE_SECURE_SETTINGS"></uses-permission> android:name="android.makeText(getApplicationContext().ACTION_REQUEST_DISCOVERABLE). Toast.Filière STI Toast. if (!wifi.ACCESS_FINE_LOCATION"></uses-permission> Bluetooth public class BluetoothBroadcastReceiver extends BroadcastReceiver { public void onReceive(Context context.category.getDefaultAdapter().setWifiEnabled(true).LENGTH_LONG).intent.WIFI_SERVICE). Développement sous Android .LENGTH_LONG). Set<BluetoothDevice> s = bluetooth.getAction().TYPE_WIFI). Intent intent) { String action = intent.Manifest Ensi de Bourges .Main" android:label="@string/app_name"> <intent-filter> <action android:name="android.permission.getName().permission.ACTION_REQUEST_ENABLE).J.getType().intent.

setContentView(R. } } 9. Toast.action.LAUNCHER" /> </intent-filter> </activity> </application> Développement sous Android .13 Localisation Main.layout.LENGTH_SHORT).requestLocationUpdates("gps".com/apk/res/android" package="andro.-F.getLastKnownLocation("gps").EXTRA_DEVICE).intent.makeText(getApplicationContext().J. // Récupération de la localisation Location localisation = manager.makeText(getApplicationContext(). List<String> fournisseurs = manager. 100.intent.13 Localisation Ensi de Bourges . Toast.Filière STI BluetoothDevice appareil = null.LENGTH_SHORT). */ @Override public void onCreate(Bundle savedInstanceState) { super.MAIN" /> <category android:name="android.Main" android:label="@string/app_name"> <intent-filter> <action android:name="android. // Fournisseurs de service LocationManager manager = (LocationManager)getSystemService(Context.ACTION_FOUND)) appareil = (BluetoothDevice)intent.getLatitude().0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".show(). new LocationListener() { public void onStatusChanged(String provider.jf" android:versionCode="1" android:versionName="1.makeText(getApplicationContext(). for (String f : fournisseurs) Toast. // Ecouteur de changement de localisation manager. Toast.equals(BluetoothDevice. if (action.java public class Main extends Activity { /** Called when the activity is first created.getAllProviders().category. "Lattitude" + localisation. 6000.onCreate(savedInstanceState).show(). Toast.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas. "" + f.getParcelableExtra(BluetoothDevice.9.main).show(). Toast.LENGTH_SHORT). } } Manifest <?xml version="1. Bundle extras) { } public void onProviderEnabled(String provider) { } public void onProviderDisabled(String provider) { } public void onLocationChanged(Location location) { // TODO Auto-generated method stub } }).android. "Longitude" + localisation. int status. Lalande 73 / 79 .LOCATION_SERVICE).getLongitude().

TYPE_ACCELEROMETER) { float x. super. t.onStop(). // Création d'un écouteur qui réagit aux événements de l'accéléromètre myListener = new SensorEventListener() { public void onSensorChanged(SensorEvent event) { if (event.9.main).java public class Main extends Activity { @Override protected void onStop() { // Surtout. // Ajout d'un écouteur pour l'acceleromètre manager.connectSimulator(). setContentView(R. } } public void onAccuracyChanged(Sensor sensor. y = event. manager.layout.values[0]. ne pas oublier de détacher l'écouteur ! manager. /** Called when the activity is first created. manager. SensorManager.unregisterListener(myListener).id.type == Sensor. SENSOR_SERVICE).J.onCreate(savedInstanceState).y. int accuracy) { // TODO Auto-generated method stub } }.SENSOR_DELAY_UI).setText("x = " + x). z = event.TYPE_ACCELEROMETER) .z.14 Capteurs Ensi de Bourges .14 Capteurs Main.-F. //SensorManager manager = (SensorManager)getSystemService(SENSOR_SERVICE). Lalande 74 / 79 .text1). SensorEventListener myListener.getSystemService(this. TextView t = (TextView)findViewById(R. x = event. manager = SensorManagerSimulator.registerListener(myListener .getDefaultSensor(Sensor.values[1].Filière STI <uses-sdk android:minSdkVersion="5" /> </manifest> 9. } SensorManagerSimulator manager. } } Développement sous Android . */ @Override public void onCreate(Bundle savedInstanceState) { super.values[2].

Basics of Android : Part III – Android Services. Lalande 75 / 79 . Android Cookbook. R Ravichandran and Dianne Hackborn. Janvier 2009. Android Competency Center. 2009. jwei512. Think Android. stackoverflow. Android developers. Emmanuel Robles.Filière STI 10 Bibliographie PA Cookbook Dalvik E-mail SO SD API-Service XML VL BAS MARA ON PT DBSQL SS LSAS KEK Programmation Android. Darwin. de la conception au déploiement avec le SDK Google Android. Damien Guignard. Marko Gargenta. Novembre 2010. Juillet 2009. Article Wikipedia. How to Position Views Properly in Layouts. the free encyclopedia.0. Dianne Hackborn. Android developers. IBM. October 22. Julien Chable. Oleg Mazurashu. O'Reilly Media. The Developer's Info. Ian F. Archives googlegroups. Utiliser les services sous Android. Comment on fait un jeu pour smartphone ?. Tab Layout. Developpez. Data storage: Using Databases. Service API changes starting with Android 2. Janvier 2010. Kek. Développement sous Android . Sensor Simulator. novembre 2009. Processes and Threads. Android permissions: Phone Calls: read phone state and identity. Working with XML on Android.10 Bibliographie Ensi de Bourges . Février 2010. from Wikipedia. 2011. 2009. Nicolas Druet. Eyrolles. Implementing Remote Interface Using AIDL.J. [android-developers] Launching a Service at the startup. Email sending in Android. 11 Février 2010. Michael Galpin.com. 23 Jun 2009. decembre 2011. Android developers.-F.