I. Participants▲
Florian Fournier (29 ans, Toulouse)
- Expert en Android .
- Responsable de la rubrique Android du site developpez.com.
Kilian Lamberdière (25 ans, Montpellier)
- Développeur mobile.
- Modérateur de la rubrique Android du site developpez.com.
Joël Drigo (44 ans , Paris)
- Chef de produit pour la société Wedia.
- Developpeur java / Eclipse RCP.
- Modérateur de la rubrique Java du site developpez.com.
Mickaël Le Trocquer
- Expert en Android pour la société Open.
Nicolas Romantzoff (42 ans, Lyon)
- Formation Systèmes & Réseaux (ENSIMAG).
- Expert Android pour la société Anyxis.
- Expert architecture logicielle pour la société Ars-Media.
- Rédacteur/Modérateur de la rubrique Android du site developpez.com.
Thomas Andres Gomez
- Développeur Android / iOs pour la société Novedia Group.
Sébastien Knopff
Yannick Eglem
Florian Le Ficher (23 ans, Paris)
- Développeur Android pour la société Valtech.
- Chef de projet technique pour la société Saint-Gobain.
- A fréquenté l'EPITA, promotion 2013 en alternance.
David Silvera (24 ans, Paris)
- Expert en Android pour la société Webtyss.
- Rédacteur/Modérateur de la rubrique Android du site developpez.com.
- A fréquenté l'EPITA, promotion 2013 en alternance.
Sébastien Grimault
Mathias Seguy
II. Architecture▲
1) Bien packager les sources par type de classe :
- haut niveau GCM/ Application / Constante :
- Pour les constantes, cela peut se positionner soit dans une classe statique permettant de centraliser les données ou directement depuis sa classe en privé ou protected selon le besoin ;
- activité :
- générique,
- par UC :
- fragment,
- service,
- adapter,
- view,
- autres ;
- fragment générique ;
- service générique ;
- adapter générique ;
- utils ;
- provider ;
- view générique :
- par Type de View (View, ViewGroup, etc.) ;
- BD :
- Pojo ;
- com / WS :
- Pojo ;
- listener / BroadCast ;
- animations.
2) Si des sources externes doivent être importées, les placer dans un projet à part et les inclure au projet courant.
3) Toujours avoir des images sous différents formats selon le type d'écran de mobile cible :
- hdpi ;
- ldpi (plus utilisé par les constructeurs mais les vieux téléphones les utilisent encore) ;
- mdpi ;
- xhdpi ;
- xxhdpi (représente en général les tablettes qui sont, elles, reconnues par la dimension sw-600).
Tips : Essayer au maximum d'utiliser les drawables sous format XML pour ne pas surcharger l'application en ressource ( voir shape ).
4) Les fichiers XML sur les animations ou selector doivent se trouver dans le dossier drawable par défaut si ceux-ci ne sont pas dépendants de la configuration du téléphone, avec un nommage pour les différencier. Ex : animation_fade_in, selector_action, etc.
5) Toutes les chaînes de caractères doivent être définies dans le fichier “res/values/strings.xml”, ce qui permet de modifier les libellés sans toucher aux sources. Cela a aussi pour but de pouvoir développer en multi-langage assez rapidement.
Tips : Si votre projet est assez lourd en ressources, vous pouvez également diviser vos données en plusieurs fichiers, par exemple par activity ou Use Case pour faciliter l'accessibilité à ces données.
6) De la même manière que pour le point précédent, déclarer les couleurs et les dimensions dans leurs fichiers respectifs “res/values/colors” et “res/values/dimens”.
7) Supprimer les fichiers non utilisés dans votre programme. En effet, en fin de développement, nous retrouvons souvent des images que nous n'utilisons plus dans notre application. Pour retrouver facilement ces fichiers, utiliser Lint (cf, chapitre “Le plus”).
III. Nommage▲
III-A. Côté JAVA▲
1) Tous les identifiants sont en CamelCase (les mots sont attachés, chaque mot commence par une majuscule). Le caractère “_” (underscore) est interdit (sauf constantes voir #7).
2) Le premier mot des identifiants de types (classes, enum, interface) commence avec une majuscule (voir points #3 et #4). Les autres identifiants commencent par une minuscule, que ce soient des variables (voir points #5 et #6) ou des fonctions.
3) Toujours commencer le nom d'une interface par la lettre I.
4) Toujours commencer le nom d'une classe abstraite par Abstract. Pour ne pas avoir de nom à rallonge, ce nom peut commencer par la lettre A.
5) Les membres de classe non statiques doivent commencer par un ‘m' et/ou précéder l'utilisation d'un membre de classe par “this”, le but étant de pouvoir les différencier des variables locales.
6) Les membres de classe statiques non final doivent commencer par un ‘s'.
7) Les variables de type public static final doivent être en majuscules. De plus, le nom de la variable contenant plusieurs mots doit séparer ces derniers par un underscore :
public
static
final
String MON_NOM_DE_VARIABLE_STATIC_FINAL;
8) Les noms de variables doivent commencer par une minuscule.
9) Les noms de fonctions doivent être explicites. Nous devons savoir ce que fait une fonction sans même lire le code.
De même, les noms de variables doivent aussi être explicites. Il faut favoriser les noms discriminants.
Exemple : Cas rencontré dans une discussion de forum : bleu = new JMenuItem("Bleu"), rouge = new JMenuItem("Rouge"), vert = new JMenuItem("Vert"); Plus loin dans le code : red = new JButton(new ImageIcon("images/rouge.jpg")), green = new JButton(new ImageIcon("images/vert.jpg")), blue = new JButton(new ImageIcon("images/bleu.jpg")); Le problème rencontré est que la personne en question a confondu sa variable appelée “blue” avec celle appelée “bleu”. S'il avait plutôt eu le code suivant, il ne se serait sûrement jamais trompé et la lisibilité du code aurait été meilleure : menuBlue = new JMenuItem("Bleu"), menuRed = new JMenuItem("Rouge"), menuGreen = new JMenuItem("Vert"); Plus loin dans le code : buttonRed = new JButton(new ImageIcon("images/rouge.jpg")), buttonGreen = new JButton(new ImageIcon("images/vert.jpg")), buttonBlue = new JButton(new ImageIcon("images/bleu.jpg")); |
III-B. Côté XML▲
Les identifiants XML ne doivent pas contenir de majuscule. La bonne pratique consiste à remplacer la majuscule par une minuscule précédée d'un tiré bas. Par exemple, “monIdentifiant” devient “mon_identifiant”.
Il faut éviter les doublons au niveau des identifiants. Une pratique pour cela peut être de préfixer l'identifiant par le fichier de layout.
Les identifiants XML commencent par une minuscule.
III-C. Nom des fichiers▲
Astuce : Les fichiers de ressource des layout sont souvent difficiles à retrouver parmi nos dizaines de fichiers. Il est préférable de préfixer le nom du fichier de ressource par le type de layout. Par exemple :
- Un fichier de ressource pour une activité sera préfixé par “activity_” suivi du nom de l'activité l'utilisant. Si l'activité est “AuthenticationActivity.java”, le layout sera “activity_authentication”.
- Un fichier de ressource pour un fragment sera préfixé par “fragment_” suivi du nom du fragment l'utilisant. Si le fragment est “ListEtudiantFragment.java”, le layout sera “fragment_list_etudiant”.
- Un fichier de ressource pour une vue sera préfixé par “view_” suivi du nom de la vue l'utilisant. Si la vue est “MyCardView.java”, le layout sera “view_my_card”.
- Un fichier de ressource pour un item sera préfixé par “item_” suivi du type d'item. Si l'item correspond à une ligne d'une ListView affichant des noms d'étudiants, le nom du fichier sera “item_student_name”.
Ainsi nommés, il sera facile de rechercher le layout d'une activité ou autre.
III-D. Nom des packages▲
1) Le nom des packages ne doit pas contenir d'underscore (_) ni de majuscule !
2) Le nom des packages ne doit pas commencer par com.android. En effet, cette appellation est réservée aux applications créées par Google pour Android.
3) Le nom du package par défaut est décomposé en 3 parties :
- l'abréviation du pays : fr (France), uk (Angleterre), ca (Canada), com (International -> le plus souvent utilisé) ;
- la société qui développe l'application ;
- le nom de l'application.
Exemple : “com.android.sms” représente une application nommée “sms”, créée par la société “Android” à destination internationale (com).
IV. Implémentation▲
1) Toujours implémenter le else quitte à retourner false.
2) Un seul return.
3) Préférer à un while(true), un boolean conditionBoucle = true, avec while(conditionBoucle) { };.
4) Simplifier les if (maCondition == true) en if (maCondition).
5) Utiliser la fonction equals lors de la comparaison d'Objet, implémenter la fonction si cela est nécessaire ! Par cohérence, faire de même avec compareTo.
6) Favoriser les types primitifs au lieu des Objets (int != Integer), mais faire attention à l'auto-unboxing avec les passages en bundle/extra.
7) Écrire des méthodes courtes. Une méthode faisant plus de 40 lignes peut toujours être découpée en deux minimums.
Conseil :
Il peut arriver que des méthodes métier (voir certaines méthodes techniques) utilisent beaucoup de lignes et beaucoup de variables. On peut arriver facilement à 1000 lignes pour faire un traitement métier, en utilisant 25 ou 30 variables. Il faut absolument découper cette méthode en plusieurs méthodes : il y a toujours une forte probabilité qu'un traitement qui prend autant de lignes soit une combinaison de traitements plus limités réutilisables, à quelques variantes près que l'on résout par des paramètres. Cependant, on peut avoir besoin de la plupart de ces variables dans chacune des nouvelles méthodes qu'on va devoir créer pour découper le processus. Faire des méthodes avec 25 ou 30 arguments est source d'erreurs. De plus, c'est lourd à appeler (on peut facilement inverser 2 paramètres, et lorsqu'on doit ajouter un nouveau paramètre, il faut le faire dans plusieurs appels). Il est aussi plus lourd à déboguer. Pour finir, les performances sont dégradées par la copie des paramètres. On peut résoudre ce problème en créant une classe qui représente un contexte : les paramètres deviennent simplement des variables de cette classe. Si l'instance est créée dans la première méthode d'appel, elle ne sera pas partagée, et, donc, on peut se permettre d'avoir des variables public, ce qui facilitera la manipulation. |
8) Commenter le code ! Voir le chapitre sur les Commentaires.
9) Les dimensions doivent être de préférence en “dp”, mis à part pour la taille des textes qui doivent être en “sp”.
10) Pour convertir une chaîne de caractères en entier, flottant, double ou booléen, utiliser la fonction static “valueOf” de la primitive désirée. Par exemple, pour convertir une chaîne de caractères en entier, nous ferons :
Integer.valueOf (maChaine);
et non :
new
Integer (maChaine);
11) Éviter au maximum les poids (weight) dans vos fichiers XML.
12) Supprimer les importations inutiles.
13) User et abuser des Thèmes et Styles pour vos applications. Cela permet d'habiller automatiquement les composants graphiques, de factoriser les déclarations graphiques et de pouvoir réagir rapidement au changement. Pour cela, vous pouvez vous référer à ce tutoriel : http://nicroman.developpez.com/tutoriels/android/styles_themes/.
14) Ne pas instancier d'objets lorsque l'on n'en a pas besoin. Cela peut paraître bête, mais il peut arriver que l'on oublie ce genre de choses après un refactoring.
IV-A. Création d'objet initialisation▲
1) Interdiction d'initialiser les variables membres en dehors de leur classe.
2) Les variables de classe doivent être définies de préférence au début du fichier. Si elles ne peuvent pas y être pour une quelconque raison, elles doivent être déclarées avant la fonction les utilisant.
3) Les variables de boucle doivent être déclarées directement dans la boucle :
for
(int
i =
0
; i <
size; i+
+
)
et non :
int
i;
for
(i =
0
; i <
size; i+
+
)
4) Ne pas réutiliser une même variable dans un même scope pour représenter plusieurs choses différentes (même si elles se ressemblent).
Exemple à ne pas suivre :
Date date =
reservation.getStartDate
();
if
(newDate.before
(date)) {
reservation.setStartDate
(newDate);
//
...
2
ou
3
lignes
de
codes
ici
}
//
...
2
ou
3
lignes
de
codes
ici
date =
reservation.getEndDate
();
if
( newDate.after
(date) {
reservation.setEndDate
(newDate );
//
...
2
ou
3
lignes
ici
}
/*
On
peut
facilement
ajouter
ici
du
code
qui
manipule
date,
en
pensant
que
c'est
la
date
de
début,
si
on
ne
regarde
pas
tout
le
code
de
la
méthode.
De
plus,
si
le
code
est
repris
par
un
autre
développeur,
il
comprendra
difficilement
à
quoi
sert
la
variable.
*/
IV-B. Exception▲
1) Utiliser au maximum les exceptions JAVA existantes (ArithmeticException par exemple, lorsqu'un calcul ne peut être fait parce qu'un opérande est invalide), et ne jamais faire de throw new Exception(“...”), mais faire sa propre classe d'exceptions.
2) Pensez à centraliser vos exceptions si vous voulez les gérer. Pour cela, créer une classe ExceptionManager permettant de réaliser les tâches à faire lors d'une remontée d'exception (Log, affichage de dialogue, etc ..). Cette opération peut s'avérer difficile par type d'exception, une interface ou un paramètre vous seront peut-être utiles pour différencier de quel Use Case l'exception a été remontée.
IV-C. Annotation▲
1) Ne jamais appeler de méthode marquée deprecated : une méthode marquée deprecated est toujours accompagnée d'un commentaire indiquant quelle autre méthode appeler à la place. Toujours dans le scope de votre API minimum, essayer au possible d'utiliser les nouvelles fonctionnalités en fonction de l'API installé sur votre device.
Exemple :
if
(android.os.Build.VERSION.SDK_INT >=
android.os.Build.VERSION_CODES.GINGERBREAD) {
//
only
for
gingerbread
and
newer
versions
}
Vous pouvez également passer par un boolean depuis les ressources pour savoir sur quel API vous êtes.
2) Utiliser les annotations Android plutôt que de laisser des warnings. Typiquement, pour les utilisations de méthodes ou d'objets disponible dans une version ultérieure de la cible, encadrer par le #1, et rajouter l'annotation @TargetApi(XX).
V. Documentation et Commentaires▲
V-A. JAVA▲
1) Tous les documents ayant une licence doivent commencer par un commentaire contenant la définition du copyright. Chaque bloc doit être séparé par un espace. Par exemple, si l'on veut contribuer aux sources Android, on écrira :
/*
*
Copyright
(C)
2013
The
Android
Open
Source
Project
*
*
Licensed
under
the
Apache
License,
Version
2.0
(the
"License");
*
you
may
not
use
this
file
except
in
compliance
with
the
License.
*
You
may
obtain
a
copy
of
the
License
at
*
*
http://www.apache.org/licenses/LICENSE-2.0
*
*
Unless
required
by
applicable
law
or
agreed
to
in
writing,
software
*
distributed
under
the
License
is
distributed
on
an
"AS
IS"
BASIS,
*
WITHOUT
WARRANTIES
OR
CONDITIONS
OF
ANY
KIND,
either
express
or
implied.
*
See
the
License
for
the
specific
language
governing
permissions
and
*
limitations
under
the
License.
*/
2) Avant chaque interface ou chaque classe, une description de celles-ci doit être écrite en commentaire. Le commentaire doit être “JavaDoc” (commencer par /** )
Par exemple:
/**
*
Un
étudiant
correspond
à
une
personne
en
cours
d
'
apprentissage
*/
public
class
Student {
}
3) Commenter chaque fonction publique avec la notation de la javadoc:
/**
*
*
@param
*
@return
*/
Ceci à pour but de savoir :
- à quoi sert la fonction (1ère ligne) ;
- avec quels paramètres l'appeler (à placer après le @param) ;
- que retourne la fonction (à placer après le @return).
Cette notation permet aussi de générer facilement une documentation du code.
4) Lorsque des parties de code seront implémentées par la suite, laisser un commentaire expliquant ce que doit faire le code manquant précédé de “TODO”. La plupart des IDE gère automatiquement ce genre de tags, dans une TodoList : ceci ajoutant en plus de l'information utile un moyen pratique de suivre ses tâches à faire. Pour l'utiliser efficacement, ne pas oublier de supprimer les “TODO” générés automatiquement par les IDE, pour ne pas se retrouver avec X vrais “TODO” et Y bruits “// TODO auto-generated method stub”.
5) De la même façon que pour le tag TODO, le tag FIXME peut être utilisé pour marquer un commentaire qui indique qu'on sait qu'un code pose problème dans certains cas, mais qu'on n'a pas forcément le temps de traiter dans l'immédiat.
6) Pensez à marquer deprecated les méthodes que vous avez besoin de modifier de façon importante, en indiquant en commentaire de déprécation la méthode de remplacement. Corollaire : dans un code réutilisé (une bibliothèque par exemple), ne jamais modifier les paramètres d'une méthode ; en créer une nouvelle, que l'ancienne appelle, et rendre deprecated l'ancienne éventuellement : si les paramètres ajoutés ne peuvent avoir de valeur par défaut facilement déterminable, laisser les 2 codes. Les IDE modernes permettent de visualiser les appels de méthodes marquées deprecated de manière globale afin d'aider le développeur à intégrer une nouvelle version d'une bibliothèque. Ceci est applicable également aux classes.
V-B. XML▲
1) Pensez à structurer vos fichiers xml en créant des blocs par activité à l'aide des commentaires. Par exemple, l'ensemble des chaînes de caractère de l'activité d'authentification seront précédé d'un commentaire simple en xml m'indiquant que ces chaînes de caractères sont utilisées dans cette activité :
<!--
AUTHENTICATION
ACTIVITY
-->
<
string
name
=
"
hello
"
>
Hello<
/
string
>
VI. Coding Style▲
VI-A. JAVA▲
1) Au niveau du formatage automatique dans les IDE, Google met à disposition des fichiers ici : https://android.googlesource.com/platform/development/+/master/ide.
2) Déclaration d'une variable à la fois.
3) Déclarer les variables types tableau selon cette convention :
int
[] array;
et non
int
array[];
qui est elle sujette à confusion et tromperie.
4) Ne pas écrire une fonction sur la même ligne qu'une déclaration.
5) Les accolades suivant une condition doivent être présentes sur la même ligne que la condition, séparées d'un espace.
Exemple :
if
( toto =
=
1
) {
//
...
}
else
{
//
...
}
6) Une ligne doit faire au maximum 100 caractères.
7) Aérer le code. Ne pas hésiter à ajouter un espace après et avant chaque opérateur.
Ce code :
1
+
2
*
3
/
4
est plus lisible et agréable à lire que :
1
+
2
*
3
/
4
Surtout si nous remplaçons les chiffres par des noms de variables.
De même, sauter des lignes entre des blocs de code ou des suites d'instructions pour rendre le code moins compact et plus lisible, ainsi que faire ressortir des suites d'instructions liées.
VI-B. XML▲
1) Ajouter un retour à la ligne après chaque paramètre d'un widget. Par exemple, cette déclaration :
<
ImageView
android
:
id
=
"
@+id/iv_back
"
android
:
layout_width
=
"
wrap_content
"
android
:
layout_height
=
"
wrap_content
"
android
:
layout_alignParentLeft
=
"
true
"
android
:
layout_alignParentTop
=
"
true
"
android
:
layout_centerVertical
=
"
true
"
android
:
layout_marginLeft
=
"
@dimen/marginLeft
"
android
:
layout_marginRight
=
"
@dimen/marginRight
"
android
:
contentDescription
=
"
@string/app_name
"
android
:
src
=
"
@drawable/ico_back
"
/
>
et plus lisible que :
<
ImageView
android
:
id
=
"
@+id/iv_back
"
android
:
layout_width
=
"
wrap_content
"
android
:
layout_height
=
"
wrap_content
"
android
:
layout_alignParentLeft
=
"
true
"
android
:
layout_alignParentTop
=
"
true
"
android
:
layout_centerVertical
=
"
true
"
android
:
layout_marginLeft
=
"
@dimen/marginLeft
"
android
:
layout_marginRight
=
"
@dimen/marginRight
"
android
:
contentDescription
=
"
@string/app_name
"
android
:
src
=
"
@drawable/ico_back
"
/
>
VI-C. Style▲
Comme vu dans la section XML, nous devons rendre les plus lisibles possibles les fichiers de ressources. Il s'avère que souvent nos composants ont x paramètres. Les styles sont là justement pour permettre au développeur de pouvoir factoriser le code des ressources et ainsi rendre plus lisibles et plus simples les changements à opérer sur le graphique.
Pour cela, vous devrez créer un style par composant graphique pour votre application. Ces styles pourront être personnalisés par types de charte graphique que vous pourrez trouver dans votre application.
Utilisez les thèmes pour personnaliser les composants par défaut d'Android (Listes, Dialogs,...).
Par exemple, si nous avons un composant Button, nous pourrons trouver comme style :
- Button Action ;
- Button NetWork ;
- etc.
VII. Autres : meilleures pratiques (éviter les anti-patterns)▲
Toujours préférer typer avec l'interface, ou la classe, la plus générique possible les résultats et les arguments de méthodes, pour permettre une plus grande souplesse dans la modification de l'implémentation interne, sans impact sur l'appelant, comme :
public
static
<
T>
Collection<
? extends
T>
filter
(Collection<
? extends
T>
collection, IFilter<
? super
T>
filter) {
/*
personnellement,
j'ai
pris
l'habitude
également
de
tester
mes
arguments
en
premier,
surtout
dans
les
méthodes
utilitaires,
plutôt
que
de
laisser
les
exceptions
standard
se
soulever
le
cas
échéant,
mais
je
ne
suis
pas
sûr
que
cela
soit
une
bonne
pratique
*/
if
( collection =
=
null
) throw
new
IllegalArgumentException
("
Collection
to
be
filtered
must
be
not
null
"
);
if
( filter =
=
null
) throw
new
IllegalArgumentException
("
Filter
must
be
not
null
"
);
List<
T>
filteredCollection =
new
ArrayList<
>
();
for
( T element : collection ) {
if
( filter.accept
(element) ) {
filteredCollection.add
(element);
}
}
return
filteredCollection;
}
avec pour exemple d'utilisation :
List<
Integer>
exampleList =
Arrays.asList
(10
, -
10
, 20
, -
20
); //
exemple
de
liste
d'entier
//
exemple
:
filtrer
les
nombres
positifs
IFilter<
Integer>
exampleFilter =
new
IFilter<
>
() {
public
boolean
accept
(Integer element) {
return
element.intValue
()>
0
;
}
;
}
;
for
(Integer element : FilterTools.filter
(exampleList, exampleFilter )) {
//
traitement
}
VIII. Le plus : Cas d'usage en entreprise▲
1) Utilisez les bonnes fonctions de Log. Encapsuler les logs au sein d'une classe pour ne pas les afficher sur l'application en production.
2) Pensez à créer la possibilité d‘écrire les logs sur la sdcard si vous avez besoin depuis votre projet de récupérer les données terrain autres que les exceptions (par exemple les données GPS). Ceci peut se faire avec Acra ou Crashlytics, mais ils ne sont pas prévus pour cela.
2) N'utilisez jamais System.out.println() ni Exception#printStackTrace().
4) Utilisez lint pour vérifier :
- que toutes les chaînes décrites dans les XML sont utilisées dans le code ou layout ;
- que toutes les chaînes possèdent une traduction dans les langues supportées par l'application ;
- que toutes les images possèdent bien leurs dérivées dans les résolutions supportées par votre application ;
- qu'il n'y a pas d'utilisation de méthodes disponibles dans un API supérieur au minimum supporté par le projet.
5) Vérifiez toujours la mise à jour d'une application avant de la pousser sur le store. Pour ce faire, installez l'application via Google Play, ayez en local l'APK signé prêt à aller sur le store et faites la mise à jour via adb avec adb install -r NouvelleVersionApk.apk.