Cette présentation a été donnée à la Devoxx France 2012 par Android2EE - Mathias Seguy en tant qu'University (3h).
L'objectif est de présenter l'ensemble des notions et des bonnes pratiques élémentaires de ce type de développement. Elle démystifie ce type de programmation et permet aux développeurs de se lancer dans la programmation Android en ayant en tête une vision globale du système et de son fonctionnement. L'idée est de commencer par Android et le monde de l'IT puis de partir d'un projet minimaliste possédant une activité et d'élargir la vision : Un projet simple, Ressource, Traitement (Handler, AsyncTask), DataBase, ContentProvider, SharedPreference, Service, WebView, Utilisation d'un service Rest, les capteurs, Quelques Patterns de programmations, Problématique des tests, Gestion de projet (Junit, Maven, Hudson).
Cette présentation est une présentation d’Android2EE, entreprise spécialisée dans l’expertise, le consulting et la formation Android. Vous souhaitez apprendre la programmation Android, Android2EE vous accompagne au-travers ses EBooks ou ses formations.
Laissez moi vous raconter mon histoire concernant cette entreprise. En 2010, je me suis penché sur la technologie Android, j'ai écrit trois EBooks sur l'art de la programmation Android (disponible sur Android2EE). J'ai quitté ma précédente entreprise et j’ai créé l'entreprise Android2EE (Android To Enterprise Edition) dont l'objectif est la formation, l’expertise et le consulting Android. J’ai quelques contrats en tant que consultant et aussi en tant que formateur. J'ai monté un ensemble de formations Android pour les entreprises:
Formation Initiale : Devenir autonome (3j).
Formation Approfondissement (2j).
Formation Spécificité Tablette et Graphisme(2j).
Formation Applications complexes (2j).
Formation chef de projet - responsable technique (1j).
Formation Mutlimédia (2j).
Formation sur mesure (2 à 3j).
Je pratique ces formations en donnant des cours dans les universités et les écoles d'ingénieurs de Toulouse ainsi que dans de grandes conférences Java:
CESI-EXIA, EPITECH, Sup-Info, IUP ISI, InfoSup, IUP SI, IUP ISI, IngeSup, INSA
Et bien sûr: Devoxx France (LA conférence Java française de 2012), Mercredi 18 Avril 2012,
Et
JCertif Afrique 2012 (LA conférence Java du continent Africain) Septembre 2012 Conférence et Cours
Enfin, j’ai investi l’espace numérique, voici quelques uns des ces sites:
http://www.android2ee.com/
http://mathias-seguy.developpez.com/
http://blog.developpez.com/android2ee-mathias-seguy/
http://android2ee.blogspot.com/
La critique de mon livre « Android A Complete course » par developpez.com (dont je ne suis pas peu fier, quand même):
http://android.developpez.com/livres/#L9791090388000
J'ai des articles sur Developpez.com concernant Android.
Ah oui, j'oubliais, mon cv se trouve ici (si jamais) : http://mathias-seguy.developpez.com/MathiasSeguyCV201106/
2. •Android et le monde de l’IT
Android et le monde
de l’IT
2
3. •Android et le monde de l’IT
•Mettre en place une activité
oLes acteurs du système : Activity –
ContentProvider – Service
oLa structure d’un projet Android
oLes IHM
Mettre en place une
Activité
3
4. •Android et le monde de l’IT
•Mettre en place une activité
•Autour de l’activité :
oLes Intents
oLes traitements : Thread et AsyncTask
oLa gestion des ressources
Autour de
l’activité
Mettre en place une
Activité
4
5. •Android et le monde de l’IT
•Mettre en place une activité
•Autour de l’activité
•Autres éléments d’une application:
oApplication Autres éléments de
l’application
oPermissions
oPersistance des données Autour de
l’activité
oService
Mettre en place une
Activité
5
6. •Android et le monde de l’IT
•Mettre en place une activité
Communiquer avec le
•Autour de l’activité système
•Autres éléments d’une application
•Communiquer avec le système: Autres éléments de
l’application
oInternet
oCapteur Autour de
l’activité
Mettre en place une
Activité
6
7. •Android et le monde de l’IT
Autour du projet
•Mettre en place une activité
Communiquer avec le
•Autour de l’activité système
•Autres éléments d’une application
•Communiquer avec le système Android et le monde
Autres éléments de
•Autour du projet de l’IT
l’application
oSigner et Déployer Autour de
l’activité
oStratégies de tests
Mettre en place une
Activité
7
8. Formateur Consultant Expert Android
mathias.seguy.it@gmail.com (mail)
@android2ee (twitter)
Auteur d’EBooks sur la programmation Android (Android2ee.com)
Conférencier et Enseignant Android
Docteur en Mathématiques Fondamentales
Expert Technique de l'Agence Nationale de la Recherche
Rédacteur sur Developpez.com
Blogs Android : Android2EE sur DVP et Android2ee sur BlogSpot
Doctorat Mathématiques
Fondamentales Via CapGemini Via Sopra Naissance Android2EE
Siemens Magnus DGA CNES Airbus Airbus Airbus STI Android2EE
VDO
03 04 05 06 07 08 09 10 11 12
Java J2EE
Android
Manager Manager
Leader Leader Leader Leader Directeur Fondateur
Développeur Développeur IHM Technique Technique Manager Technique Technique Android2EE
Master Informatique de Chez STI
l’ENSEEIHT
8
10. Invasion de la mobilité via les
smartphones:
Le taux de pénétration des
smartphones est exceptionnel, tant
part sa valeur actuelle que par la
rapidité de sa progression.
•486M de smartphones vendus en
2011.
•27% du parc mondial.
Android, l’OS Winner !
En quatre ans, Android est devenu
leader mondial du système
d’exploitation des smartphones, en
terme de stock et en terme de vente.
10
11. GooglePlay (AndroidMarket) versus AppleStore,
la guerre du Market.
Le nombre d’applications sur GooglePlay est en hausse permanente.
• + 17 391 % en 3 ans
• + 320 % en 1 an
Le nombre d’applications téléchargées a pour unité le milliard.
•+500% sur 1 an
•10 000 000 000 de downloads !
Cette dynamique est la clef de réussite de la plateforme, le facteur
d’adoption.
11
12. La guerre des brevets fait rage dans les grands comptes de l’IT. Depuis l’été 2011,
cette guerre fait des ravages dans le monde entier. Le système Android est attaqué
par le trio Microsoft-Apple-Oracle et se défend:
Apple versus Android : La guerre froide
Ces deux-là se mènent une guerre non frontale: Apple attaque les constructeurs et
Google rachète Motorola pour 8.6B$.
Apple versus HTC et Samsung: La guerre des tribunaux.
Interdiction du GalaxyTab en Australie, plainte en Europe et aux Etats Unis… (Depuis
peu demande un accord « amiable » entre 5 et 15$ par unité).
Microsoft versus Android: La guerre des gangs.
Microsoft a su imposer une redevance (5$) à Samsung et à HTC pour chaque unité
Android vendue.
Oracle Versus Android : l’open-guerre
Quand l’open source fait la guerre à l’open source…
Plainte déposée par Oracle en 2010 (le procès
s’ouvre cette semaine… à suivre).
Vidyanand Kamat/Forbes India
12
15. •Les règles
•Les acteurs du système : Activity, Service, ContentProvider et Intent
•La structure d’un projet :
•Vision globale
•Le fichier AndroidManifest.xml qui décrit votre application
•Le fichier layoutmain.xml qui décrit l’IHM de votre activité
•Le fichier resvaluesstring.xml qui décrit les chaines de caractère de votre application
•L’activité
•Le test avec l’émulateur
15
16. Respecter ses données,
Respecter sa confidentialité.
Respecter le CPU,
Respecter la batterie,
Respecter la mémoire.
Respecter la charte graphique Android,
Respecter les bonnes pratiques du système.
…
16
17. : Ce sont les programmes vu par l’utilisateur. Ils : Les services ont une durée de vie potentiellement infinie
possèdent une I.H.M. (contrairement aux activités et aux fournisseurs de données). Ce
sont des processus qui tournent dans la même Thread que
: Ils offrent un niveau d’abstraction pour l’application sans IHM.
l’accès à toutes les données stockées sur le terminal. Les : Ce sont des messages systèmes qui servent de support
données sont identifiées au moyen d’URI (Unified Ressources événementiel pour permettre le dialogue entre applications. On
Identifier). répond et on envoie des intentions qui lancent ou communiquent
avec les activités.
Listen Service
Listen
BroadCastReceiver Bind
Activity
StartActivity ContentResolver.action(URI,…)
CRUD
operation
return
URI
ContentProvider
Send
Intent Find ContentProvider
17
18. Un projet Android simple contient les dossiers:
pour les sources Java
pour les sources générées à partir du dossier res
pour les dossiers de ressources:
pour les images
pour la définition des composants graphiques
pour les chaînes de caractères
ainsi que le fichier qui est le centre névralgique de votre application. Il
décrit les besoins de votre application, en termes de SDK, de compatibilité matérielle, d’API
utilisées et d’utilisation de services du système. Il décrit ce qu’offre votre application au
système (Activity, ContentProvider, Service…), et les éléments auxquels votre application
réagit au moyen des IntentFilters et d’URI ainsi que les permissions nécessaire pour les
utiliser.
18
19. Le fichier AndroidManifest.xml décrit votre application: <?xml version=“1.0“ encoding=“utf-8“?>
• son package, <manifest xmlns:android=http://schemas.android.com/apk/res/android
• son nom, package= “ com.android2ee.android.tuto.gui“
• sa version, android:versionCode=“1“
• son icône,
android:versionName=“1.0“>
• son label,
<application android:icon=“@drawable/icon“
• la liste des activités qu’elle possède et les intentions
android:label=“@string/app_name“>
auxquelles chacune répond (ici l’Intent démarrage),
<activity android:name=“.RadioButtonTuto“
• ainsi que la version minimale du système nécessaire à
android:label=“@string/app_name“>
la faire fonctionner.
<intent-filter>
<action android:name=“android.intent.action.MAIN“ />
Il peut aussi déclarer:
• les services et les content provider qu’elle contient, <category android:name=“android.intent.category.LAUNCHER“ />
• les permissions dont elle a besoin et celles qu’elle </intent-filter>
procure. </activity>
</application>
Enfin, il peut déclarer les éléments matériels dont elle a <uses-sdk android:minSdkVersion=“8“ />
besoin. </manifest>
19
20. Le fichier des Layouts : layoutmain.xml
Ce fichier décrit les composants graphiques de votre activité. <LinearLayout xmlns:android=http://schemas.android.com/apk/res/android
Ils se placent dans un layout et définissent les propriétés android:orientation=“vertical“
suivantes (en fonction du besoin): android:layout_width=“fill_parent“
android:layout_height=“fill_parent“ >
• La manière dont ils remplissent l’espace <TextView android:layout_width=“fill_parent“
(android:layout_width, android:layout_height) android:layout_height=“wrap_content“
• Un identifiant pour pouvoir être récupéré dans le code android:text=“@string/like“/>
java (android:id) <CheckBox android:id=“@+id/checkchocolate“
• Le texte affiché (android:text) android:layout_width=“wrap_content“
• Le poids du composant (android:layout_weight) android:layout_height=“wrap_content“
android:text=“@string/chocolate“ />
Ce fichier est interprété par l’activité pour construire son écran
<Button android:id=“@+id/btnShow“
via la méthode :
android:layout_width=“fill_parent“
setContentView(R.layout.main);
android:layout_height=“wrap_content“
android:text=“@string/show“/>
Un composant se récupère dans le code par la méthode:
</LinearLayout>
btn=(Button)findViewById(R.id.btnShow);
20
21. Le fichier des chaines de caractères: resvaluesstring.xml valuesstring.xml
<resources>
<string name=“hello“>Hello World, CheckBoxTuto!</string>
Ce fichier décrit les chaines de caractères utilisables par votre activité. <string name=“app_name“>CheckBoxTuto</string>
<string name=“like“>Do you like :</string>
Chaque chaine est identifiable par un nom et possède une valeur. <string name=“choice“>Your choice is :</string>
<string name=“chocolate“>Chocolate</string>
<string name=“nicotine“>Nicotine</string>
Pour lier cette chaine à un composant dans le fichier des layout: <string name=“hug“>Hug</string>
android:text=“@string/like“ <string name=“santaclaus“>Santa Claus</string>
</resources>
Pour utiliser cette chaine dans le code Java:
String hello = getString(R.string.hello) ; values-frstring.xml
<resources>
<string name=“hello“>Bonjour, CheckBoxTuto!</string>
Mais surtout ces fichiers sont internationalisables de manière
<string name=“app_name“>CheckBoxTuto</string>
transparente: <string name=“like“>Aimez-vous:</string>
• Le fichier valuesstring.xml contient les chaines de caractères à <string name=“choice“>Votre choix est:</string>
utiliser par défaut, <string name=“chocolate“>Chocolat</string>
<string name=“television“>Télévision</string>
• Le fichier values-frstring.xml contient les chaines de caractères <string name=“internet“>Internet</string>
à utiliser quand la locale de l’appareil est le français, <string name=“nicotine“>Nicotine</string>
• Et ainsi de suite. <string name=“hug“>Câlins</string>
<string name=“santaclaus“>Le père Noël</string>
</resources>
22. Le code Java d’une activité ressemble à du code Java des plus naturels. public class MainActivity extends Activity implements
Cela ressemble beaucoup à du Swing : View.OnClickListener {
• Les composants principaux sont naturels. /** * A button */
• Ils possèdent les méthodes usuelles des composants (setText, private Button btn;
getText, setOnClickListener, hasFocus, isPressed, isSelected, /** * A dummy counter */
setBackground, setTextColor, ...) private int count;
/** Called when the activity is first created. */
Les différences notables sont:
@Override
public void onCreate(Bundle savedInstanceState) {
• L’agencement des composants et leur définition se fait en XML.
super.onCreate(savedInstanceState);
//Récupère le fichier main.xml pour construire l’IHM
• L’IHM de l’activité se construit par l’appel de la méthode
setContentView(R.layout.main);
setContentView(R.layout.main) où main est le nom de votre fichier
//Instancie le bouton en récupérant son Id,
reslayoutmain.xml.
//on peut ensuite utiliser le bouton normalement.
• Ils se récupèrent via la méthode findViewById en utilisant leur btn=(Button)findViewById(R.id.button);
identifiant. btn.setOnClickListener(this);
btn.setText(“Hello“);
• Votre activité étend la classe Activity. }
public void onClick(View view) {
count++; btn.setText(“Hello “+count);}
}
22
23. Le cycle de vie d’une activité est une notion fondamentale d’Android.
Le système, pour des raisons de priorisation d’activités (coup de
téléphone), peut tuer une activité quand il a besoin de ressources.
Une activité possède 4 états :
• Active. L’activité est lancée par l’utilisateur, s’exécute au
premier plan.
• En Pause. L’activité est lancée par l’utilisateur, elle s’exécute
et est visible mais elle n’est plus au premier plan. Une
notification ou une autre activité lui a volé le focus et une partie
du premier plan.
• Stoppée. L’activité à été lancée par l’utilisateur mais n’est
plus au premier plan et est invisible. L’activité ne peut interagir
avec l’utilisateur qu’avec une notification.
• Morte. L’activité n’est pas lancée. http://developer.android.
com/reference/android/
app/Activity.html
23
24. Et le projet se teste en utilisant l’émulateur fournit par Google qui s’intègre parfaitement avec l’IDE Eclipse:
Clic droit sur le projet->Run As-> Android Application
Bien sûr, vous avez auparavant installé votre espace de travail et définit vos AVD (Android Virtual Device ).
24
26. •Les Intents
•Les traitements:
•Handler versus AsyncTask
•Handler
•AsyncTask
•Fuite mémoire
•Gestion des ressources:
•Les chaines de caractères
•Images, couleurs et thèmes
•L’arbre des caractéristiques possibles d’un appareil.
•La problématique du multi-screens et de la fragmentation.
26
27. Pour mettre en place cette communication entre activités, Android utilise le système des intentions. Une intention peut se traduire par
“ je veux, toi système, que tu fasses … “. Et cela dépend de l’action demandée et du contexte. Celles-ci permettent d’envoyer des
messages d’une activité vers une autre avec des données pour les activer.
Dans ce contexte, il faut se demander si l’activité mère quand elle lance une autre activité est dans une relation maître-esclave ou
dans une relation de pair à pair. Dans le premier cas, l’activité mère attend que l’activité fille finisse et est informée de cette fin.
Dans l’autre cas, l’activité fille est lancée, l’activité mère n’a plus de contact avec elle.
Une intention est ainsi une action associée à des données. Les actions sont des constantes : ACTION_VIEW, ACTION_EDIT,
ACTION_PICK, les données sont des URI.
Les intentions peuvent se spécifier ; on peut leur rajouter des informations :
• Une catégorie : LAUNCHER (pour les activités présentes dans le lanceur Android), DEFAULT ou ALTERNATIVE.
• Un type MIME : indique le type de la ressource (comme pour les mails)
• Un composant : indique la classe d’activité censée recevoir l’intention. Cette méthode n’est pas la plus heureuse, car elle fige
l’activité associée. Soit vous l’avez codée vous-même, dans ce cas, c’est naturel, sinon, l’utilisation des autres paramètres est
préconisée.
• Des “ extras “ : un bundle d’informations permettant de transférer des données à l’activité cible.
Exemple de déclaration d’une écoute d’intention: <intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
27
28. Pour utiliser les données renvoyées par l’activité appelée
Pour lancer une activité : protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//To launch your own activity
super.onActivityResult(requestCode, resultCode, data);
//First declare an intent
if (requestCode == INTENT_CALL_ID) {
final Intent intent = new Intent().setClass(this, OtherActivity.class);
// Launch the activity if (resultCode == Activity.RESULT_OK) {
startActivity(intent); Uri contactData = data.getData(); …
// Do the same for Geo* } else if (resultCode == Activity.RESULT_CANCELED) {…}
// Retrieve latitude and longitude }}
String latitude = “43.565715431592736“;
String longitude = “1.398482322692871“;
// Format the associated uri Pour renvoyer des informations à l’activité appelante, il faut utiliser
Uri uriGeo = Uri.parse(“geo:“ + latitude + “,“ + longitude); la méthode setResult (n’importe où dans le code).
// Declare the associated Intent JavaDoc:
final Intent intentGeo = new Intent(Intent.ACTION_VIEW, uriGeo);
// Launch the activity
public final void setResult (int resultCode, Intent data)
startActivity(intent);
Call this to set the result that your activity will return to its caller.
// Do the same for Contact waiting for result
Parameters
// Format the associated uri
resultCode The result code to propagate back to the originating activity, often
Uri uriContact = Uri.parse(“content://contacts/people“);
RESULT_CANCELED or RESULT_OK
// Declare the intent:
data The data to propagate back to the originating activity.
final Intent intentContactForResult = new Intent(Intent.ACTION_PICK,
See Also:RESULT_CANCELED, RESULT_OK, RESULT_FIRST_USER,
uriContact);
setResult(int).
// Launch the activity
startActivityForResult(intentContactForResult, INTENT_CALL_ID);
28
29. Si une activité ne réagit pas dans les 5 secondes, elle sera tuée par l’ActivityManager qui la considèrera comme morte.
Les IHMs de l’activité sont dans une Thread qui leur sont propres (comme en swing, l’EDT). Plusieurs façons d’interagir entre les
threads en arrière plan et la thread d’IHM sont possibles.
Pour résoudre ce problème il faut effectuer les traitements dans des threads indépendantes. Android met à votre disposition deux
moyens pour parvenir à ce découpage:
• Les Handlers
• Les AsyncTask
Ces deux classes permettent de lancer un traitement à partir de la thread d’IHM qui puisse communiquer avec elle.
La question est de savoir laquelle choisir dans quelle circonstance:
Si vous faites un traitement lourd, par lots, spécifique, qui n’a besoin que de donner son avancement et sa fin (typiquement
charger des données), le mieux est l’utilisation de l’AsyncTask.
Si vous avez besoin d’établir une communication avec l’IHM, asynchrone, il est plus pertinent d’utiliser un Handler (l’exemple de
la communication Bluetooth entre deux appareils utilise ce pattern).
Article DVP:
Thread, Handler, AsyncTask et fuites mémoires
29
30. Thread I.H.M /** Called method when launching the activity */
Autre Thread public void onStart() {
Activity Handler
Thread super.onStart();
sendMessage() handleMessage()
sendMessage() // initialize the progress bar
bar.setProgress(0);
message
// Thread definition, it could have been done in an external classes
Thread background = new Thread(new Runnable() {
/** The bundle exchanged within the message between the tread and the handler*/
Tout se passe dans la classe de votre activité:
Bundle messageBundle=new Bundle();
• Vous déclarez le Handler
• Vous déclarez la thread /*** The message exchanged between this thread and the handler*/
• Vous communiquez par messages Message myMessage;
// Overriden Run method
public void run() {
/*** The handler that manage the communication from external threads to
try { // Sleep one second
Gui's thread */
Handler handler = new Handler() { Thread.sleep(1000);
@Override //create a message, the best way is to use that method:
public void handleMessage(Message msg) { myMessage=handler.obtainMessage();
int progress=msg.getData().getInt(PROGRESS_BAR_INCREMENT); //then, if you want add data to your message (you could use arg0 and arg1 too)
// here, just increment the progress bar
messageBundle.putInt(PROGRESS_BAR_INCREMENT, i);
bar.incrementProgressBy(progress);
// You can do quick stuff that update the Gui myMessage.setData(messageBundle);
}}; // Handler declaration end. //then send the message
handler.sendMessage(myMessage);}}
catch (Throwable t) { // just end the background thread}}});
30
31. Pour mettre en place un AsynTask, il faut :
Créer une sous-classe d’AsyncTask (elle peut être interne et privée à l’activité).
Redéfinir une ou plusieurs de ces méthodes pour spécifier son travail.
La lancer au moyen de sa méthode execute
La dérivation d’une classe AsyncTask n’est pas triviale, elle est générique et à paramètres variables:
public class MyAsyncTask extends AsyncTask<Params, Progress, Result>{
protected Result doInBackground(Params... param) {}
protected void onProgressUpdate(Progress... progress) {}
protected void onPostExecute(Result result) {}}
Pour la généricité elle attend 3 paramètres :
Params : Le type de l’information qui est nécessaire au traitement (dans l’exemple )
Progress : Le type de l’information qui est passé à sa tache pour indiquer sa progression (dans l’exemple )
Result: Le type de l’information qui est passé au code lorsque la tâche est finie (dans l’exemple ).
Thread I.H.M. Other Thread
Usual communication
Activity MyAsyncTask extends AsyncTask
doInBackground()
onProgressUpdate() update
update
OnPostExecute()
31
32. class MyAsyncTask extends AsyncTask< , Integer, String> { public void onCreate(Bundle savedInstanceState) {
/** * A simple counter */ super.onCreate(savedInstanceState);
Integer count = 0; setContentView(R.layout.main);
// override of the method doInBackground (the one which is running in a separate thread) // Define the progress bar
@Override bar = (ProgressBar) findViewById(R.id.progress);
protected String doInBackground( ... unused) { bar.setMax(210);
try { // Launch the asynchTask
while (count < 20) { new MyAsyncTask().execute();
count++;//increment the counter }
Thread.sleep(1000);// Make a pause
publishProgress(count);//talk to the onProgressUpdate method
}
} catch (InterruptedException t) {
return (“The sleep operation failed“);// just end the background thread}
return (“return object when task is finished“);
}
// override of the onProgressUpdate method (runs in the GUI thread)
@Override
protected void onProgressUpdate(Integer... diff) {
bar.incrementProgressBy(diff[0]);
}
// override of the onPostExecute method (runs in the GUI thread)
@Override
protected void onPostExecute(String message) {
Toast.makeText(getApplicationContext(), message,Toast.LENGTH_SHORT).show();}}
}
32
33. Que se passe-t-il lorsque l’activité suit son cycle Thread I.H.M Thread de traitement
de vie et est détruite puis recréée ? Activity Handler
Typiquement une rotation de l’écran? Que sendMessage()
devient la Thread de traitement ?
message
Dans la plupart des cas, le développeur ne fait
pas attention et l’ancienne Thread n’est pas
OnDestroy()
détruite, une nouvelle Thread est mise en place.
OnCreate()
Le traitement peut rester cohérent (ou pas).
Thread de traitement initiale
La même chose se produit avec les Thread I.H.M sendMessage()
AsynchTask.
Alors quelle est la parade? Activity Handler
fantôme fantôme
• Accorder le cycle de vie de la thread à
celui de l’activité (la détruire dans Thread de traitement
onDestroy). Activity Handler
sendMessage()
• Sauvegarder la Thread pour le prochain
démarrage.
message
• Associer la Thread à un service et non
pas à une activité
33
34. protected void onCreate() {…
Thread I.H.M Thread de traitement backgroundThread = new Thread(new Runnable() {
Activity Handler /** * The message exchanged between this thread and the handler*/
sendMessage() Message myMessage;
public void run() {
while (isThreadRunnning.get()) {
message if (isThreadPausing.get()) {
OnDestroy() // When pausing just sleep 2 seconds
OnCreate() Thread.sleep(2000);
Thread I.H.M } else {// Do something
Thread de traitement
Thread.sleep(100);
Activity Handler myMessage = handler.obtainMessage();
sendMessage()
handler.sendMessage(myMessage);
}...
message // Initialize the thread Safe booleans
isThreadRunnning.set(true);
Avantages de la méthode:
//start the thread
• Est la méthode la plus sécurisée contre les fuites mémoire. backgroundThread.start();}
Inconvénients de la méthode:
protected void onDestroy() {// kill the thread
• Les données de la Thread doivent être sauvegardées puis restaurées.
isThreadRunnning.set(false);super.onDestroy();}
/**The atomic boolean to set the thread run*/ protected void onPause() {// stop the thread
private AtomicBoolean isThreadRunnning = new AtomicBoolean(); isThreadPausing.set(true);super.onPause();}
/*** The atomic boolean to set the thread pause */ protected void onResume() {//relaunch the thread
private AtomicBoolean isThreadPausing = new AtomicBoolean(); isThreadPausing.set(false);super.onResume();}
34
35. /** Called when the activity is first created. */ Avantages de la méthode:
@Override
• Conserve la thread et son état
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); Inconvénients de la méthode:
…//Do something • Ne marche que pour lorsque l’activité va être recréée immédiatement
backgroundThread = (Thread)
• Est deprecated depuis l’API level 11 (Fragment.setRetainInstance())
getLastNonConfigurationInstance();
…//Test if backgroundThread == null Thread I.H.M Thread de traitement
…//Do something again
Activity Handler
} sendMessage()
@Override
public Object onRetainNonConfigurationInstance() {
//Save the thread message
return backgroundThread;
} OnDestroy()
OnCreate()
Lorsque votre activité est détruite pour être
immédiatement recrée, la méthode Thread I.H.M
onRetainNonConfigurationInstance permet Thread de traitement
Activity Handler sendMessage()
d’envoyer un objet de l'instance de l’activité
mourante vers l'instance de la nouvelle activité.
message
!!!Attention ne pas utiliser en l’état !!!
!!! Fuite mémoire !!!
35
36. La gestion des ressources vous permet de gérer les éléments suivants:
• Les chaines de caractères et leur internationalisation,
• Les images,
• Les couleurs,
• Les styles et thèmes.
La localisation des ressources et l’arbre des caractéristiques potentielles des appareils.
La problématique du multi-screens et de la fragmentation.
36
37. Les chaines de caractères de l’application doivent être Externalisées dans L’utilisation de ces chaînes de caractères est la suivante.
un fichier xml et Internationalisées. Remarquez que la chaîne renvoyée dépend de la locale de
Cela s’effectue en les plaçant au sein des dossiers resvalues-**: l’utilisateur et des fichiers ressources définis.
resvalue-**string.xml correspond au fichier des chaines de caractères à Dans le code Java:
utiliser quand l’appareil a pour locale ** (** peut-être fr, en, ru, es...). //Retrieve String param_message with its parameters
resvaluesstring.xml stocke les chaines de caractères qui seront utilisées Resources res = getResources();
par défaut (quand aucune locale de votre application ne correspond à la String parameterString=
locale de l’utilisateur). String.format(res.getString(R.string.param_message),“peace“,“war“,2);
Ces fichiers stockent les chaines de caractères en xml. Vous pouvez //Retrieve the array of strings
stocker une chaine simple, une chaine avec des paramètres, un tableau de String[] iLikeItems = res.getStringArray(R.array.i_like_array);
chaines ou une chaine singulier ou pluriel.
//Retrieve a plural string
resvaluesstring.xml
int count = 2;
<?xml version=“1.0“ encoding=“utf-8“?>
<resources><!--String with parameters ($s for string, $d for decimal … )--> String humans = res.getQuantityString(R.plurals.singleOrPlural, count);
<string name=“param_message“>Hello, i love %1$s, i hate %2$s, i am %3$d years
old</string> Dans un fichier xml, seule la string simple est utilisable:
<!--String array--> <TextView android:layout_width=“fill_parent“
<string-array name=“i_like_array“> android:layout_height=“wrap_content“ android:text=“@string/hello“
<item>chocolate</item> <item>television</item> />
</string-array>
<!--Plurals String-->
<plurals name=“singleOrPlural“> Dans un fichier xml, le tableau s’utilise avec les Spinners:
<item quantity=“one“>I am alone on moon</item> <Spinner android:id=“@+id/spinner“
<item quantity=“other“>I am 7 Billion on earth</item> android:layout_width=“fill_parent“ android:layout_height=“wrap_content“
</plurals> android:prompt=“@array/string_array“ />
</resources>
37
38. Les images se placent dans resdrawable. Les formats acceptés sont PNG (best) et JPEG (ok). GIF n’est pas recommandé.
Il existe 9 types d’image qui s’adaptent à la situation (étirable Nine-patch, à état StateList, associé à un niveau...) et sont définis par
un fichier XML.
Exemple d’image dépendant de l’état du composant (StateListDrawable):
<?xml version=“1.0“ encoding=“UTF-8“?>
<selector xmlns:android=“http://schemas.android.com/apk/res/android“>
<!-- when focused --> <item android:state_focused=“true“ android:drawable=“@drawable/sky“/>
<!-- when pressed --> <item android:state_pressed=“true“ android:drawable=“@drawable/coffee“ />
<!-- when normal --> <item android:drawable=“@drawable/icon“ />
</selector>
Certaines images permettent de définir une forme (fichier dans drawablecolor_border.xml) :
<shape xmlns:android=“http://schemas.android.com/apk/res/android“>
<stroke android:width=“1dip“ android:color=“#FFFFFFFF“ /> <!-- Bordure-->
<solid android:color=“#FF00FF00“ /> <!– Couleur du fond-->
<corners android:radius=“20dp“ /> <!– angle des coins-->
</shape>
Utilisation pour obtenir un composant à bords arrondis:
// make a round button
shape = (GradientDrawable) getResources().getDrawable(R.drawable.color_border);
// set the shape to the component
onOff.setBackgroundDrawable(shape);
38
39. Les couleurs se placent dans valuescolor.xml (par convention) qui associe un nom à une couleur. Le format est soit RVB en
hexadécimal (FFFFFF pour le blanc) soit ARVB où A n’est autre que le alpha (la transparence) ce qui donne FFFFFFFF (blanc
toujours)
<resources> <TextView
<color name=“opaque_red“>#ff0000</color> android:layout_width=“fill_parent“
<color name=“translucent_red“>#80ff0000</color> android:layout_height=“wrap_content“
</resources> android:textColor=“@color/translucent_red“
android:text=“Hello“/>
Il est très important dans vos applications de déclarer les couleurs que vous utilisez au sein d’un seul fichier de manière à externaliser
toute votre charte couleur et à pouvoir les réutiliser dans vos styles.
<resources> <TextView
<color name=“backgound“>#f00f0000</color> android:layout_width=“fill_parent“
<color name=“background_component“>#80ff0000</color> android:layout_height=“wrap_content“
<color name=“backgound_component_second“>#f00f0900</color> android:textColor=“@color/foreground_color“
<color name=“background_component_third“>#80ff0740</color> android:text=“Hello“/>
<color name=“ foreground_color “>#f00f0008</color>
…
</resources>
Pourquoi? Maintenance, Evolution, Réutilisation de charte graphique entre application, Factorisation...
39
40. Les thèmes se placent dans valuestheme.xml et définissent un ensemble de styles. Un style est associé à un nom et définit un
ensemble de variables (couleur de fond, couleur, taille, font de police, marges ...).
<?xml version=“1.0“ encoding=“utf-8“?> <TextView
<resources> style=“@style/ ToDoTheme“
<style name=“ToDoTheme“ parent=“@android:style/Theme.Black“> android:text=“@string/hello“ />
<item name=“android:textSize“>12sp</item>
</style>
</resources>
Un exemple concret avec dérivation:
!--Style for TextView--> <!--Style for EditText-->
<!-- ************************--> <!-- ************************-->
<style name=“mainTxv“> <style name=“matchEDT“ parent=“mainTxv“>
<item name=“android:textSize“>12sp</item> <item name=“android:background“> "@color/background_edt" </item>
<item name=“android:textStyle“>bold</item> <item name=“android:minWidth“>14.5sp</item>
<item name=“android:background“>"@color/background"</item> <item name=“android:textColor“> "@color/foreground_edt" </item>
<item name=“android:paddingLeft“>3dip</item> <item name=“android:textStyle“>normal</item>
<item name=“android:paddingRight“>3dip</item> </style>
<item name=“android:textColor“> "@color/foreground" </item>
<item name=“android:layout_width“>wrap_content</item>
<item name=“android:layout_height“>wrap_content</item>
</style>
40
41. Android fait bien les choses, voire trop bien.
Où comment Android décide quel est le fichier de ressources à utiliser (cela se comprend bien dans le cas de l’internationalisation mais ce
concept est étendu à l’arbre des caractéristiques des devices Android). La liste des paramètres principaux pris en compte par Android
est la suivante :
Code réseau et code mobile du pays de la carte SIM dit code MCC (Mobile Country Code) et MNC (Mobile Network Code).
http://en.wikipedia.org/wiki/Mobile_Network_Code
Langue et région (internationalisation usuelle)
Taille de l’écran (small, medium, large et xLarge)
Rapport largeur/longueur (long ou not long) de l’écran
Orientation de l’écran (port pour portrait, land pour landscape ou square pour carré)
Densité de l’écran en pixel (ldpi pour low dpi, mdpi pour medium dpi, hdpi pour high dpi, xhdpi)
Type d’écran tactile (notouch pour non sensitif, stylus pour le stylet et finger pour sensitif)
Visibilité du clavier (keyexposed pour clavier physique apparent, keyshidden pour clavier physique caché ou keysoft pour un clavier logiciel)
Type de clavier (nokeys, qwerty ou 12Key ???)
Type de navigation (nonav pour pas de navigation, dpad pour le pad, trackball pour trackball et wheel pour la roue de la souris ???)
Version du système (v1,…v15,…)
Ainsi pour définir la branche dans laquelle se trouve votre ressource, il faut spécifier son chemin :
Il faut définir dans l’ordre des paramètres ci-dessus listés,
On peut omettre des éléments,
http://developer.android.com/guide/topics
Il faut un unique qualificateur par type de paramètre, /resources/providing-resources.html
Il faut les séparer par un trait d’union.
Exemple : values-fr, drawable-hdpi, layout-large-port, values-v14,…
41
43. Merci pour votre attention.
Merci à DEVOXX France
et Bravo !
Et rendez-vous à Brazzaville
en septembre
android2ee.com.
#android2ee
MyLight et MyTorch mathias.seguy.it@gmail.com
disponible sur GooglePlay
43