J’avais déjà donné une astuce lors d’un précédent article expliquant la nécessité de minimiser l’utilisation des vues. Cela permet d’une part de minimiser la mémoire consommée et de sauver du temps CPU lors de la passe de mesure par exemple. Les lecteurs auront probablement remarqué que l’article était plus rédigé sous forme de “à faire/à ne pas faire” plutôt que sous forme “d’astuce”.
Pour résumer succinctement, la conclusion de l’astuce de mon précédent article était : “Réfléchissez à ce que vous faites, ne faites pas de choses inutiles”. L’objectif était bien sûr de vous faire comprendre les problèmes engendrés par l’utilisation abusives de vues. J’espère sincèrement qu’à la lecture de l’astuce #1 vous avez réétudié l’ensemble de vos interfaces et que le nombre de View et de ViewGroup utilisés a été revu à la baisse. Maintenant que vous maitrisez tous le sujet, je pense qu’il est temps de vous faire part d’une nouvelle astuce qui vous permettra d’optimiser votre hiérarchie de vues (ou plus précisément de vérifier la minimalité de l’arbre des vues) : l’outil layoutopt.
L’outil layoutopt est disponible dans le SDK Android (sous le dossier tools) et s’utilise très simplement :
layoutopt
Prenons l’example d’un fichier XML non minimal tel que le suivant (fichier nommé bad.xml) :
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Minimisez l'utilisation des vues !" />
</RelativeLayout>
</FrameLayout> |
En utilisant un peu nos méninges, on se rend facilement compte que le FrameLayout est inutile. L’arbre optimal devrait normalement être de la forme suivante (fichier nommé good.xml) :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Minimisez l'utilisation des vues !" />
</RelativeLayout> |
L’utilisation de layoutopt sur les deux fichiers précédents affiche la sortie suivante :
MacBook-Pro-de-Cyril-Mottier:tools cyrilmottier$ ./layoutopt /Users/cyrilmottier/Desktop/testfolder/
/Users/cyrilmottier/Desktop/testfolder/bad.xml
5:20 The root-level <FrameLayout/> can be replaced with <merge/>
10:18 This RelativeLayout layout or its FrameLayout parent is useless
/Users/cyrilmottier/Desktop/testfolder/good.xml |
La sortie donnée par layoutopt sur bad.xml est assez explicite et mentionne que FrameLayout ou RelativeLayout est inutile. Le fichier good.xml est considéré comme minimal. Super non? Vous pensez maintenant que vos méninges sont inutiles? Malheureusement non … Android a beau fournir de superbes outils, il ne peut pas réfléchir pour vous. Si le seul et unique but de votre UI est d’afficher un texte au centre de l’écran, il est encore possible de minimiser l’interface précédente (fichier really_good.xml) :
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:text="Minimisez l'utilisation des vues !" /> |
A vous de jouer. Minimisez bien !
Note : l’astuce donnée dans ce post n’est pas vraiment relative à Android. C’est, en réalité, une astuce rendue possible par la flexibilité du langage Java. Néanmoins, il s’avère que cette technique est bien souvent utile et méconnue des développeurs Android.
Ne vous est-il jamais arrivé d’écrire des lignes répétitives un peu du genre de celles données ci-dessous?
ImageView mImageViews[] = new ImageView[100];
mImageViews[0] = new ImageView(this);
mImageViews[0] = setImageResource(R.drawable.image0);
mImageViews[1] = new ImageView(this);
mImageViews[1] = setImageResource(R.drawable.image1);
// Encore et encore les mêmes lignes ...
mImageViews[98] = new ImageView(this);
mImageViews[98] = setImageResource(R.drawable.image98);
mImageViews[99] = new ImageView(this);
mImageViews[99] = setImageResource(R.drawable.image99); |
J’espère pour vous que vous n’avez jamais fait cela ! En effet, en plus de faire grossir le code compilé résultant, ce genre de code source est surtout illisible pour de futurs utilisateurs/relecteurs, sujet à erreurs (copier-coller oblige) et surtout très ennuyant à écrire. Tout bon programmeur tente, dans la mesure du possible de factoriser le code. Pour ce faire, on définit d’abord les constantes au niveau de la classe (j’ai mis trois points parce que je suis feignant et que ça n’a pas de réelle utilité de mettre la vraie suite) :
// Prepare le tableau des identifiants de ressources
private static final int IMAGE_IDS = {R.drawable.image0, R.drawable.image1, R.drawable.image2, ..., R.drawable.image98, R.drawable.image99}; |
Il est maintenant possible de factoriser le code via une simple boucle for :
ImageView mImageViews[] = new ImageView[100];
final int count = IMAGES_IDS.length;
for (int i = 0; i < count; i++) {
mImageViews[i] = new ImageView(this);
mImageViews[i].setImageResource(IMAGES_IDS[i]);
} |
Une telle méthode a l’avantage certain de minimiser le code “effectif”, d’être beaucoup plus lisible et moins dangeureuse. Malheureusement, elle reste encore assez rébarbative puisque il faut créer le tableau IMAGES_IDS. Pour finir, si on souhaite ajouter de nouveaux drawables, il faut retoucher au code du tableau IMAGES_IDS pour qu’ils soient pris en compte.
Android regroupe, de façon automatique (c’est l’outil aapt qui s’en charge) , l’intégralité des ressources dans un fichier nommé R.java. En d’autres termes, les ressources sont accessibles via le code Java par l’intermédiaire de constantes définies dans ce fichier de ressources. Pour faciliter la récupération de ressources, il est donc possible d’utiliser les Field. Le code précédent devient maintenant :
// On récupère l'ensemble des "champs" de la classe R.drawable
Field[] fields = R.drawable.class.getFields();
// Le -1 est nécessaire pour ne pas inclure l'icône de l'application
ImageView mImageViews[] = new ImageView[fields.length - 1];
int i = 0;
for (Field field : fields) {
try {
// Pour chaque champ, on récupère sa valeur (c'est à dire l'identifiant de la ressource)
int currentResId = field.getInt(R.drawable.class);
// Si cet identifiant n'est pas celui de l'icône de mon application
if (currentResId != R.drawable.mydemo_icon) {
mImageViews[i] = new ImageView(this);
mImageViews[i].setImageResource(currentResId);
i++;
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} |
Toute méthode a ses avantages et ses inconvénients. Cette dernière ne déroge pas à la régle. Son avantage indéniable est de supprimer le code à maintenir dans le cas d’ajout ou de suppression de drawable. Lorsqu’on ajoute un nouveau drawable au projet, il est automatiquement pris en compte au prochain lancement du programme. Son inconvénient réside dans sa “globalité”. Imaginons que vous souhaitiez ne prendre un compte qu’un nombre fini (20 par exemple) des 100 drawables de votre projet. La condition d’exclusion devient énorme !.
Voilà un post un peu atypique qui vient étoffer cette longue liste d’articles franchement techniques que j’ai pu rédiger pour mon blog. Aujourd’hui j’ai souhaité crier haut et fort ma déception … que dis-je … mon énervement le plus profond lorsque je surfe sur Internet et que je lis les dernières actualités Android. Cela va probablement vous sembler un peu exagéré mais c’est pourtant une chose sur laquelle je mets un point d’honneur : voir de grands sites/groupes écrire le nom de notre système d’exploitation adoré avec une faute d’orthographe m’exaspère !
Je vous laisse analyser les quelques screenshots (gardez à l’esprit que c’est un tout petit échantillon du véritable phénomène) que j’ai pu découvrir et qui écorche honteusement le véritable nom de notre chère plateforme open-source :
Chez Orange : 
Sur le site Internet du Monde : 
Encore une fois sur le site Internet d’Orange : 
Il me semblait important de rappeler à tout le monde que le système Android s’écrit bien Android et non pas Androïd ou Androïde … Le nom “Android” a d’ailleurs été déposé par l’OHA. Un androïde se réfère à un robot à forme humaine tandis que le mot Androïd n’a tout bonnement aucune signification dans la langue française. Je suppose que cette erreur provient simplement d’une prononciation à la française d’Android et de l’analogie avec un androïde. Faites donc passer le mot autour de vous et aidez moi à stopper cette pandémie d’erreurs :p