Académique Documents
Professionnel Documents
Culture Documents
walid.zeddini@gmail.com
www.zeddini.com
Objectifs
Une des caractéristiques d’Android est son approche fortement modulaire qui permet
aux applications de collaborer entre elles. Une application est constituée de plusieurs
composants, chacun de ces composants pouvant être réutilisé depuis une autre
application ce qui évite ainsi de devoir réinventer la roue.
Ce chapitre traite de ce modèle d’architecture, les différents types de composants, la
façon de les invoquer et leur cycle de vie sont passés en revue.
Ressources :
http://developer.android.com/ tout, tout, tout
Cours, TD/TP, code http://www.u-picardie.fr/ferment/android
http://www.vogella.com/android.html tutoriel
http://saigeethamn.blogspot.fr/ tutoriel
http://www.franck-simon.com cours tres complet
l'Art du developpement, M. Murphy, Pearson livre simplePro Android 4, S
Komatineni D MacLean, Apress livre complet
Le livre écrit par Florent Garin « Android »
Le livre de Mark Murphy chez Pearson
Le cours de Victor Matos :
http://grail.cba.csuohio.edu/~matos/notes/cis-493/Android-Syllabus.pdf
Plusieurs livres :
Android A Programmers Guide - McGraw Hill
Professional Android Application Development - Wrox
2
4.1 UNE FORTE MODULARITÉ
Il y a plus d’une dizaine d’années, l’avènement des langages objets a fait naître
l’espoir de pouvoir développer facilement des éléments de code réutilisables d’une
application à l’autre. Malheureusement, si le paradigme objet, avec des notions telles
que l’encapsulation, favorise la création de librairies, il n’en demeure pas moins que
développer une portion de code réellement exploitable dans un contexte autre que
celui d’origine n’est toujours pas chose aisée.
Pour faciliter la collaboration entre les éléments de programme, le modèle
applicatif d’Android définit la notion de composants d’applications. Un composant
est un module de l’application qui pourra être activé depuis un autre programme
Android. Lorsqu’un module est activé, l’application à laquelle il appartient est lancée
si nécessaire, il est donc exécuté dans son propre processus.
Les fonctionnalités du module sont donc réutilisées en mettant en œuvre des
communications interprocessus.
Les objets de ces types sont donc des morceaux de l’application qui pourront être
invoqués par d’autres applications pour remplir une tâche précise. C’est un peu comme
si le programme avait un point d’entrée officiel, celui de l’activité principale qui se
lance lorsque l’utilisateur clique sur l’icône de l’application, et des points d’entrée
alternatifs démarrés par d’autres programmes.
3
directe. C’est-à-dire qu’une activité donnée déclenche l’affichage d’une autre activité
en appelant la méthode startActivity avec un Intent mentionnant clairement le nom
de l’activité. On verra par la suite comment créer concrètement des objets Intent.
Le mode explicite est donc très classique : comme dans la plupart des applications,
les écrans à afficher sont invariablement les mêmes d’une exécution à l’autre et identi-
fiés à l’avance. Aucune particularité n’est à noter dans ce mode de fonctionnement si
ce n’est qu’Android permet d’afficher des activités n’appartenant pas à l’application
d’origine.
Le mode implicite par contre est une spécificité d’Android extrêmement puissante.
Dans ce mode, le nom de l’activité n’est pas précisé nominativement. L’objet Intent qui
encapsule la demande de changement ne porte pas l’identification de l’activité mais
un descriptif des caractéristiques ou plutôt des capacités de traitement dont l’activité
devra être dotée. Ensuite, une mécanique de résolution se met en marche, Android
recherche dans tout le système, et non pas uniquement dans l’application courante, les
activités répondant aux exigences exprimées dans l’Intent. À la fin de cet algorithme,
l’activité identifiée s’affiche alors. Au cas où il y aurait plusieurs activités en mesure
d’assurer le service demandé, leur liste est proposée à l’utilisateur qui devra alors en
sélectionner une. Il peut arriver aussi qu’aucune activité ne puisse couvrir le besoin.
Si cela se produit, une exception est alors lancée.
4
setDataAndType(Uri,String).
On pourrait se demander pourquoi renseigner le type si on ne fournit pas de
donnée. La réponse est que l’activité appelée et appelante communiquent dans les
deux sens. L’intention (Intent) est créée par l’activité de départ et transmise à la
suivante. Celle-ci a aussi la possibilité de retourner des données, toujours transportées
par un objet de type Intent, à la première activité. On peut donc imaginer que l’Intent
initial ne contienne pas de data mais seulement le type MIME souhaité pour le format
de la réponse.
La catégorie, quant à elle, apporte une classification à l’action. La catégorie
Intent.CATEGORY_LAUNCHER positionne l’activité comme étant exécutable,
cette catégorie est utilisée de pair avec l’action Intent.ACTION_MAIN. Android
positionne les activités de cette catégorie dans le lanceur d’applications. La catégorie
CATEGORY_HOME marque l’activité à afficher au démarrage du téléphone.
L’objet Intent possède également d’autres attributs tels que les flags et les extras. Ces
attributs sont d’autres possibilités de transmission d’informations à l’activité appelée.
Ces attributs sont néanmoins accessoires dans la mesure où ils n’entrent pas en compte
dans le processus de recherche de l’activité cible.
5
Les intentions sont invoquées avec des options suivantes :
from: http://code.google.com/android/reference/android/content/Intent.html
startActivity (myActivity);
6
Examples de pairs action/data sont :
from: http://code.google.com/android/reference/android/content/Intent.html
7
Pour la liste des actions voir:
http://developer.android.com/reference/android/content/Intent.html
8
ACTIONS INTÉGRÉES STANDARD BROADCAST
Liste des actions standard que les intentions peuvent utiliser pour la réception des Broadcast
(généralement par registerReceiver (BroadcastReceiver, IntentFilter) ou une balise
<receiver>dans le manifeste) :
ACTION_TIME_TICK
ACTION_TIME_CHANGED
ACTION_TIMEZONE_CHANGED
ACTION_BOOT_COMPLETED
ACTION_PACKAGE_ADDED
ACTION_PACKAGE_CHANGED
ACTION PACKAGE REMOVED
ACTION_PACKAGE_ACTION_UID_REMOVED
ACTION_BATTERY_CHANGED
9
Tag XML :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/label1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#ff0000cc"
android:text="@string/lb"
android:textSize="20sp"
android:textStyle="bold" />
<EditText
android:id="@+id/text1"
android:layout_width="fill_parent"
android:layout_height="54dp"
android:text="tel:5554-20123456"
android:inputType="text" />
<Button
android:id="@+id/btnPickContact"
android:layout_width="149dp"
android:layout_height="wrap_content"
android:text="@string/bt" >
</Button>
</LinearLayout>
10
La classe MainActivity.java
package tn.rnu.isi.intentdemo1;
import android.os.Bundle;
import android.app.Activity;
import android.view.View.OnClickListener;
import android.content.Intent;
import android.net.Uri;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
setContentView(R.layout.activity_main);
label1 = (TextView) findViewById(R.id.label1);
text1 = (EditText) findViewById(R.id.text1);
btnCallActivity2 = (Button)
findViewById(R.id.btnPickContact);
btnCallActivity2.setOnClickListener(new ClickHandler());
} catch (Exception e) {
Toast.makeText(getBaseContext(), e.getMessage(),
Toast.LENGTH_LONG).show();
}
}
} catch (Exception e) {
label1.setText(e.getMessage());
}
}// onClick
}// ClickHandler
}// MainActivity
11
1- Appeler immédiatement
12
5- Faire une recherche sur Google à la recherche des associations tunisiennes
Modifier l' Exemple complet 1 en remplaçant la méthode "clickHandler" par le code
suivant :
intent.putExtra(SearchManager.QUERY,
"associations tunisiennes ");
startActivity(intent);
Data Secondaire
13
8- Voir une page web
Modifier l' Exemple complet 1 en remplaçant la méthode
"clickHandler" par le code suivant :
14
12- Envoyer Email
Modifier l' Exemple complet 1 en remplaçant la méthode "clickHandler" par le code
suivant :
15
Démarrage des activités et obtenir des résultats
La méthode startActivity (intention) est utilisée pour démarrer une nouvelle activité, qui sera
placé au sommet de la pile de l'activité. L’appelant continue, cependant, à exécuter dans son
propre thread.
Parfois, vous voulez obtenir un résultat en retour de la dite sous-activité lorsqu'elle se
termine.
Par exemple, vous pouvez démarrer une activité qui permet à l'utilisateur de choisir une
personne à partir d'une liste de contacts; lorsqu'elle se termine, elle retourne la personne qui a
été sélectionné.
Afin d'obtenir des résultats retour de la dite activité, nous utilisons la méthode :
startActivityForResult ( Intent, requestCodeID )
Où requestCodeID est une valeur arbitraire que vous choisissez pour identifier l'appel.
Le résultat envoyé par la sous-activité pourrait être repris par le Listener comme méthode
asynchrone.
onActivityResult (requestCodeID, resultCode, Intent )
Avant qu’une activité invoquée, quitte ou sort, elle peut appeler SetResult(resultCode) pour
renvoyer un signal de terminaison à l'activité parente.
• Il est commode de fournir un code de résultat, qui peut être des résultats standard :
Activity.RESULT_CANCELED, Activity.RESULT_OK, ou des valeurs personnalisés.
• Toutes ces informations peuvent être renvoyées au parent avec la méthode :
onActivityResult (int requestCodeID, int resultCode, Intention data)
• Si une activité enfant échoue pour une raison quelconque (comme un crash), l'activité
parente recevra un résultat avec le code RESULT_CANCELED.
16
Exemple complet 2:
1. Afficher tous les contacts et en choisir un particulier (Intent. ACTION_PICK).
2. Pour une interaction réussie, l'activité principale accepte l'URI renvoyé identifiant la
personne que nous voulons appeler (contenu :/ / contacts / people / n).
3. Afficher l'entrée du contact sélectionné permettant de faire des appels, envoyer des SMS,
emailing (Intent.ACTION_VIEW).
17
Intent.ACTION_PICK
2
Activity‐1 Activité-2 Intégrée
Uri des Contacts
(Afficher de la liste des
contacts)
Activité Appeler
Principale de Intent.ACTION_VIEW Envoyer SMS
l’User Activité-3 Intégrée Envoyer E-mail
(Afficher le contact
sélectionné)
Tag XML :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/label1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#ff0000cc"
android:text="Activité Intent"
android:textSize="20sp"
android:textStyle="bold" />
<EditText
android:id="@+id/text1"
android:layout_width="fill_parent"
android:layout_height="54px"
android:text="content://contacts/people/"
android:textSize="18sp" />
<Button
android:id="@+id/btnPickContact"
android:layout_width="149px"
android:layout_height="wrap_content"
android:text="Sélectionner Contact" >
</Button>
</LinearLayout>
18
La classe IntentDemo2.java
package tn.rnu.isi.intentdemo2;
import android.os.Bundle;
import android.app.Activity;
import android.view.View.OnClickListener;
import android.content.Intent;
import android.net.Uri;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
setContentView(R.layout.activity_intent_demo2);
label1 = (TextView) findViewById(R.id.label1);
text1 = (EditText) findViewById(R.id.text1);
btnCallActivity2 = (Button) findViewById(R.id.btnPickContact);
btnCallActivity2.setOnClickListener(new ClickHandler());
} catch (Exception e) {
Toast.makeText(getBaseContext(), e.getMessage(), Toast.LENGTH_LONG)
.show();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
super.onActivityResult(requestCode, resultCode, data);
try {
//utiliser requestCode pour savoir qui va communiquer, de nouveau
// avec nous
switch (requestCode) {
case (222): {
// 222 C'est notre activité amicale contact sélecteur
if (resultCode == Activity.RESULT_OK) {
String selectedContact = data.getDataString();
// On retourne un URI qui ressemble à:
// content://contacts/people/n
// où n est l’ID du contact sélectionné
label1.setText(selectedContact.toString());
19
// montre unécran avec le contact sélectionné
Intent myAct3 = new Intent(Intent.ACTION_VIEW,
Uri.parse(selectedContact));
startActivity(myAct3);
} else {
// utilisateur a appuyé sur la touche BACK
label1.setText("Selection CANCELLED " + requestCode + " "
+ resultCode);
}
break;
}
}// switch
} catch (Exception e) {
Toast.makeText(getBaseContext(), e.getMessage(),
Toast.LENGTH_LONG)
.show();
}
}// onActivityResult
}// IntentDemo2
20
Exemple complet 3:
Affichage des photos et vidéo - Appel d’une sous-activité, la réception des résultats.
La classe IntentDemo3.java
package tn.rnu.isi.intentdemo3;
import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.widget.Toast;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_intent_demo3);
showVideosImages();
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
if ((requestCode == 0) && (resultCode == Activity.RESULT_OK)) {
String selectedImage = intent.getDataString();
Toast.makeText(this, selectedImage, 1).show();
Intent myAct3 = new Intent(Intent.ACTION_VIEW,
Uri.parse(selectedImage));
startActivity(myAct3);
}
}// onActivityResult
} // IntentDemo3
21
Exemple complet 4:
Affichage / Lecture de pistes sonores - Appel d’une sous-activité, la réception des résultats.
La classe IntentDemo4.java
package tn.rnu.isi.intentdemo3;
import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.widget.Toast;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_intent_demo4);
showSoundTracks();
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
if ((requestCode == 0) && (resultCode == Activity.RESULT_OK)) {
String selectedTrack = intent.getDataString();
Toast.makeText(this, selectedTrack, 1).show();
Intent myAct3 = new Intent(Intent.ACTION_VIEW,
Uri.parse(selectedTrack));
startActivity(myAct3);
}
}// onActivityResult
}//IntentDemo4
22
Retenons
Une activité présente habituellement une seule interface utilisateur visuelle à partir de
laquelle un certain nombre d'actions pourrait être effectué.
Le passage d'une activité à l'autre est accompli par avoir l'activité courante lancer la
suivante à travers des intents.
1
Inter-Process Communication
23
24
Android Bundles http://developer.android.com/reference/android/os/Bundle.html
Example des Méthodes publiques
void clear()
Removes all elements from the mapping of this Bundle.
Object clone()
Clones the current Bundle.
boolean containsKey(String key)
Returns true if the given key is contained in the mapping of this Bundle.
void putIntArray(String key, int[] value)
Inserts an int array value into the mapping of this Bundle, replacing any
existing value for the given key.
void putString(String key, String value)
Inserts a String value into the mapping of this Bundle, replacing any existing
value for the given key.
void putStringArray(String key, String[] value)
Inserts a String array value into the mapping of this Bundle, replacing any
existing value for the given key.
void putStringArrayList(String key, ArrayList<String> value)
Inserts an ArrayList value into the mapping of this Bundle, replacing any
existing value for the given key.
void remove(String key)
Removes any entry with the given key from the mapping of this Bundle.
int size()
Returns the number of mappings contained in this Bundle.
25
Exemple complet 5:
Activity1 recueille deux valeurs de son interface utilisateur et appelle Activity2 pour calculer
la somme d'eux. Le résultat est renvoyé de l'Activity 2 à Activity1.
26
Etape 2. Créer GUI pour Activity2 (main2.xml)
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#ff0000ff"
android:text="Activity2"
android:textSize="22sp" />
<EditText
android:id="@+id/etDataReceived"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Donnée reçu..." />
<Button
android:id="@+id/btnDone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Fait ‐ Callback" />
</LinearLayout>
Etape 3. Dans l’Activity1 : Après avoir cliqué sur le bouton des données, de l'interface
utilisateur est placé dans un paquet et envoyé à Activity2. Un listener reste attentif attendre des
résultats à venir de l'activité appelée.
27
package tn.rnu.isi.intentbundledemo;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main1);
txtVal1 = (EditText) findViewById(R.id.EditText01);
txtVal2 = (EditText) findViewById(R.id.EditText02);
lblResult = (TextView) findViewById(R.id.TextView01);
btnAdd = (Button) findViewById(R.id.btnAdd);
btnAdd.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// obtenir des valeurs de l'interface utilisateur
Double v1 = Double.parseDouble(txtVal1.getText().toString());
Double v2 = Double.parseDouble(txtVal2.getText().toString());
////////////////////////////////////////////////////////////////////////////
28
// local listener recevant callbacks (rappels) des autres activités
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
try {
if ((requestCode == 101) && (resultCode ==
Activity.RESULT_OK)) {
Bundle myResults = data.getExtras();
Double vresult = myResults.getDouble("vresult");
lblResult.setText("La somme est " + vresult);
}
} catch (Exception e) {
lblResult.setText("Problèmes :" +
requestCode +" " +resultCode);
}
}// onActivityResult
}// Activity1
Etape 4. Activity2 . Appelée à partir Activity1. Extrait des données d'entrée du paquet attaché
à l'intent. Effectue le calcul local. Ajoute le résultat bundle. Retourne signal OK.
package tn.rnu.isi.intentbundledemo;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main2);
29
// opérer sur les données d'entrée
Double vResult = v1 + v2;
// à titre d'illustration. Afficher les données reçues et le résultat
dataReceived.setText("Données reçues : \n" + "val1= " + v1 + "\nval2= "
+ v2 + "\n\nresult= " + vResult);
// ajouter au paquet le résultat calculé
myBundle.putDouble("vresult", vResult);
// oindre bourdon mis à jour pour invoquer l'intention
myLocalIntent.putExtras(myBundle);
// renvoyer en envoyant un signal OK pour appeler l'activité
setResult(Activity.RESULT_OK, myLocalIntent);
// finish();
}// onCreate
@Override
public void onClick(View v) {
//fermer l'écran en cours - fin Activity2
finish();
}// onClick
}// Activity2
Exemple 6:
Activity1 invoque Activity2 l'aide d'une intention. Un paquet contenant un ensemble de
données de différents types, il est envoyé en va-et-vient entre les deux activités
Le code Java complet et les tags XML de cet exemple se trouvent dans l’atelier
« IntentBundleSetDataType » . Nous allons juste présenter le fragment du code relatif au
bundle le PUT et le GET :
Remplir le Bundle :
private class Clicker1 implements OnClickListener {
public void onClick(View v) {
try {
// créer un Intent pour communiquer avec Activity2
Intent myIntentA1A2 = new Intent(Activity1.this,Activity2.class);
30
// preparer un Bundle et ajouter les parties data à envoyer
Bundle myData = new Bundle();
myData.putInt("myRequestCode", IPC_ID);
myData.putString("myString1", "Hello Android");
myData.putDouble("myDouble1", 3.141592);
int[] myLittleArray = { 1, 2, 3 };
myData.putIntArray("myIntArray1", myLittleArray);
// Lier le Bundle et l'Intent qui communique avec Activity2
myIntentA1A2.putExtras(myData);
// Appeler Activity2 et attendre les resultats
startActivityForResult(myIntentA1A2, IPC_ID);
}
catch (Exception e) {
Toast.makeText(getBaseContext(), e.getMessage(),
Toast.LENGTH_LONG).show();
}
}// onClick
}// Clicker1
Extraire du Bundle :
....
// créer un Intent local – nous avons été appelés!
myLocalIntent = getIntent();
// récupérer l'ensemble des données avec toutes les pièces envoyées à nous
Bundle myBundle = myLocalIntent.getExtras();
// extraire les parties individuel (data) du bundle
int int1 = myBundle.getInt("myRequestCode");
String str1 = myBundle.getString("myString1");
double dob1 = myBundle.getDouble("myDouble1");
int[] arr1 = myBundle.getIntArray("myIntArray1");
....
31
Etape 2. Modifier Activity1. Créer une instance de la classe Person et ajouter la au bundle en
utilisant la méthode putSerializable (key, object);
myIntentA1A2.putExtras(myData);
// Appeler Activity2 et attendre les resultats
startActivityForResult(myIntentA1A2, IPC_ID);
32
4.3.2 La résolution
La résolution est le mécanisme de détermination de la ou des activités aptes à gérer
l’action exprimée par l’intention. L’algorithme repose sur la confrontation de l’objet
Intent et les IntentFilter des activités présentes sur le système Android.
Un IntentFilter est un objet rattaché à une activité par lequel cette dernière informe
publiquement de ses capacités. Cette déclaration est réalisée comme on peut s’en
douter dans le fichier manifeste.
Une activité peut définir un ou plusieurs IntentFilter, chacun étant une fonction que l’activité
peut remplir. Pour être sélectionnée, une activité devra avoir un IntentFilter remplissant à
lui seul entièrement le contrat matérialisé par l’Intent.
L’extrait suivant du manifeste déclare un unique IntentFilter pour l’activité Intent-
TesterActivity. Celui-ci possède trois actions, une catégorie et une entrée data avec le type
MIME d’indiqué :
<activity
android:name=".IntentTesterActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.DELETE" />
<category android:name="android.intent.category.LAUNCHER" />
<data android:mimeType="vidéo/mpeg" />
</intent-filter>
</activity>
La résolution des intentions peut se schématiser comme on le voit sur la figure 5.1.
En détail, l’algorithme compare un à un les trois attributs majeurs des intentions et des
IntentFilters que sont les actions, les data/type et les catégories.
Si la comparaison échoue sur un de ces points, la fonctionnalité représentée par l’IntentFilter
sera écartée pour incompatibilité.
Pour être valide par rapport à l’Intent, l’IntentFilter devra avoir dans sa liste des actions ,
celle qui est spécifiée dans l’Intent.
Si l’Intent n’en mentionne aucune, ce premier test sera validé à condition que
l’IntentFilter ait au moins une action.
Vient ensuite le test des catégories. Toutes les catégories référencées dans l’Intent devront
être présentes dans la déclaration de l’IntentFilter.
Bien sûr, l’IntentFilter pourra en définir d’autres encore ; Qui peut le plus peut le moins.
Toutefois, les IntentFilter devront en plus mentionner la catégorie
Intent.CATEGORY_DEFAULT car les activités lancées par la méthode
Context.startActivity sont tenues d’avoir cette catégorie.
Enfin, le test portera sur la nature des données. La logique est toujours la même : l’URI
et/ou le type MIME de l’Intent doit figurer dans la liste des objets traitables par l’activité.
33
L’IntentFilter peut recourir à l’usage de « wildcards ». Ainsi dans l’exemple de déclaration
précédent, le type MIME supporté par l’activité est « vidéo/mpeg ». Cela veut dire qu’un
Intent référençant une vidéo au format mov, dont le type MIME est « vidéo/quicktime » ne
pourra pas être passé à cette activité.
34
Par contre, si l’IntentFilter avait déclaré comme type MIME « vidéo/* », cela aurait convenu. À l’instar du type,
l’URI peut n’être définie qu’en partie au niveau du filtre :
<data android:mimeType="vidéo/*"
android:scheme="http"/>
Et celle-ci toutes les vidéos délivrées par youtube. Plus l’URI est précisée finement dans l’IntentFilter, plus
la contrainte de sélection sera forte.
Ci-dessous, un exemple de code déclenchant l’affichage du dialer avec un numéro prérempli :
private void startDialActivity(){
Intent dial = new Intent();
dial.setAction(Intent.ACTION_DIAL);
dial.setData(Uri.parse("tel:1234567"));
startActivity(dial);
}
Le dialer est une activité native d’Android, l’exemple montre clairement que les activités peuvent
s’enchaîner sans devoir nécessairement appartenir à la même application.
Du point de vue système bas niveau, il est à souligner que l’activité dialer tourne dans son propre
processus et non pas dans celui de l’activité appelante.
Si on s’amuse à créer une activité réagissant à l’action Intent.ACTION_DIAL et à l’enregistrer tel quel dans
le système par la définition d’un IntentFilter ad hoc :
Alors, à l’exécution du code précédent, comme deux activités présentes sur Android seront capables de
recevoir l’objet Intent, une boîte de dialogue demandant à l’utilisateur de trancher apparaîtra :
35
La résolution implicite est une fonctionnalité très puissante. Cependant, il est tout à fait légitime de vouloir
appeler une activité précise et avoir l’assurance qu’aucune autre activité ne pourra intervenir à la place.
Ceci arrive d’autant plus fréquemment que les activités à enchaîner se trouvent au sein de la même
application.
Pour cela, la construction de l’objet Intent doit se faire en nommant l’activité au travers des méthodes
setComponent, setClassName ou setClass :
En sens inverse, lorsque B aura achevé son traitement, il aura sans doute des informations à fournir à A pour
rendre compte des opérations effectuées, en tout cas au minimum pour annoncer si le traitement s’est soldé par
un succès ou un échec.
Si l’activité A a démarré l’activité B par la méthode startActivity(Intent), la communication retour ne sera pas
possible. A ne sera même pas alertée de la fin de l’activité B.
Par recevoir cette notification, accompagnée éventuellement de données complémentaires, l’activité devra être
amorcée par la méthode startActivityForResult(lntent, int).
L’entier passé en plus de l’lntent est un simple code qui sera renvoyé tel quel à l’activité A lors du retour, lui
permettant d’identifier l’activité venant de s’achever.
La valeur de l’entier peut être librement choisie par l’activité émettant l’lntent, ce code est indispensable lorsque
cette activité en lance plusieurs autres car les notifications de fin sont toutes acheminées par le même canal, par
la méthode onActivityResult, il est donc crucial de pouvoir distinguer la provenance du message de retour.
La figure en bas, montre un scénario complet d’une collaboration entre deux activités. Le point de départ est le
lancement de l’lntent et le point d’arrivée est la réception de la notification de retour par la première activité.
Les flèches en pointillés reliant les deux activités indiquent clairement que le couplage entre les activités est
faible : aucune des deux ne connaît l’autre, elles communiquent par messages interposés acheminés par le
framework d’Android.
Avec la définition de la constante suivante (qui ne requiert pas de visibilité de type public) :
private static final int TRUQUEE_REQUEST_CODE = 0;
L’étape (2) est prise en charge par Android et non implémentée par du code applicatif. Ensuite, en (3) et (4),
dans la méthode onCreate de l’activité B, on aurait :
Intent intent = getIntent();
//...
//intent.getData();
//traitement...
//intent.setData(data);
//...
setResult(RESULT_OK, intent);
finish();
36
Enfin, la méthode onActivityResult de A s’exécute avec les informations de résultat en paramètre :
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
if (requestCode == TRUQUEE_REQUEST_CODE) {
37