Développement d'applications Android
Développement d'applications Android
Parti 1: Android
Systeme
Module GIM21S04
Technologie XML et Technologie Mobile
REDOUANE EZZAHIR
2015/2016
Introduction 4
Historique des versions 4
Les éléments d'une application 7
Le Manifest de l'application 7
Les ressources 8
Les chaines 8
Internationalisation 9
Autres valeurs simples 9
Autres ressources 9
Les activités 9
Cycle de vie d' une activité 10
Sauvegarde des interfaces d'activité 11
Démonstration 11
Interfaces graphiques 12
Propriétés de l'écran (unités de mesure) 12
Attributs XML obligatoires 12
Inclusions de gabarits 15
Positionnement avancé 15
ListView 16
Menu et Action bar 20
Menu : déclaration dans un fichier XML 20
Menu Contextuel 22
Résumé sur menus et menus contextuels 23
Action Bar 23
Les Intents 25
Principe des Intents 25
Résultat d'une activité 26
partager des informations via les intents 27
Types d'Intent: actions 27
Catégories d'Intent 28
Recevoir et filtrer les Intents 28
Filtrage d'un Intent par l'activité 29
Utilisation de catégories pour le filtrage 29
Réception par un BroadcastReceiver 30
Les messages natifs 30
Persistance des données 31
Différentes persistances 31
Préférences partagées 32
Activité d'édition de préférences 32
Attributs des préférences 33
Préférences: toggle et texte 33
Préférences: listes 33
Les fichiers 34
BDD SQLite 35
Lecture / Ecriture dans la BDD 35
Programmation concurrente 36
Processus 36
Cycle de Vie des processus 36
Threads 37
Services 37
Threads et interface graphique 37
Services 38
Démarrer / Arrêter un service 38
Auto démarrage d'un service 39
Service dans le thread principal 40
Références: 41
Introduction
Il est important de prendre la mesure des choses. A l'heure actuelle (November 2016):
• juillet 2011: 550 000 activations par jour
• décembre 2011: 700 000 activations par jour
• sept. 2012: 1.3 millions d'activations par jour (Wikipedia)
• avril 2013: 1.5 millions d'activations par jour (Wikipedia) Il y aurait donc un parc
de 400 millions d'appareils Android.
La plateforme Android
La plate-forme Android est une pile logicielle qui a été conçu principalement, mais
pas exclusivement pour le soutien des appareils mobiles tels que les téléphones et
les tablettes. Cette pile a plusieurs couches :
• Un système d'exploitation open-source
• Un intergiciel (middleware)
• et des applications clés.
Le système Android est également livré avec un kit de développement, ou SDK, qui
fournit des outils et des APIs nécessaires pour le développement d’applications Android
en utilisant le langage de programmation Java.
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): S’appuyant sur le noyau Linux 2.6 pour les services
système de base. Elle Offre une architecture d’autorisations Mémoire et processus de
gestion des Fichier, et d'IO réseau.
• Pilotes : de Gestion du matériel (écran clavier ...) et de Gestion des capteurs (appareil
photo, GPS, accéléromètre …). assurent la sécurité et fournissent une couche
d'abstraction entre le Hardware et le reste de la pile logiciel
• un couche d'abstraction pour l'accès aux capteurs (HAL)
• une machine virtuelle: Dalvik Virtual Machine (avant Lollipop)
• un compilateur de bytecode vers le natif Android Runtime (pour Lollipop)
• des applications (navigateur, gestion des contacts, application de téléphonie )
• des bibliothèques (SSL, SQLite, OpenGL ES, etc )
• des API d'accès aux services Google Anatomie d'un déploiement: Dalvik et ART
Dalvik et ART
[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. [Dalvik] n'est pas compatible avec une JVM du type Java SE ou
même Java ME. La librairie d'accès est donc redéfinie entièrement par Google.
Application Framework
Fonction Rôle
Utilisé pour construire une application, y compris les listes, grilles, texte, Boîtes,
View System
des boutons, et le navigateur Web intégré
Permettant aux applications d'accéder aux données d'autres applications ou pour
Content Provider
partager leurs propres données
Resource Fournir un accès à des ressources non-code (chaînes localisées, les graphiques et
Manager les fichiers de mise en page)
Notification Permettre à toutes les applications d’afficher des alertes de clients dans la barre
Manager d'état
Gestion du cycle de vie des applications en fournissant une navigation
Activity Manager
commune de "pile de retour (back stack).
Les éléments d'une application
Le Manifest de l'application
Le fichier déclare l'ensemble des éléments de l'application.
<?xml version="1.0" encoding="utf-8"?>
<intent-filter>
<category android:name="[Link]"/>
</intent-filter>
</activity>
<service></service>
<receiver></receiver>
<provider></provider>
</application>
</manifest>
Les ressources
Les chaines
Les chaines constantes de l'application sont situées dans l’externalisation des chaines
permettra de réaliser l'internationalisation de l'application. Voici un exemple:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello Hello JFL !</string>
<string name="app_name">AndroJF</string>
</resources>
Autres ressources
D'autres ressources sont spécifiantes dans le dossier res: telles que les menus, les
images ([Link]), des dimensions ([Link]) et des couleurs ([Link])
Les activités
Une application Android étant hebergée sur un système embarqué, le cycle de vie d'une
application ressemble à celle d'une application Java ME. L'activité peut passer des états:
• démarrage -> actif: détient le focus et est démarré
• actif -> suspendue: ne détient plus le focus
• suspendue -> actif:
• suspendue -> détruit:
Le nombre de méthodes à surcharger et même plus important que ces états:
Cycle de vie d' une activité
[Link](savedInstanceState);
setContentView([Link]);
}
protected void onDestroy() {
[Link]();
}
protected void onPause() {
[Link]();
}
protected void onResume() {
[Link]();
}
protected void onStart() {
[Link]();
}
protected void onStop() {
[Link]();
}
}
Démonstration
Videos:
parti1
parti2
Interfaces graphiques
Les éléments graphiques héritent de la classe View. 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 sont placés les uns par rapport aux autres
• TableLayout: disposition matricielle
• FrameLayout: disposition en haut à gauche en empilant les éléments
• GridLayout: disposition matricielle avec N colonnes et un nombre infini de lignes
Inclusions de gabarits
Les interfaces peuvent aussi inclure d'autres interfaces, permettant de factoriser des
morceaux d'interface. On utilise dans ce cas le mot clef include:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="; android:orientation="vertical"
android:layout_width="match_parent" android:layout_height="match_parent" >
<include android:id="@+id/include01"
android:layout_width="wrap_content" android:layout_height="wrap_content"
layout="@layout/acceuil" >
</include>
</LinearLayout>
Si le gabarit correspondant à LinearLayout contient lui aussi un LinearLayout, on risque
d'avoir deux imbrications de LinearLayout inutiles car redondant:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android=";>
<LinearLayout xmlns:android=";>
<TextView/><TextView/>
</LinearLayout> </LinearLayout>
Positionnement avancé
Le problème précédent est dû au fait qu'un layout doit contenir un unique root element, du
type View ou ViewGroup. On peut pas mettre une série de TextView sans root element.
Pour résoudre ce problème, on peut utiliser le tag **merge**. Si l'on réécrit le layout
comme cela:
<merge xmlns:android=";>
<TextView /> <TextView /> </merge>
L'inclusion de celui-ci dans un layout linéaire transformera:
<LinearLayout xmlns:android=";><include android:id="@+id/include01" layout="@layout/
acceuil"></include> </LinearLayout>
en:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android=";>
<TextView/><TextView/> </LinearLayout>
Pour obtenir une interface agréable, il est souvent nécessaire de réaliser correctement le
positionnement des éléments graphiques. La difficulté est d'arriver à programmer un
placement qui n'est pas dépendant de l'orientation ou de la taille de l'écran.
Dans [VL], on trouve une explication pour réaliser un placement simple: un texte à gauche
et une image à droite de l'écran, alignée avec le texte. Cela peut être particulièrement utile
si par exemple on réalise une liste d'item qui contient à chaque fois un texte et une icône.
Le principe réside dans l'imbrication de LinearLayout:
• Un premier layout contiendra l'ensemble des éléments. Son orientation doit
être horizontal et sa gravité (gravity) center. On inclut ensuite le texte.
• Puis, l'image étant censée être à droite, il faut créer un LinearLayout consécutif au
texte et préciser que la gravité (gravity) est right. Pour aligner les éléments, il faut préciser
que la gravité du layout (layout_gravity) est center.
Le layout décrit ci-avant ressemble à:
<LinearLayout xmlns:android="; android:layout_height="match_parent"
android:orientation="horizontal" android:layout_width="match_parent"
android:gravity="center">
<TextView ></TextView>
<LinearLayout android:layout_height="wrap_content"
android:orientation="horizontal" android:layout_width="match_parent"
android:gravity="right" android:layout_gravity="center">
<Image />
</LinearLayout></LinearLayout>
ListView
Au sein d'un gabarit, on peut implanter une liste que l'on pourra dérouler si le nombre
d'éléments est important. Si l'on souhaite faire une liste plein écran, il suffit juste de poser
un layout linéaire et d'y implanter une ListView. Le XML du gabarit est donc:
<LinearLayout>
<ListView android:id="@+id/listView1" >
</ListView></LinearLayout>
La classe ListView est de type ViewGroup et constituée de plusieurs lignes, chaque ligne
est une vue enfant du ViewGroup qui prend en charge l'affichage des données d'un
élément de la liste. Les données à afficher sont de provenance divers
la liste utilise un ListAdpater qui lie la vue aux données.
Android fournit plusieurs ListAdapter ArrayAdapter<T> pour les données de type tableau
CursorAdapter pour les données stockées en base SimpleCursorAdapter pour les
données de type String ou image stockées en base etc.
Etant donné qu'une liste peut contenir des éléments graphiques divers et variés, 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.
Prenons un exemple simple: une liste de chaine de caractères. Dans ce cas, on créé un
nouveau gabarit montexte et on ajoute dynamiquement un ArrayAdapter à la
liste listView1.
Lorsque les listes contiennent un layout plus complexe qu'un texte, 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. A chaque entrée de la liste, 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.
ArrayAdapter (Context context, int resource, int textViewResourceId)
Le code de l'exemple précédent doit être adapté comme ceci:
ListView list = (ListView)findViewById([Link]); ArrayAdapter<String> tableau
= new ArrayAdapter<String>( [Link](), [Link],
[Link]); for (int i=0; i<40; i++) {
Lorsque chaque item de la liste contient plusieurs données dynamiques, il faut recoder la
classe ArrayAdapter, comme expliqué dans LVAA. Si l'on suppose par exemple que la
donnée est:
public class User { public String name; public String hometown; }
Et que le layout d'un item est:
<LinearLayout >
<TextView android:id="@+id/tvName" />
<TextView android:id="@+id/tvHome" /> <LinearLayout/>
On doit alors écrire une méthode getView d'une classe UsersAdapter héritant
de ArrayAdapter qui va renvoyer l'élément graphique correspondant à une item (donné
ci-après). Pour utiliser cette ArrayAdapter on fait comme précédemment:
// Construct the data source
ArrayList<User> arrayOfUsers = new ArrayList<User>();
// Create the adapter to convert the array to views
UsersAdapter adapter = new UsersAdapter(this, arrayOfUsers); ListView listView =
(ListView) findViewById([Link]); [Link](adapter) (new User( ..));
Exemple
<menu xmlns:android=[Link]
<item android:id="@+id/agenda" android:icon="@drawable/agenda"
android:orderInCategory="100" android:title="Agenda">
<menu>
<item android:id="@+id/rdv" android:title="RDV"/>
<item android:id="@+id/rappel" android:title="Rappel"/>
</menu>
</item>
</menu
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate([Link], menu); return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
[Link](0).setTitle("Agenda" + [Link]());
rSubMenu submenu = [Link](99, 777, 101, "Signets")
[Link](99, 778, 101, "AdrooidNote");
[Link](99, 779, 101, "JavaNote"));
[Link](0).setIcon([Link]);
return [Link](menu);
}
Pour filtrer les clicks de l'utilisateur sur un des boutons de l'action bar, il faut utiliser la
méthode onOptionsItemSelected de votre activité:
@Override
public boolean onOptionsItemSelected(MenuItem item) { switch ([Link]()) {
case [Link]: [Link](this, "Agenda", Toast.LENGTH_SHORT).show(); break;
case [Link]: [Link](this, "RDV " + [Link](),
Toast.LENGTH_SHORT).show(); break;
case [Link] : [Link](this, "Rappel " + [Link](),
Toast.LENGTH_SHORT).show(); break;
case 777: [Link](this, "Signets", Toast.LENGTH_SHORT).show(); break; case 778:
[Link](this, "AdrooidNote", Toast.LENGTH_SHORT).show(); break;
case 779: [Link](this, "JavaNote", Toast.LENGTH_SHORT).show();break; }
return [Link](item);
}
Menu Contextuel
Un menu contextuel apparaît si l'utilisateur effectue un appui long sur l'écran
• Il faut donc que l'appui long soit géré par le widget possédant le menu contextuel
• appeler la méthode registerForContextMenu après setContentView, lorsque la
construction de la vue (dans onCreate(). la méthode reçoit l'identifiant du widget sur
lequel ajouter le menu contextuel
• La création du menu est effectuée dans la méthode
onCreateContextMenu
• La méthode onContextItemSelected est utilisée pour traiter le choix de l'utilisateur
RelativeLayout view;
@Override
protected void onCreate(Bundle savedInstanceState) {
[Link](savedInstanceState); setContentView([Link].activity_main);
view = ((RelativeLayout) findViewById([Link].view1));
; registerForContextMenu(view); }
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate([Link], menu); return true;
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v,ContextMenuInfo menuInfo)
{
[Link](menu);
[Link](menu, v, menuInfo);
}
@Override
public boolean onContextItemSelected(MenuItem item) { switch ([Link]()) {
case [Link]:
[Link](this, "Context Menu (Agenda)", Toast.LENGTH_SHORT).show();
break;
case 777:
[Link](this, "context Menu (Signets)", Toast.LENGTH_SHORT).show();
break;
}
return [Link](item); }
@Override
public boolean onLongClick(View v) { [Link]();
return false;
}
• onCreateOptionsMenu - est invoqué que lorsque le menu est affiché pour la première fois.
Crée un menu et n'est pas plus utilisé. Nous ajoutons des éléments de menu ici.
• onPrepareOptionsMenu - invoqué à chaque fois avant d'afficher le menu. Nous apportons
des changements au menu déjà existant si cela est nécessaire.
• onOptionsItemSelected(MenuItem) appelée lors d'un choix dans un menu de l'activité. Le
paramètre est le choix effectuéon
• onOptionsMenuClosed(Menu) appelée lors de la fermeture d'un menu de l'activité. Le
paramètre est le menu fermé.
• onCreateContextMenu(ContextMenu, View, [Link]) appelée
lorsqu'un menu contextuel est affiché. Le 1er paramètre est ce menu contextuel, le 2ème
est l'élément d'interface auquel il est associé, le dernier donne des informations sur le
contenu de l'élément d'interface qui a causé l'apparition du menu contextuel
Action Bar
Pour ajouter une action bar, il suffit d'utiliser un thème l'ayant prévu, par
exemple [Link] et de faire hériter votre activité
de AppCompatActivity. Dans votre manifest, il faut modifier l'attribut android:theme:
<activity android:theme="@style/[Link]" >
Le menu de l'action bar est controlé par la méthode onCreateOptionsMenu(Menu
menu) qui charge un fichier menu depuis les ressources.
L'attribut app:showAsAction="ifRoom" permet de demander d'ajouter cette action
comme un bouton dans la barre d'action, si la place est disponible.
Il est possible d'élaborer des action bar avec des actions plus complexes:
• un champ de recherche: avec
l'attribut app:actionViewClass="[Link]"
• une action presonalisée:
• ShareActionProvider: pour partager un élément dans l'écosystème Android
• une action personalisée: on doit construit l'objet View soit même.
Pour aller plus, se référer à la documentation de l'Action bar.
Certaines classes helpers permettent d'organiser l'espace de l'écran ou de réaliser des
animations pour chabger l'état de l'écran.
• L'animation des gabarits
• Les onglets: une combinaison de l'action bar et de fragments
• Le défilement à gauche ou à droite: en utilisant ViewPager, PagerAdapter et des
fragments Et d'autres animations que je ne détaille pas par la suite:
• fondu enchainé de deux activités
• animation de retournement de carte
• effet de zoom sur un élément
L'animation d'un gabarit est simple à mettre en
oeuvre: il suffit par exemple d'ajouter l'attribut
android:animateLayoutChanges="true" à un LinearLayout pour que l'animation soit
jouée quand on ajoute un élément à ce gabarit:
[Link](newView, 0);
La réalisation d'onglets permet de mieux utiliser l'espace réduit de l'écran. Pour réaliser
les onglets, les choses se sont considérablement simplifiées depuis l'API 11. Dans
l'activité principale, il faut ajouter à l'action bar existante des onglet des objets de
type Tab ayant obligatoirement un listener de type TabListener, comme présenté dans la
documentation sur les onglets:
final ActionBar actionBar = getActionBar();
// Specify that tabs should be displayed in the action
bar. [Link](ActionBar.NAVIGATION_MODE_TABS); // Create a
tab listener that is called when the user changes tabs. [Link] tabListener
= new [Link]() { public void onTabSelected( tab,
FragmentTransaction ft) { // show the given tab } public void
onTabUnselected( tab, FragmentTransaction ft) { // hide the given tab }
public void onTabReselected( tab, FragmentTransaction ft) { // probably ignore
this event
}}; // Add 3 tabs, specifying the tab's text and TabListenerfor (int i = 0; i < 3; i++)
{ Tab t = [Link]().setText("Tab " + (i + 1));
[Link](tabListener); [Link](t);
}
Et celui du fragment :
<LinearLayout
<CheckBox android:id="@+id/checkBox1" />
</LinearLayout>
Les Intents
Lorsque le bouton retour est pressé, l'activité courante prend fin et revient à l'activité
précédente. Cela permet par exemple de terminer son appel téléphonique et de revenir à
l'activité ayant initié l'appel.
Au sein d'une application, une activité peut vouloir récupérer un code de retour de l'activité
"enfant". On utilise pour cela la méthode startActivityForResult qui envoie un code de
retour à l'activité enfant. Lorsque l'activité parent reprend la main, 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.
L'appel d'un Intent devient donc:
public void onCreate(Bundle savedInstanceState) {
Intent login = new Intent(getApplicationContext(),
[Link]); startActivityForResult(login,48); }
Le filtrage dans la classe parente permet de savoir qui avait appelé cette activité enfant:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 48)
[Link](this, "Code de requête récupéré (je sais d'ou je viens)",
Toast.LENGTH_LONG).show();
}
Pour définir une action personnelle, il suffit de créer une chaine unique:
Intent monIntent = new Intent(".nom_du_message");
Catégories d'Intent
Les catégories d'Intent permettent de grouper les applications par grands types de
fonctionnalités (clients emails, navigateurs, players de musique, etc ). Par exemple, on
trouve les catégories suivantes qui permettent de lancer:
• DEFAULT: catégorie par défaut
• BROWSABLE: une activité qui peut être invoquée depuis un clic sur un navigateur web,
ce qui permet d'implémenter des nouveaux types de lien, e.g. foo://truc
• APP_MARKET: une activité qui permet de parcourir le market de télécharger des
applications • APP_MUSIC: une activité qui permet de parcourir et jouer de la musique
L'attribut host définit l'URI de l'autorité (on évite ainsi le fishing). L'attribut scheme définit la
partie de gauche de l'URI. On peut ainsi lancer une activité lors d'un clic sur un lien:
<a href=";>lien</a>
Réception par un BroadcastReceiver
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=".broadcast" />
Démonstration : voir TP N° 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 partagées)
• 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. S'il est nécessaire de réaliser une sauvegarde plus
personnalisée, 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 fournit aussi automatiquement, la persistance du chemin de navigation de
l'utilisateur, ce qui le renvoie à la bonne activité lorsqu'il appuie sur la touche Retour. La
navigation vers le parent (bouton "up") n'est pas automatique car c'est le concepteur qui
doit décider vers quelle activitée l'application doit retourner quand on appuie sur "up". Elle
peut être programmée dans le Manifest avec l'attribut android:parentActivityName.
Préférences partagées
La classe SharedPreferences permet de gérer des paires de clé/valeurs associées à une
activité. On récupère un tel objet par l'appel à getPreferences:
SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
String nom = [Link]("login", null);
Long nom = [Link]("taille", null);
La méthode getPreferences(int) appelle en fait getPreferences(String, int) à partir du
nom de la classe de l'activité courante. Le mode MODE_PRIVATE restreint l'accès au
fichier créé à l'application. Les modes
d'accès MODE_WORLD_READABLE et MODE_WORLD_WRITABLE permettent aux
autres applications de lire/écrire ce fichier.
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. 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.
Il s'agit de PreferenceActivity. A partir d'une description XML des préférences, la classe
permet d'afficher un écran composé de modificateurs pour chaque type de préférences
déclarées.
Voici un exemple de déclarations de préférences XML, à stocker dans :
<PreferenceScreen xmlns: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" />
</PreferenceScreen>
</PreferenceScreen>
Des attributs spécifiques à certains types de préférences peuvent être utilisés, par
exemple android:summaryOn pour les cases à cocher qui donne la chaine à afficher
lorsque la préférence est cochée. On peut faire dépendre une préférence d'une autre, à
l'aide de l'attribut android:dependency. Par exemple, 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" />
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:
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 valeur "Petite", la préférence vitesse est associée à "1".
Ecriture de préférences
Il est aussi possible d'écraser des préférences par le code, 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.
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.MODE_PRIVATE); Editor editor = ();
[Link]("login", "jf"); [Link]();
Il est même possible de réagir à un changement de préférences en installant un écouteur
sur celles-ci:
[Link]( new OnSharedPreferenceChanged () {});
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, c'est à dire de
nombreux cas. 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.
Pour la gestion des permissions, 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. En utilisant ces constantes comme un masque, on
peut ajouter | MODE_APPEND pour ajouter des données.
try { FileOutputStream out = openFileOutputStream("fichier", MODE_PRIVATE);
} catch (FileNotFoundException e) { }
Les ressources permettent aussi de récupérer un fichier embarqué dans l'application:
Resources res = getResources();
InputStream is = [Link]([Link]);
A noter: les fichier sont à éviter. 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.
BDD SQLite
Android dispose d'une base de donnée relationelle basée sur SQLite. Même si la base
doit être utilisée avec parcimonie, cela fournit un moyen efficace de gérer une petite
quantité de données.
[DBSQL] présente un exemple de création de base de données, ce qui se fait en héritant
de SQLiteOpenHelper:
Processus
Threads
Dans le processus principal, le système crée un thread d'exécution pour l'application:
le thread principal. Il est, entre autre, responsable de l'interface graphique et des
messages/notifications/événements entre composants graphiques. Par exemple,
l'événement générant l'exécution de onKeyDown() s'exécute dans ce thread.
Ainsi, si l'application doit effectuer des traitements longs, elle doit éviter de les faire dans
ce thread. Cependant, il est interdit d'effectuer des opérations sur l'interface graphique en
dehors du thread principal (aussi appelé UI thread), 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. Le
comportement est imprévisible car le toolkit graphique n'est pas thread-safe.
Services
public void onClick(View v) { // DO NOT DO THIS !
new Thread(new Runnable() {
public void run() {
Bitmap b = loadImageFromNetwork("");
[Link](b); } }).start(); }
}}
Une exception peut parfois (pas toujours) être levée quand cela est
détecté: CalledFromWrongThreadException.
Services
La structure d'une classe de service ressemble à une activité. Pour réaliser un service, on
hérite de la classe Service et on implémente les méthodes de création/démarrage/arrêt du
service. La nouvelle méthode qu'il faut implémenter ici est onBind qui permet aux IPC de
faire des appels à des méthodes distantes.
On obtient:
Service dans un processus indépendant
[Link]
title=An_Overview_of_the_Android_Architecture&mobileaction=toggle_view_desktop
[Link]
enjeux-et-pratique/download?chk=8764161b42e7c18cad66c63b5d1c7cd8