Compare commits

...

7 Commits

44 changed files with 2190 additions and 1 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/.idea/
/.idea/*

153
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,153 @@
# Contribuer au référentiel de bonnes pratiques
Tout personne qui le souhaite peut se proposer pour contribuer au référentiel.
Les règles du présent document devront être respectées.
L'équipe projet en charge de ce repository s'assure de la bonne tenue de ces règles.
## But des contributions
Les contributions ont pour but :
- De supprimer les bonnes pratiques obsolètes, non applicables ou sans effet
- Dajouter des bonnes pratiques qui semblent pertinentes, sont issues du terrain
- Dassocier aux nouvelles bonnes pratiques une règle de conformité
- De vérifier, améliorer le cas échéant les bonnes pratiques existantes ainsi que leur règle de conformité
- De proposer toute idée qui pourrait améliorer le référentiel d'une manière générale et le cas échéant la mettre en place
## Règles générales
- Tout le monde a sa place, chacun peut apporter son expérience
- La convivialité, le partage, la courtoisie et la bienveillance sont des règles d'or dans les échanges
- Chaque bonne pratique doit être le résultat d'un retour terrain ou d'une approche scientifique
- Ces bonnes pratiques sont aussi un aide mémoire donc même une bonne pratique simple, de bon sens peut avoir sa place
## Equipe Projet
L'équipe projet est constituée de:
-
- [Fanny Demey](https://github.com/FannyDemey)
- [Emmanuel Demey](https://github.com/EmmanuelDemey)
- [Thomas Lemaire](https://github.com/ACTLEM)
Cette équipe s'assure que les règles instaurées pour la contribution soient respectées.
En cas de désaccord entre contributeurs, son rôle est aussi de trancher une décision.
L'équipe projet fait preuve de transparence et fait en sorte que les décisions soient les plus collégiales possibles.
L'équipe projet est responsable de la validation définitive des `Pull Request` et donc de l'intégration de la proposition d'ajout, de modification ou de suppression dans le référentiel.
Un membre de l'équipe projet peut soumettre une proposition aux contributeurs mais ne peut pas être responsable seul de la validation ou non de celle-ci : il faut un consensus de l'équipe projet.
## Proposition
Chaque proposition d'ajout, de modification majeure ou de suppression de bonnes pratiques doit passer par [une discussion](https://github.com/cnumr/best-practices/discussions/categories/bonnes-pratiques).
Le titre de la discussion doit être explicite et doit spécifier s'il s'agit d'un ajout, d'une modification majeure ou d'une suppression.
Une modification majeure peut être:
- Une modification importante du niveau de priorité, c'est à dire des niveaux de mise en oeuvre et d'impacts écologique
- L'ajout ou la suppression d'élément dans la définition de la règle
- L'ajout ou la suppression d'exemples
- La modification de la règle de conformité
Tout contributeur peut donner son avis sur la proposition:
- En cas d'accord, un contributeur clique sur la "flèche vers le haut"
- En cas de désaccord, un contributeur ajoute un "pouce bas" et devra obligatoirement expliquer via un commentaire les raisons de son désaccord
- En cas d'incompréhension, il peut entamer une discussion pour demander des précisions
- En cas d'idée d'amélioration, il peut la proposer
Lorsqu'une proposition reçoit plusieurs votes positifs, l'équipe projet est sollicitée et évalue la pertinence de la bonne pratique.
Les cas suivants peuvent se produire:
- La proposition est validée et le contributeur peut créer une `Issue` avec la discussion en référence
- La proposition est définitivement rejetée avec justification
- La proposition doit être retravaillée avant une décision finale
L'`Issue` créée devra contenir le tag `création`, `modification` ou `suppression` suivant le type de proposition et être rattachée au projet [`Bonnes Pratiques V5`](https://github.com/cnumr/best-practices/projects/2).
En cas d'ajout, le fichier sera nommé suivant le pattern suivant: `BP-5xxx-fr.md` avec `5xxx` le numéro de la bonne pratique qui sera défini par l'équipe projet.
Le fichier sera à copier à partir du [template](./resources/BP_xxxx_fr.md)
Une `Pull Request` associée à l'`Issue` sera créée et soumise à l'équipe projet qui est en charge de la revue.
La création de `Pull Request` suite le processus classique au sein de Github, à savoir:
- Création d'un `fork` personnelle de la personne en charge de la `Pull Request`
- Ajout du repository d'origine comme remote (`upstream`)
- Création d'une branche sur le fork personnel
- Créer une `Pull Request` à partir du fork en mettant comme cible la branche `main` du repository cible
## Amélioration
Pour les améliorations comme:
- Une correction d'orthographe, de grammaire ou de conjugaison
- Une autre formulation
- Une autre mise en forme
Il n'est pas nécessaire de passer par une discussion, il faut:
- Créer une `Issue` avec un intitulé explicite et les tags `modification` et `mineur`
- Développer et créer la PR associée sans oublier la référence à l'`Issue`
## Contenu d'une bonne pratique
Chaque bonne pratique devra obligatoirement contenir:
- Un titre
- Un degré de priorité (sur 5, 5 = prioritaire, 1 = non prioritaire)
- Une difficulté de mise en oeuvre (sur 5, 5 = facile, 1 = difficile)
- Un niveau d'impact écologique (sur 5, 5 = fort, 1 = faible)
- Une description
- Une règle de validation et un seuil de conformité
Les éléments suivants sont facultatifs:
- Un ou des exemples
- Une solution alternative
### Règle de validation et seuil de conformité
La règle de validation permet de tester la mise en oeuvre de la bonne pratique avec une approche objective.
Le seuil de conformité associe à la règle de validation une valeur numérique (seuil) qui permet de décider de façon binaire (oui/non) si la bonne pratique a été mise en oeuvre ou pas.
L'ensemble des règles de validation et des seuils de conformité permettent d'établir un niveau de maturité atteint par le service numérique.
### Définition des niveaux
Trois niveaux doivent être définis pour une bonne pratique :
1. Mise en oeuvre (facilité ou difficulté à mettre en oeuvre la bonne pratique)
2. Impacts environnemental (contribution plus ou moins forte à la réduction des impacts env.)
3. Priorité (ce niveau est défini en fonction de 1. et 2.)
Chacun de ces niveaux est défini par une note de 1 à 5.
#### Mise en oeuvre (higher is better)
La facilité ou difficulté de mise en oeuvre peut être évaluée de la manière suivante :
1. La mise en oeuvre nécessite un haut niveau d'expertise ET une refonte en profondeur du projet
2. La mise en oeuvre nécessite un haut niveau d'expertise OU une refonte en profondeur du projet
3. Le mise en oeuvre demande une compétence particulière sans être rare (ex: SEO, DBA, ...) ET n'a pas besoin de refonte en profondeur du projet
4. La mise en oeuvre ne nécessite pas compétence particulière OU n'a pas d'effet de bord sur le reste du projet
5. La mise en oeuvre ne nécessite pas compétence particulière ET n'a pas d'effet de bord sur le reste du projet
#### Impact environnemental (higher is better)
Cet apport peut être évalué de la manière suivante :
1. Elle réduit la consommation électrique des réseaux ou serveurs à équipements constants
2. Elle réduit la consommation électrique sur la majorité des terminaux utilisateurs
3. Elle limite le besoin en ressources informatiques côté serveur
4. Elle limite le risque de saturation des réseaux mobiles ou fixes
5. Elle limite les risques d'obsolescence des terminaux utilisateurs
#### Priorité (higher is better)
Le niveau de priorité est proposé librement par le contributeur et s'appuie sur la Mise en oeuvre et l'Impact environnemental.

View File

@ -1 +1,61 @@
# mobile-best-practice # Bonnes pratiques d'écoconception appliquées au mobile
## Contexte
L'objectif de ce document est de fournir un référentiel d'écoconception adapté au mobile. L'application des bonnes pratiques ci-dessous ont toutes un but commun : prolonger la durée de vie des terminaux des utilisateurs et utilisatrices.
## Les besoins
Étant donné les évolutions continues du mobile, ce référentiel a besoin de régulièrement être mis à jour.
Toute proposition ou idée d'amélioration, de modification ou de suppression est bienvenue.
## Comment contribuer ?
N'hésitez pas à lire [le guide des contributeurs](CONTRIBUTING.md).
## La liste des Bonnes Pratiques
* [Éliminer les fonctionnalités non essentielles](chapters/MBP_001_fr.md)
* [Quantifier précisément le besoin](chapters/MBP_002_fr.md)
* [Optimiser le parcours utilisateur](chapters/MBP_003_fr.md)
* [Favoriser un design simple, épuré, adapté au mobile](chapters/MBP_005_fr.md)
* [Préférer la saisie assistée à l'autocomplétion](chapters/MBP_004_fr.md)
* [Optimiser la récupération en fonction du cycle de vie de l'application](chapters/MBP_006_fr.md)
* [Libérer la mémoire des traitements consommateurs en fonction de l'état de l'application](chapters/MBP_007_fr.md)
* [Déployer un Android App Bundle (AAB) plutôt qu'un APK](chapters/MBP_008_fr.md) // TODO a reformuler
* [Supprimer les ressources non utilisées](chapters/MBP_009_fr.md)
* [Minifier le code](chapters/MBP_010_fr.md)
* [Ne pas redimensionner les images coté application](chapters/MBP_011_fr.md)
* [Éviter d'utiliser des images matricielles](chapters/MBP_012_fr.md)
* [Utiliser le chargement paresseux](chapters/MBP_013_fr.md)
* [Ne pas recharger inutilement l'ensemble du contenu d'une liste si seulement une partie a changé](chapters/MBP_014_fr.md)
* [Optimiser le chargement paresseux d'un écran complexe comportant des listes imbriquées](chapters/MBP_015_fr.md)
* [Éviter les animations](chapters/MBP_016_fr.md)
* [Utiliser une bibliothèque que si celle-ci est réellement indispensable](chapters/MBP_017_fr.md)
* [Supprimer les bibliothèques non utilisées](chapters/MBP_018_fr.md)
* [Jetpack Compose : Améliorer la "skipabilité" d'une fonction pour réduire le nombre de Recomposition inutile](chapters/MBP_019_fr.md)
* [Réduire l'overdraw](chapters/MBP_020_fr.md)
* [Mettre en cache des données qui coutent cher à instancier ou qui sont fréquemment consultées](chapters/MBP_021_fr.md)
* [Economiser la bande passante en sauvegardant en base de données locale les données fournies par le serveur](chapters/MBP_022_fr.md)
* [Réduire le volume de données stockées au strict nécessaire](chapters/MBP_023_fr.md)
* [Concevoir et manipuler la base de données SQLite de manière optimale](chapters/MBP_024_fr.md)
* [Mettre en cache les images fournies par le serveur](chapters/MBP_025_fr.md)
* [Assurer la compatibilité avec les plus anciens appareils du parc](chapters/MBP_026_fr.md)
* [Éviter les traitements trop complexes sur des données provenant d'un serveur](chapters/MBP_027_fr.md)
* [Valider le code avec un Linter](chapters/MBP_028_fr.md)
* [Supprimer les traductions non utilisées au sein des bibliothèques tierces (seulement pour les APK)](chapters/MBP_029_fr.md)
* [Favoriser les polices standards ou utiliser les "Downloadable font"](chapters/MBP_030_fr.md)
* [Éliminer les fonctionnalités non utilisées](chapters/MBP_031_fr.md)
* [Limiter les outils d'analytics et les données collectées](/chapters/MBP_032_fr.md)
* [Utiliser la version la plus récente du langage (Kotlin/Java) et du SDK Android](chapters/MBP_033_fr.md)
* [Entretenir son application régulièrement](chapters/MBP_034_fr.md)
* [Fournir une alternative textuelle aux contenus multimédias](chapters/MBP_035_fr.md)
* [Éviter la lecture et le chargement automatique des vidéos et des sons](chapters/MBP_036_fr.md)
* [Adapter les sons/vidéo aux contextes d'écoute](chapters/MBP_037_fr.md)
* [Optimiser les PDF](chapters/MBP_038_fr.md)
* [Effectuer le téléchargement de fichiers volumineux via un réseau Wifi](chapters/MBP_039_fr.md)
## Licence
Les sources et contenus de ce projet sont [protégés](LICENCE.md)

43
chapters/MBP_001_fr.md Normal file
View File

@ -0,0 +1,43 @@
## Éliminer les fonctionnalités non essentielles
### Identifiants
Bonne pratique originale : [BP_001](https://github.com/cnumr/best-practices/blob/main/chapters/BP_001_fr.md)
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 1. Spécification | Utilisateur/Terminal | PO/AMOA |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 5 | 4 | 5 |
| Ressources Economisées |
|:-------------------------------------------------------:|
| Processeur / Mémoire vive / Stockage / Réseau |
### Description
Plusieurs études (Cast Software et Standish Group, notamment) démontrent que 70 % des fonctionnalités demandées par les utilisateurs ne sont pas essentielles et que 45 % ne sont jamais utilisées. En réduisant la couverture et la profondeur fonctionnelle de lapplication, on abaisse son coût de développement initial, sa dette technique et les impacts environnementaux associés.
On diminue ainsi mécaniquement linfrastructure nécessaire à son exécution. Par ailleurs, à niveau ergonomique constant, plus lapplication est pauvre fonctionnellement, plus elle sera simple à utiliser. Il faut donc réduire le plus possible la couverture fonctionnelle de lapplication, en la centrant sur le besoin essentiel de lutilisateur.
Détecter une fonctionnalité non essentielle est possible au moment de l'analyse de l'expression du besoin. La méthode MoSCoW, des ateliers, des wireframes (maquettes fonctionnelles) ou des prototypes avec tests utilisateurs permettent de vérifier l'utilité dune fonctionnalité en amont de son développement.
### Exemple
Les succès récents du Web Google, Twitter, WhatsApp, Pinterest, Instagram, etc. fournissent un seul service et misent sur une grande sobriété fonctionnelle.
Se poser, au moment de l'analyse de l'expression du besoin, la question : « Que se passe-t-il si on ne la pas ? ».
Respecter le principe YAGNI (You Ain't Gonna Need It) de lextreme programming : développez quand vous avez effectivement besoin dune fonctionnalité, pas lorsque vous imaginez en avoir besoin.
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|-------------------|:-------------------------:|
| de fonctionnalités dont l'utilité n'a pas été vérifiée avec un panel d'utilisateurs avant développement | 0 % |

49
chapters/MBP_002_fr.md Normal file
View File

@ -0,0 +1,49 @@
## Quantifier précisément le besoin
### Identifiants
Pratique originale : [BP_002](https://github.com/cnumr/best-practices/blob/main/chapters/BP_002_en.md)
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 1. Spécification | Utilisateur/Terminal | PO/AMOA |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 5 | 4 | 5 |
|Ressources Economisées |
|:----------------------------------------------------------:|
|Processeur / Mémoire vive / Stockage / Réseau / Requêtes |
### Description
Les « dimensions » de chaque fonctionnalité doivent être définies précisément et dans leur ensemble. Il peut sagir
dun taux de compression pour les images de linterface graphique, du temps de réponse maximum pour une requête HTTP,
du nombre ditems affichés dans une liste, etc.
Plus les « dimensions » et exigences associées à chaque fonctionnalité collent au métier, plus on évite la surqualité.
La logique doit donc être inversée par rapport aux habitudes actuelles. Si une information nest pas précisée,
cest le niveau de qualité ou la quantité minimale qui est proposé. Par exemple, en labsence de précision,
le nombre ditems dune liste est limité à 5 éléments ou au nombre maximal affichable sur le plus petit écran cible de lapplication.
Les valeurs par défaut, rarement modifiées par l'utilisateur, doivent être choisies pour répondre au besoin avec un impact minimal.
### Exemple
Gain potentiel : en jouant sur le nombre ditems affichés sur la page de résultats de son moteur de recherche Bing,
Microsoft Research a démontré quil était possible de réduire jusquà 80 % linfrastructure physique (nombre de serveurs) sous-jacente.
Autre exemple : en utilisant par défaut une résolution de vidéo acceptable (480p) plutôt que maximale, on réduit la bande passante
utilisée pour la plupart des utilisateurs (qui ne changeront pas la valeur par défaut), tout en laissant la possibilité aux autres
d'augmenter la résolution s'ils en en ont le besoin.
### Principe de validation
| Le nombre ... | est égal à |
|-------------------|:-------------------------:|
| de fonctionnalités avec des dimensions supérieures au besoin | 0 |

43
chapters/MBP_003_fr.md Normal file
View File

@ -0,0 +1,43 @@
## Optimiser le parcours utilisateur
### Identifiants
Bonne pratique originale : [BP_003](https://github.com/cnumr/best-practices/blob/main/chapters/BP_003_fr.md)
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 2. Conception | Utilisateur/Terminal | UX/UI Designer |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 5 | 4 | 5 |
|Ressources Economisées |
|:----------------------------------------------------------:|
|Processeur / Mémoire vive / Stockage / Réseau / Requêtes |
### Description
Optimiser le parcours utilisateur consiste à diminuer le temps passé par l'utilisateur sur ses usages les plus fréquents. Dans un premier temps, cibler les parcours les plus fréquents puis optimiser leur usage : diminuer le nombre d'étapes, diminuer le nombre d'actions, supprimer l'inutile, identifier les cas d'échecs, optimiser les temps de réponse... Un parcours est bien conçu lorsque le programme se comporte exactement comme l'utilisateur l'avait imaginé.
A minima, sonder en observant son entourage utilisant le service est un bon moyen didentifier les points de friction - situations ou interactions qui contribuent à dégrader lexpérience utilisateur et à ralentir le parcours - des utilisateurs. Les tests utilisateurs permettent d'aller plus en profondeur dans la recherche de ces points de friction.
Le temps passé par l'utilisateur sur son terminal est le deuxième post en termes d'impacts environnementaux.
### Exemple
* Proposer, pour une application de grande distribution, une nouvelle commande sur la base du contenu de la précédente.
* Acheter sans inscription sur une application de type e-commerce.
* Copier/Coller son RIB directement plutôt que le télécharger puis le transférer.
* Mettre en avant les champs ou les filtres les plus utilisés.
### Principe de validation
| Le nombre de | est inférieur à ou égal à |
|-------------------|:-------------------------:|
| points de friction | 0 |

86
chapters/MBP_004_fr.md Normal file
View File

@ -0,0 +1,86 @@
## Préférer la saisie assistée à l'autocomplétion
### Identifiants
// TODO
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 1. Spécification | Utilisateur/Terminal | PO/AMOA |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 3 | 3 | 3 |
| Ressources Economisées |
|:----------------------:|
| Réseau |
### Description
L'autocomplétion, ou complément automatique est une fonctionnalité très répandue consistant à suggérer à l'utilisateur ou utilisatrice
des résultats correspondant à sa recherche pendant sa saisie. Par exemple, un site permettant de rechercher un itinéraire
va proposer « Paris », « Lyon Part-Dieu » et « Paray le Monial » quand la personne tape « Par ».
L'implémentation de l'autocomplétion consiste à envoyer une requête au serveur à chaque caractère saisi pour récupérer les
résultats correspondants. On peut donc avoir beaucoup de requêtes effectuées et beaucoup de ressources dépensées.
Dans la mesure du possible, cette fonctionnalité est à remplacer par la saisie assistée.
Cela consiste à guider la personne utilisatrice par un ensemble dinformations et dindices :
- Présentation du format attendu en grisé dans le champ de saisie (`placeholder`)
- Texte expliquant le format attendu
- Réaction de linterface avec un message derreur ou un changement de couleur et aide textuelle lorsque la saisie est incorrecte
- etc.
Les interactions liées à la saisie assistée sont gérées localement, ce qui réduit les échanges avec le serveur.
Pour l'exemple de la recherche d'itinéraire et de la complétion des villes, il est possible, en cas d'ambiguïté, de proposer
les différents résultats après la soumission du formulaire. L'utilisateur entre une chaine de caractère, par exemple « Lens »,
soumet le formulaire, et se voit à ce moment proposées différentes options : « Lens (France) », « Lens (Belgique) »,
« Loison sous Lens ».
Si le recours à l'autocomplétion ne peut pas être évité il est possible de minimiser le nombre de requêtes avec des optimisations simples :
- Ajouter un délai de quelque dixièmes de secondes entre l'ajout d'un caractère et la requête : cela permet de ne pas déclencher de requête si l'utilisateur n'a pas terminé sa saisie.
- Limiter le nombre de résultats affichés par l'autocomplétion, priorisés par une note de pertinence
- Fixer un nombre de caractères minimal avant de chercher à compléter.
- Si la taille de la base de données le permet, effectuer l'autocomplétion côté client en utilisant un composant natif comme `AutoCompleteTextView`
- Mettre en cache les résultats des recherches avec pour clef la chaîne saisie pour moins solliciter la base de données.
- Contextualiser les résultats pour en limiter le nombre.
### Exemple
Gain potentiel : à chaque fois que lon utilise la saisie assistée pour une fonctionnalité, plutôt que lautocomplétion, on réduit le nombre de requêtes associées par un facteur 10.
### Solution alternative
Si la donnée qui est proposée à l'utilisateur est assez petite en quantité, vous pouvez l'inclure directement dans le code de l'application en utilisant le composant natif `AutoCompleteTextView`.
```xml
<AutoCompleteTextView
android:id="@+id/auto_complete_country_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
```
```kotlin
val COUNTRIES = arrayOf("Belgium", "France", "Italy", "Germany", "Spain")
val countriesAdapter = ArrayAdapter<String>(
this,
R.layout.simple_dropdown_item_1line,
COUNTRIES,
)
binding.autoCompleteCountryTextView.setAdapter(countriesAdapter)
```
Ce composant proposera nativement, et sans aller/retour avec le serveur, un mécanisme d'autocompletion.
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|-------------------|:-------------------------:|
| de champs en autocomplétion | 20% |

48
chapters/MBP_005_fr.md Normal file
View File

@ -0,0 +1,48 @@
## Favoriser un design simple, épuré, adapté au mobile
### Identifiants
// TODO
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 2. Conception | Utilisateur/Terminal | UX/UI Designer |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 4 | 3 | 4 |
|Ressources Economisées |
|:----------------------------------------------------------:|
| Processeur / Mémoire vive / Réseau |
### Description
Tout design d'interface doit être réfléchi en amont, en prenant en compte :
- les besoins de l'utilisateur (voir la bonne pratique "Optimiser le parcours utilisateur")
- les heuristiques d'ergonomie (Bastien et Scapin, Nielsen, etc.)
- les contraintes techniques
- les bonnes pratiques d'écoconception
- et les bonnes pratiques d'accessibilité
**Privilégiez un design simple et épuré réalisable sans ajout de dépendances spécifiques.**
### Exemple
// TODO je ne comprends pas bien l'exemple.
**Description :** Certains sites contiennent des images encadrées, non contrastées et non lisibles (RGAA) et créent une surcharge mentale non nécessaire (2.2. Densité Informationnelle de Scapin et Bastien). Téléchargées, elles ne sont pourtant pas visibles sur mobile (écoconception). On peut parfois soulever l'incohérence entre signalétique et colorimétrie (1.2.2. Groupement/Distinction par le Format de Scapin et Bastien).
**Recommandation :** Supprimer les images de fond et ajouter un glyphe (Préférer les glyphes aux images, bonne pratique d'écoconception) avec une colorimétrie cohérente si un groupement doit avoir lieu.
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|-------------------|:-------------------------:|
| de pages dont le design est plus chargé que nécessaire | 0 |

101
chapters/MBP_006_fr.md Normal file
View File

@ -0,0 +1,101 @@
## Optimiser la récupération des données en fonction du cycle de vie
### Identifiants
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:------------------------:|
| 2. Conception | Utilisateur/Terminal | Developpeur/Développeuse |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 3 | 4 | 3 |
| Ressources Economisées |
|:----------------------:|
| Mémoire / Réseau |
### Description
Lorsqu'une personne navigue d'un écran à un autre au sein d'une application, mais également entre plusieurs applications sur son mobile, ces applications transitionnent entre différents états. Selon ces changements d'état, pour optimiser l'usage de la mémoire du téléphone, le système va effectuer des opérations telles que : libérer partiellement la mémoire si l'application passe en arrière-plan, supprimer les données en mémoire de l'écran en cours si l'utilisateur quitte celui-ci en appuyant par exemple sur le bouton retour.
Il convient d'optimiser l'accès aux données faites depuis une activité selon son cycle de vie, afin d'éviter d'effectuer inutilement des requêtes en base de données locale ou sur le réseau.
Il existe plusieurs bonnes pratiques à appliquer à différents niveaux :
- À l'échelle de la vue :
- En utilisant correctement les callbacks du cycle de vie fournies au sein d'une `Activity`.
- À l'échelle du `ViewModel` :
- En utilisant une classe de type `ViewModel` (par exemple via l'implémentation fournie par l'API **Jetpack ViewModel**) avec un cycle de vie plus long que celui de la vue.
#### A l'échelle de la vue :
Au sein d'une classe de type `Activity`, il est courant de surcharger une ou plusieurs méthodes du cycle de vie afin d'initialiser et d'adapter l'état de la vue (via les méthodes `onCreate`, `onResume`, `onPause`,`onStop`, etc.). Il est important d'éviter de récupérer inutilement les données depuis la base de données locale ou le réseau à chaque fois que la vue redevient visible et utilisable (état `RESUMED`), c'est-à-dire au sein de la fonction `onResume`.
Il convient plutôt d'initialiser l'état de la vue au sein de la fonction `onCreate` et de récupérer les données dont elle a besoin soit dans l'état `CREATED`, soit dans l'état `STARTED`.
Note : Dans certaines situations, il est conseillé de développer ses propres composants "lifecycle aware" afin d'être notifié de la même façon de ces changements d'états et de rendre le code plus maintenable.
#### A l'échelle du `ViewModel`
Une classe de type `ViewModel` a un cycle de vie d'une durée de vie plus longue qu'une `Activity` ou un `Fragment`.
Gérer et maintenir les données de la vue au sein de ce type de classe permet de les garder en mémoire durant toute la durée de vie de la vue. Typiquement lors des changements de configuration (ex : l'utilisateur change l'orientation de l'écran) ou lorsque la vue passe en arrière-plan. Les données dont la vue a besoin pour se restaurer suite à ces changements restent ainsi accessibles sans avoir besoin d'effectuer une nouvelle requête en base de données ou sur le serveur. L'accès en lecture et en écriture aux données en mémoire est plus rapide et moins consommateur. De plus, une fois que la vue associée au ViewModel est détruit, par exemple si l'utilisateur quitte l'écran en appuyant sur le bouton retour, le ViewModel est aussi détruit, libérant ainsi les données de la mémoire précédemment stockées.
### Exemple
Soit une vue d'une application affichant le profil d'un utilisateur :
`MainActivity.kt` :
```
class ProfileActivity : ComponentActivity() {
private val viewModel: ProfileViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Update the uiState
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collectLatest {
// TODO update view
}
}
}
}
}
```
`ProfileViewModel.kt` :
```
class ProfileViewModel(userRepository: UserRepository) : ViewModel() {
val uiState: StateFlow<ProfileUIState> = userRepository.userData.map {
ProfileUIState.Success(it)
}.stateIn(
scope = viewModelScope,
initialValue = ProfileUIState.Loading,
started = SharingStarted.WhileSubscribed(5_000),
)
}
sealed interface ProfileUIState {
data object Loading : ProfileUIState
data class Success(val user: User) : ProfileUIState
}
```
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|-----------------------------------------------------------------------------------------------------|:-----------------------:|
| d'écrans effectuant des chargements de données à chaque fois que la vue passe dans l'état `RESUMED` | 0 |
| d'écrans rechargeant l'ensemble des données lors d'un changement de configuration | 0 |
| d'écrans n'utilisant pas de ViewModel ou équivalent pour maintenir l'état de la vue plus longtemps | 0 |

46
chapters/MBP_007_fr.md Normal file
View File

@ -0,0 +1,46 @@
## Mettre en pause les traitements consommateurs lorsque l'application est en arrière-plan
### Identifiants
// TODO
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:------------------------:|
| 2. Conception | Utilisateur/Terminal | Developpeur/Développeuse |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 3 | 4 | 3 |
| Ressources Economisées |
|:----------------------:|
| Mémoire / Batterie |
### Description
Lorsqu'une personne navigue entre plusieurs applications sur son mobile, les applications basculent entre premier plan et arrière-plan. Lorsqu'une application qui effectue un traitement consommateur en mémoire passe en arrière-plan, il convient de s'appuyer sur son cycle de vie pour réduire l'impact de ce traitement sur la batterie du téléphone.
* Dans la fonction `onPause`, l'activité est toujours visible à l'écran, mais l'utilisateur ne peut plus interagir avec.
- Dans la fonction `onStop`, l'activité n'est plus visible.
Au sein de ses deux callbacks, une bonne pratique est de libérer ou d'ajuster les ressources consommatrices initialisées précédemment.
Quelques exemples de traitements consommateurs :
* L'affichage d'une animation.
* La lecture d'une vidéo ou d'un son.
* L'usage des capteurs du téléphone : le GPS, le gyroscope.
* L'usage de protocol de connectivité : Bluetooth, Wifi, NFC, etc.
### Exemple
* Soit une application utilisant la position GPS du téléphone. Lorsque l'activité n'est plus visible, il faut désactiver la mise à jour de la position dans la fonction `onStop`. Si cela n'est pas possible, une alternative est de changer la granularité de mise à jour de la position de "fine" à "grossière".
* Soit une activité `PhotoActivity` qui utilise l'appareil photo. Lorsqu'une autre activité prend le focus sur celle-ci par exemple sous forme de modale, mais que la première activité restent visible dessous (elle est donc dans l'état `PAUSED`), il faut appeler la fonction `camera?.release()` dans la callback `onPause`.
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|-----------------------------------------------------------------------------------------------------------|:-----------------------:|
| de traitements lourds pour lesquels la mémoire n'est pas libérée dans les fonctions `onPause` ou `onStop` | 0 |

32
chapters/MBP_008_fr.md Normal file
View File

@ -0,0 +1,32 @@
## Déployer un Android App Bundle (AAB) plutôt qu'un APK
### Identifiants
// TODO
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:-----:|:------------------------:|
| 2. Conception | Utilisateur/Terminal | Développeur/Développeuse |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 3 | 3 | 3 |
|Ressources Economisées |
|:----------------------------------------------------------:|
| Processeur / Mémoire vive / Stockage |
### Description
Un Android App Bundle (AAB) contient tout le code compilé et les ressources d'une application, mais délègue au Google Play Store la responsabilité de générer et signer l'APK qui sera installé sur l'appareil de l'utilisateur. Celui-ci optimise ainsi l'APK en n'y incluant que les ressources nécessaires selon la configuration de l'appareil. Cela permet de réduire considérablement la taille de l'application. Or la taille d'une application a un impact sur sa vitesse de chargement, la quantité de mémoire qu'elle utilise et sa consommation de la batterie.
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|----------------------------------------|:-------------------------:|
| d'APK déployé sans passer par un 'store' | 0 |

39
chapters/MBP_009_fr.md Normal file
View File

@ -0,0 +1,39 @@
## Supprimer les ressources non utilisées
### Identifiants
// TODO
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:-----:|:------------------------:|
| 4. Production | Utilisateur/Terminal | Développeur/Développeuse |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 3 | 3 | 3 |
|Ressources Economisées |
|:----------------------------------------------------------:|
| Processeur / Mémoire vive / Stockage |
### Description
Supprimer les ressources et les classes non utilisées permet de réduire la taille de l'application.
Il existe plusieurs moyens d'appliquer cela :
- Utiliser le linter inclus à Android Studio qui détecte les ressources (`res/`) non utilisées et les supprimer manuellement.
```shell
res/layout/item_row.xml: Warning: The resource R.layout.item_row appears
to be unused [UnusedResources]
```
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|------------------------------------------------|:-------------------------:|
| de fichiers non utilisés dans le package final | 0 |

65
chapters/MBP_010_fr.md Normal file
View File

@ -0,0 +1,65 @@
## Minifier le code
### Identifiants
// TODO
### Catégories
| Cycle de vie | Tiers | Responsable |
|:-------------:|:----:|:----:|
| 4. Production | Utilisateur/Terminal | Développeur/Développeuse |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 4 | 3 | 4 |
| Ressources Economisées |
|:----------------------:|
| Processeur / Mémoire vive / Stockage |
### Description
Utiliser un outil de minification de code permet de réduire considérable la taille d'une application.
Sur Android, cet outil se décompose en quatre processus :
1. La suppression du code non utilisée (appelé aussi "Code shrinking"). Ce processus supprime en particulier le code non utilisé au sein des bibliothèques tierces incluses dans notre projet, par exemple si notre code n'utilise qu'une petite partie de celles-ci. (paramètre `isMinifyEnabled`).
2. L'optimisation. Ce processus inspecte et optimize le code pour supprimer plus en détail les parties du code qui ne sont jamais exécutés. Par exemple il peut détecter que la branche `else` d'une condition n'est jamais empruntée, et donc la supprimer du code compilé.
3. L'obfuscation. Ce processus réduit le nom des classes et de ses membres, augmentant la sécurité et réduisant par la même occasion la taille des fichiers. Par exemple la classe : `com.monpackage.MaClass` devient une fois compilée : `a.a.B`. (paramètre `isMinifyEnabled`).
4. La suppression des ressources non utilisées dans notre code appelé aussi "Ressource shrinking" (paramètre `isShrinkResources`). Ce processus ne s'applique que si le paramètre `isMinifyEnabled` est activé.
Pour activer et configurer ces processus, il faut activer les paramètres `isShrinkResources` et `isMinfyEnabled` dans le fichier Gradle de l'application, dans les paramètres du build qui sera déployé. Au-delà de ses paramètres, il faut ensuite spécifier les processus qui doivent s'appliquer et sur quelles classes dans le fichier `proguard-rules.pro` du projet.
Cette pratique permet généralement de réduire la taille finale de l'application de moitié.
### Exemple
```kotlin
android {
buildTypes {
getByName("release") {
// Enables code shrinking, obfuscation, and optimization for the release build type
isMinifyEnabled = true
// Enables resource shrinking, which is performed by the Android Gradle plugin.
isShrinkResources = true
// Includes the default ProGuard rules files
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
...
}
```
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|----------------------------|:-----------------------:|
| de fichiers non minifiés | 25% |
| de ressources inutilisées | 25% |

41
chapters/MBP_011_fr.md Normal file
View File

@ -0,0 +1,41 @@
## Ne pas redimensionner les images coté application
### Identifiants
| GreenIT | V2 | V3 | V4 |
|:-------:|:----:|:----:|:----:|
| 93 | 20 | 34 | |
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 3. Réalisation (fabrication / développement) | Utilisateur/Terminal | Utilisateur |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 4 | 4 | 4 |
|Ressources Economisées |
|:----------------------------------------------------------:|
| Processeur / Réseau |
### Description
Lorsque le serveur fourni à l'application des images à afficher, ne pas redimensionner les images côté application. Cette approche impose en effet de transférer ces images dans leur taille originale, gaspillant ainsi de la bande passante et des cycles CPU.
Note : Si l'image à afficher est statique (c'est-à-dire qu'elle ne dépend pas de la donnée fournie par le serveur et qu'elle ne change pas dans le temps), il est encore mieux de directement l'inclure dans le code de l'application. Il convient alors de choisir un [format adéquat](MBP_012_fr.md) et de dimensionner cette image au plus juste.
### Exemple
Une image de 350 × 300 pixels encodée en PNG 24 pèse 41 Ko. Redimensionnée dans le code, la même image affichée en vignette à 70 × 60 pixels pèse toujours 41 Ko, alors quelle ne devrait pas dépasser 3 Ko ! Soit 38 Ko téléchargés à chaque fois pour rien…
La meilleure solution consiste à ce que le serveur génére des images à la taille à laquelle elles sont affichées.
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|---------------------------------------------|:-------------------------:|
| d'images redimensionnées dans l'application | 0 |

54
chapters/MBP_012_fr.md Normal file
View File

@ -0,0 +1,54 @@
## Eviter d'utiliser des images matricielles
### Identifiants
// TODO
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 5. Utilisation | Utilisateur/Terminal | Utilisateur |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 4 | 4 | 4 |
|Ressources Economisées |
|:----------------------------------------------------------:|
| Réseau |
### Description
Choisir le bon format dimage est crucial pour réduire la taille de l'application.
Par ailleurs, avec la multiplication des terminaux, des tailles décran et laugmentation de leur résolution, une approche vectorielle
doit être privilégiée par rapport à des images matricielles.
Grâce à cette bonne pratique, linterface est indépendante de la résolution de lécran. On limite donc aussi la dette technique.
La première règle consiste à remplacer les images matricielles (GIF, PNG, JPEG, WebP, etc.) par des images vectoriels (SVG), ou des icônes inclues dans la police de caractères.
Sil nest pas possible dutiliser ce format, il convient de convertir au moins les images de type GIF, PNG et JPEG en image de type WebP. En effet, ce dernier offre une compression meilleure et supporte également la gestion de transparence depuis Android 4.3 (API Level 18).
Android Studio permet de transformer rapidement des images de type PNG ou JPEG en format WebP. Il suffit de faire clic droit sur le fichier de l'image ou un répertoire contenant plusieurs images, et sélectionner "Convert to WebP".
Note : Il est intéressant de noter qu'un fichier SVG n'est pas inclus tel quel au sein d'un projet Android, mais qu'il est transformé en un fichier XML interprétable par le SDK Android via l'outil d'import d'Android Studio. Ce dernier va optimiser le contenu de ce fichier XML en supprimant les attributs inutiles et les commentaires qui pouvaient être inclus dans le fichier SVG.
### Exemple
![Différentes formes géométriques qui se superposent.](demo-image-format.png)
Cette image de 198 × 198 pixels pèse :
- 118 Ko dans un format matriciel non compressé ;
- 6,5 Ko en JPEG (compression à 90 %) ;
- 3,8 Ko en PNG ;
- 0,7 Ko en WebP (qualité d'encodage à 75%);
- 0,7 Ko en SVG.
Le format vectoriel est, dans ce cas précis, 5 à 10 fois moins lourd quun format matriciel tout en pouvant être redimensionné à linfini.
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|-------------------|:-----------------------:|
| d'images matricielles | 5 |

39
chapters/MBP_013_fr.md Normal file
View File

@ -0,0 +1,39 @@
## Utiliser le chargement paresseux
### Identifiants
// TODO
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 3. Réalisation (fabrication / développement) | Réseau | UX/UI Designer |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 4 | 4 | 5 |
|Ressources Economisées |
|:----------------------------------------------------------:|
| Processeur / Réseau |
### Description
Lorsquune personne ne consulte pas la totalité dun écran scrollable, pour éviter de charger toutes les ressources situées en dehors de la zone visible à l'écran, il est possible d'utiliser une [RecyclerView](https://developer.android.com/develop/ui/views/layout/recyclerview) (UI Toolkit original) ou un [Lazy layout](https://developer.android.com/jetpack/compose/lists#lazy) comme `LazyColumn` (Jetpack Compose). En effet, ces composants appliquent la technique du chargement paresseux (lazy loading) qui consiste à ne charger un élément que lorsque son emplacement devient visible à lécran.
### Anti-pattern
#### Ne pas imbriquer une liste au chargement paresseux au sein d'un layout défilant dans le même sens
Avec le UI Toolkit original, il est important de ne pas envelopper une `RecyclerView` au sein d'un layout de type `NestedScrollView` scrollant dans la même direction. En effet, ce dernier ayant besoin de connaitre ses dimensions pour être rendu à l'écran, va forcer la mesure de tous ses enfants, et donc le rendu de tous les enfants de la RecyclerView. L'avantage du chargement paresseux de la RecyclerView est alors perdu. Dans cette situation, il est préférable de considérer l'ensemble de la vue comme une `RecyclerView` pouvant accueillir différent type d'éléments.
Pour la même raison, avec Jetpack Compose, il ne faut pas envelopper un lazy layout au sein d'un layout standard scrollable dans la même direction. La bonne pratique a appliqué dans cette situation est documentée [ici](https://developer.android.com/jetpack/compose/lists#avoid-nesting-scrollable).
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|-------------------------------------------------------------|:-----------------------:|
| d'écrans défilables n'utilisant pas le chargement paresseux | 0 |

75
chapters/MBP_014_fr.md Normal file
View File

@ -0,0 +1,75 @@
## Ne pas recharger inutilement l'ensemble du contenu d'une liste si seulement une partie a changé
### Identifiants
// TODO
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 3. Réalisation (fabrication / développement) | Réseau | UX/UI Designer |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 4 | 4 | 5 |
|Ressources Economisées |
|:----------------------------------------------------------:|
| Processeur / Réseau |
### Description
Si une liste qui est affichée à l'écran peut subir des modifications dans le temps, il existe des solutions permettant d'indiquer au système Android de recharger uniquement les éléments de cette liste qui ont changé, plutôt que de recharger l'ensemble de la liste.
Avec le UI Toolkit original, deux solutions sont possibles :
1. Si nous connaissons la position des éléments modifiés, la classe `RecyclerView.Adapter` fournie les fonctions suivantes :
```kotlin
notifyItemChanged(int)
notifyItemInserted(int)
notifyItemRemoved(int)
notifyItemRangeChanged(int, int)
notifyItemRangeInserted(int, int)
notifyItemRangeRemoved(int, int)
```
A contrario, il faut éviter d'utiliser la fonction `notifyDataSetChanged()` qui va forcer le rechargement complet de la liste.
2. Si le code pour déterminer la position des éléments à modifier est complexe, `ListAdapter` intègre directement un mécanisme calculant la différence entre deux listes via `DiffUtil` pour ne modifier que les éléments concernés.
Avec Jetpack Compose :
1. L'association du `key` unique a chaque élément d'une `LazyList` permet à Compose d'optimiser la réutilisation des cellules d'une liste en cas de réagencement des éléments de la liste.
```kotlin
LazyColumn {
items(
items = datas,
key = { data ->
data.id
}
) { data ->
Cell(data)
}
}
```
2. étant donné que les collections de type `List`, `Set` et `Map` sont considéré comme instable par le compilateur, une bonne pratique consiste à utiliser la bibliothèque Kotlinx immutable collections ou d'annoter les paramètres de type liste par `@Immutable` ou `@Stable`. Cela permet de garantir au compilateur de la stabilité des paramètres et ainsi de réduire le nombre de recomposition inutile.
### Exemple
* [Article](https://medium.com/androiddevelopers/adapting-to-listadapter-341da4218f5b) expliquant la mise en place de `ListAdapter`
* [Lazy list keys - Jetpack Compose - Documentation](https://developer.android.com/develop/ui/compose/lists#item-keys)
* [Stabilité - Jetpack Compose - Documentation](https://developer.android.com/develop/ui/compose/performance/stability#summary)
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|-------------------------------------------------------------------------------------------|:-------------------------:|
| d'écran utilisant la fonction `notifyDataSetChanged` pour recharger les données d'une liste | 0% |
| de recomposition inutile d'une liste | 0% |

58
chapters/MBP_015_fr.md Normal file
View File

@ -0,0 +1,58 @@
## Optimiser le chargement paresseux d'un écran complexe comportant des listes imbriquées
### Identifiants
// TODO
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 3. Réalisation (fabrication / développement) | Réseau | UX/UI Designer |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 4 | 4 | 5 |
| Ressources Economisées |
|:----------------------:|
| Mémoire |
### Description
Il existe beaucoup d'applications qui affichent une liste verticale contenant plusieurs listes horizontales. Par exemple l'application du Play Store, ou encore des applications de streaming de vidéo comme Netflix. Ce type de design fonctionne très bien d'un point de vue UX, mais cela represente beaucoup de vues dans tous les sens.
Avec le UI Toolkit original, par défaut, chaque liste horizontale a sa propre `RecycledViewPool`. Une optimisation pouvant réduire l'usage de la mémoire consiste à partager le `RecycledViewPool` entre ces listes horizontal si elles affichent des vues similaires.
Un autre bonne pratique consiste à limiter le nombre d'éléments qui sont préchargés pour chaque liste horizontale appartenant à la liste verticale. Par exemple si visuellement pour une liste horizontale, nous avons toujours 3 éléments et demi visible à l'écran, nous pouvons appliquer sur le `LinearLayoutManager` de la liste horizontale `setInitialItemPrefetchCount(4)`. Cela permet ainsi d'indiquer à la liste verticale que dès qu'une nouvelle liste horizontale apparait à l'écran, il faut charger les 4 premiers éléments de la liste, permettant de soulager la mémoire utilisée à ce stade.
### Exemple
```kotlin
class OuterAdapter : RecyclerView.Adapter<OuterAdapter.ViewHolder>() {
...
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
// Inflate inner item, find innerRecyclerView by ID.
val innerLLM = LinearLayoutManager(parent.context, LinearLayoutManager.HORIZONTAL, false)
innerRv.apply {
layoutManager = innerLLM
recycledViewPool = sharedPool
}
return OuterAdapter.ViewHolder(innerRv)
}
...
```
Source : [Developer Android - Nested Recyclerviews](https://developer.android.com/topic/performance/vitals/render#recyclerview_nested_recyclerviews)
####
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|-------------------------------------------------------------|:------------------------:|
| d'écrans défilables n'utilisant pas le chargement paresseux | 0 |

38
chapters/MBP_016_fr.md Normal file
View File

@ -0,0 +1,38 @@
## Éviter les animations
### Identifiants
// TODO
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 2. Conception | Utilisateur/Terminal | UX/UI Designer |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 4 | 3 | 5 |
|Ressources Economisées |
|:----------------------------------------------------------:|
| Processeur / Mémoire vive |
### Description
Les animations peuvent être très coûteuses en termes de cycles CPU et de consommation mémoire, car elles déclenchent une phase de "Re-draw" voir de "Re-layout" à chaque frame (soit environ 50 fois par ms).
. Il faut donc éviter au maximum les animations, et ne les utiliser que lorsquelles sont indispensables.
Si vous ne pouvez pas vous passer dune animation, limitez-vous à l'animation de propriétés necessitant de ré-éxecuter uniquement la phase "Drawing". Par exemple : `alpha`, `translation`, `rotation`, `color`. A l'inverse, éviter l'animation de propriété nécessitant de ré-executer en plus la phase de "Layout". Par exemple : `padding`, `height`, `width`.
### Exemple
// TODO mettre des exemples en UI Toolkit et en Compose
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|------------------------|:-----------------------:|
| d'animations par écran | 1 |

30
chapters/MBP_017_fr.md Normal file
View File

@ -0,0 +1,30 @@
## Utiliser une bibliothèque que si celle-ci est réellement indispensable
### Identifiants
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 2. Conception | Réseau | Architecte Logiciel/Développeur |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 4 | 4 | 4 |
| Ressources Economisées |
|:----------------------:|
| Stockage / Mémoire |
### Description
Certaines bibliothèques prêtes à lemploi sont des outils pratiques pour faciliter le développement de certains composants ou la maintenabilité du code. Cependant, celles-ci pourraient parfois être facilement remplacé par des bibliothèques déjà présentes dans le projet, voir des API incluses dans le SDK Android de base. Il est intéressant de se poser régulièrement la question de l'utilité des bibliothèques incluses dans un projet.
Par ailleurs, l'application de la bonne pratique [Minifier le code](MBP_010_fr.md) permet d'inclure dans le package final uniquement les portions utilisées des dépendances de notre projet.
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|--------------------------------------------|:-------------------------:|
| de bibliothèques dont l'utilité est minime | 1 |

33
chapters/MBP_018_fr.md Normal file
View File

@ -0,0 +1,33 @@
## Supprimer les bibliothèques non utilisées
### Identifiants
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 2. Conception | Réseau | Architecte Logiciel/Développeur |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 4 | 4 | 4 |
| Ressources Economisées |
|:----------------------:|
| Stockage / Mémoire |
### Description
Au cours de la vie d'un projet, il peut arriver qu'une dépendance à une bibliothèque reste présente alors que celle-ci n'est plus du tout utilisée dans le code. Il convient de la supprimer.
Pour cela, il existe plusieurs outils. Par exemple :
* [Autonomous App - Dependency Analysis Gradle Plugin](https://github.com/autonomousapps/dependency-analysis-gradle-plugin)
À noter que cette pratique n'a peu de sens en terme d'optimisation de la taille de l'application si la bonne pratique [Minifier le code](MBP_010_fr.md) est déjà appliquée sur le projet. Cependant, elle a le mérite d'améliorer la maintenabilité du code permettant la gestion des dépendances de l'application.
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|--------------------------------|:-----------------------:|
| de bibliothèques non utilisées | 0 |

74
chapters/MBP_019_fr.md Normal file
View File

@ -0,0 +1,74 @@
## Jetpack Compose : Améliorer la "skipabilité" d'une fonction pour réduire le nombre de recomposition inutile
### Identifiants
//TODO
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 3. Réalisation (fabrication / développement) | Utilisateur/Terminal | Architecte Logiciel/Développeur |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 4 | 4 | 4 |
|Ressources Economisées |
|:----------------------------------------------------------:|
| Processeur |
### Description
L'une des optimisations faites par Jetpack Compose :
* Éviter le plus possible de recomposer un composant qui nest pas impacté par un changement détat.
Pour cela, Jetpack Compose calcule la "skipabilité" d'une fonction, en évaluant d'abord la **stabilité de ses paramètres**.
Stabilité des paramètres :
* **Immutable**
* **Stable** : Type mutable mais pour lequel Jetpack Compose sera notifié en cas de changement de l'une de ses propriétés publiques.
* **Unstable**
3 situations typiques où Jetpack Compose ne peut pas définir une fonction comme `skippable` :
1. La propriété est mutable à cause d'une variable mutable déclarée avec `var`
2. La propriété est de type `List`, `Set` ou `Map`. En effet, ces types sont considérés comme unstable par Compose.
3. La propriété utilise un type provenant d'une dépendance externe.
L'outil [Compose Compiler Report](https://developer.android.com/jetpack/compose/performance/stability/diagnose#compose-compiler) permet d'obtenir facilement un rapport analysant la stabilité des fonctions Composable d'un projet Android.
Il peut être intéressant de mener ce genre d'analyse sur les fonctions Composable les plus riches d'une application.
### Exemple
Pour améliorer la "skipabilité" d'une fonction Composable et ainsi ne pas trop sollicité le processeur, il faut :
1. Utiliser `val` plutôt que `var`.
2. Utiliser `ImmutableList` (KotlinX) :
```shell
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:${version}")
}
```
3. Marquer une data classe comme `@Immutable` si elle utilise un type provenant d'une dépendance externe
```kotlin
@Immutable
data class LogEntry {
val timestamp: LocalDateTime,
val log: String
)
```
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|-------------------------------------------------------------------------------------------|:-----------------------:|
| Fonctions Composables riches dans l'application dont la skipabilité n'a pas été optimisée | 40% |

43
chapters/MBP_020_fr.md Normal file
View File

@ -0,0 +1,43 @@
## Réduire l'overdraw
### Identifiants
// TODO
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 3. Réalisation (fabrication / développement) | Utilisateur/Terminal | Architecte Logiciel/Développeur |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 4 | 4 | 4 |
|Ressources Economisées |
|:----------------------------------------------------------:|
| Processeur |
### Description
Un pixel a l'écran peut être dessiné ("Draw") plusieurs fois durant le processus chargé d'afficher l'interface exacte à l'écran. Le fait de redessiner plusieurs fois un pixel est appelé "Overdraw". Pour réduire le nombre de Redraw et ainsi soulager le processeur, il convient d'appliquer ce genre de pratique :
* Ne pas ajouter un fond a un layout via la propriété `background` si celui-ci est ensuite recouvert complétement par ses enfants.
* Réduire l'imbrication de layout. Par exemple utiliser un `ConstraintLayout` plutôt que plusieurs `LinearLayout` imbriqués permet d'éviter ce genre de situation.
* Réduire le nombre de composants utilisant la transparence (propriété `alpha`). En effet, pour afficher un pixel en appliquant une valeur d'alpha, le processeur dessine d'abord le pixel sans alpha, avant de redessiner le pixel en appliquant le bon calcul pour la couleur.
### Exemple
* Pour afficher un texte en gris, plutôt que de définir la couleur du composant `TextView` en noir, et de lui appliquer un niveau de transparence via le paramètre `alpha`, définir plutôt la couleur du texte en gris directement.
Pour aller plus loin :
[Overdraw - Documentation](https://developer.android.com/topic/performance/rendering/overdraw)
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|-------------------|:-------------------------:|
| de modifications n'affectant pas le layout et occasionnant un repaint (ex: color, background, visibility) | 1 |

112
chapters/MBP_021_fr.md Normal file
View File

@ -0,0 +1,112 @@
## Mettre en cache des données qui coutent cher à instancier ou qui sont fréquemment consultées
### Identifiants
// TODO
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 3. Réalisation (fabrication / développement) | Utilisateur/Terminal | Architecte Logiciel/Développeur |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 4 | 3 | 4 |
|Ressources Economisées |
|:----------------------------------------------------------:|
| Processeur / Mémoire vive |
### Description
Lorsque des calculs de valeurs sont coûteux en ressources, les mettre en cache si les valeurs demeurent inchangées afin de ne pas réitérer ces opérations. De même, mettre en cache des données qui sont fréquemment consultées permet de réduire le nombre de lectures sur le disque ou sur le réseau.
Plusieurs options pour mettre en cache ce type de données. Par exemple :
A l'échelle de la vue :
* Au sein du `ViewModel` associée à la vue.
* Avec Jetpack Compose, les fonctions `remember{}` ou `rememberSaveable{}` permet de maintenir en cache un objet au delà des Recompositions de son composant parent.
Si cette valeur est utilisée par plusieurs écrans de notre application :
* Dans un singleton au niveau de la couche data, exposée par exemple via un observable (Kotlin Flow ou RX). Par exemple un singleton `ConfigManager` pourra exposer un observable contenant un objet `Config` contenant la configuration globale de l'application.
### Exemple
#### Avec Jetpack Compose
* Stocker en mémoire un objet qui coute cher à instancier (traitement trop lourd) :
_Avant :_
```kotlin
@Composable
fun MyComponent(){
/*...*/
val expensive = BigObject()
}
```
_Après :_
```kotlin
@Composable
fun MyComponent(){
/*...*/
val expensive = remember { BigObject() }
}
```
* Exécuter un traitement couteux sur un état uniquement lorsque cet état change
_Avant :_
```kotlin
Box(modifier = modifier.fillMaxSize()) {
LazyColumn(
state = listState,
reverseLayout = true,
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
items(
items = messages.sortedByDescending { it.dateTime }
) { message ->
ClickableChatMessage(
message = message
)
}
}
}
```
Problème, a chaque recomposition de ce composant, la liste de message est à nouveau triée. Même si celle-ci n'a pas changé.
_Après :_
```kotlin
Box(modifier = modifier.fillMaxSize()) {
val sortedMessages = remember(messages) {
messages.sortedByDescending { it.dateTime }
}
LazyColumn(
state = listState,
reverseLayout = true,
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
items(
items = sortedMessages
) { message ->
ClickableChatMessage(
message = message
)
}
}
}
```
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|-------------------|:-------------------------:|
| de données peu volatiles, demandant un calcul et accédées plusieurs fois, non mises dans un système de cache | 0 |

41
chapters/MBP_022_fr.md Normal file
View File

@ -0,0 +1,41 @@
## Economiser la bande passante en sauvegardant en base de données locale les données fournies par le serveur
### Identifiants
// TODO
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 4. Production | Utilisateur/Terminal | Architecte Logiciel/Développeur |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 3 | 2 | 4 |
|Ressources Economisées |
|:----------------------------------------------------------:|
| Réseau / Requêtes |
### Description
Les réponses d'un serveur qui restent inchangées dans un futur proche et qui sont utilisées à chaque fois que l'application est ouverte ne doivent pas être redemandées. Par conséquent, il faut les sauvegarder en base de données locale pour économiser de la bande passante.
Si besoin, lorsque des données sont fournie par un serveur distant, il peut être intéressant de définir une stratégie de mise en cache/base de données locale. Par exemple :
1. La première fois que l'application récupére une donnée du serveur, elle est stockée en cache afin d'être utilisée immédiatement par l'application.
2. En arrière-plan, la donnée est sauvegardée en base de données en incluant la date de récupération de celle-ci.
3. Plus tard, si l'application nécessite l'accès à nouveau à cette donnée et qu'elle n'est plus en cache : si en base de donnée la date de récupération de cette donnée est inférieure à X jours/minutes, celle-ci est à nouveau mise en case et fournie à la vue, sinon, la donnée est à nouveau récupérée du serveur et l'étape 1) ci-dessus est rejouée.
### Exemple
Si une requête retourne une liste de noms de villes ou de noms de contacts, il faut mettre ces réponses en base de données pour ne pas générer à nouveau une requête vers le serveur dès que cette liste doit être à nouveau affichée.
### Principe de validation
| Le pourcentage ... | est inférieur ou égal à |
|------------------------------------------------------------|:-----------------------:|
| de réponses du serveur non mises en base de données locale | 20% |

39
chapters/MBP_023_fr.md Normal file
View File

@ -0,0 +1,39 @@
## Réduire le volume de données stockées au strict nécessaire
### Identifiants
// TODO
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:-----------:|:----:|
| 2. Conception | Application | Architecte Logiciel/Développeur |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 4 | 4 | 4 |
|Ressources Economisées |
|:----------------------------------------------------------:|
| Stockage |
### Description
Réduire le volume de données stockées au nécessaire consiste à :
* Supprimer les données qui ne sont plus utilisées.
* Éviter les doublons entre les tables est un moyen doptimiser le volume de données stockées.
* Dans le cas de fonctionnalité permettant à l'utilisateur de télécharger du contenu volumineux, associer une durée de vie à ce contenu.
### Exemple
Par exemple une application de streaming qui propose le téléchargement d'un film ou d'une série peut :
* Associer une durée de vie de 30 jours au contenu. Une tâche est programmée 30 jours plus tard grâce au [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager) afin de supprimer le fichier.
* Supprimer le fichier une fois que le média a été visionné entièrement.
### Principe de validation
| Le nombre ... | est inférieur à |
|-------------------|:-------------------------:|
| de données stockées et non utiles pour le service numérique | 0 |

91
chapters/MBP_024_fr.md Normal file
View File

@ -0,0 +1,91 @@
## Concevoir et manipuler la base de données SQLite de manière optimale
### Identifiants
// TODO
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:--------------------:|:----:|
| 3. Réalisation (fabrication / développement) | Terminal utilisateur | Architecte Logiciel/Développeur |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 3 | 3 | 3 |
| Ressources Economisées |
|:-------------------------------------------------------:|
| Processeur / Mémoire vive |
### Description
Lors de la conception de la base de données SQLite locale, il existe plusieurs optimisations pouvant être faite permettant de réduire la consommation mémoire et les cycles processeurs nécessaires lors de l'exécution de certaines requêtes.
Par exemple :
* L'utilisation de `PRIMARY KEY` sur l'identifiant d'une donnée permet de bénéficier d'une recherche par identifiant optimisée (reposant sur l'utilisation en interne d'un [B-Tree](https://en.wikipedia.org/wiki/B-tree))
* Pour optimiser les requêtes sur un champ spécifique de type filtrage `WHERE`, tri `ORDER BY` ou regroupement `GROUP BY`, il peut être intéressant d'ajouter un index sur ce champ. Attention cependant a ne pas créer inutilement un index, car cette pratique a un coût en termes de stockage.
* Si la donnée à stocker est volumineuse, stocker plutôt cette donnée dans un fichier, et si besoin référencer le chemin vers ce fichier dans la base de donnée locale.
* Lors de la lecture, récupérer uniquement les données dont l'application a besoin.
* Utiliser au maximum les opérateurs de requêtes fournis par SQL comme les requêtes imbriquées ou les jointures plutôt que de faire ces manipulations dans le code. En effet, ces opérateurs sont optimisés.
* En cas d'insertion multiple de données, utiliser une transaction plutôt que d'effectuer unitairement chaque insertion.
### Exemple
* Soit une application permettant de gérer des contacts. Si notre application est susceptible de vouloir accéder à un contact précis à partir de son identifiant, écrire :
```sql
CREATE TABLE Contact(
id INTEGER PRIMARY KEY,
name TEXT,
city TEXT
);
```
plutôt que :
```sql
CREATE TABLE Contact(
id INTEGER,
name TEXT,
city TEXT
);
```
* Sur cette même application de gestion de contacts, les requêtes de ce style sont fréquemment exécutées :
```sql
SELECT id, name
WHERE city = 'Lille, France';
```
Pour optimiser ce genre d'opération, créer un index comme ceci :
```sql
CREATE INDEX city_index ON Contact(city);
```
* Récupérer les données dont nous avons besoin :
```sql
SELECT name
FROM Customers
LIMIT 10;
```
plutôt que :
```sql
SELECT *
FROM Customers;
```
Source : [SQLite performance best practices](https://developer.android.com/topic/performance/sqlite-performance-best-practices)
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|-------------------|:-------------------------:|
| de requêtes SQL à l'intérieur d'une boucle | 0 |

35
chapters/MBP_025_fr.md Normal file
View File

@ -0,0 +1,35 @@
## Mettre en cache les images fournies par le serveur
### Identifiants
// TODO
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:--------------------:|:----:|
| 3. Réalisation (fabrication / développement) | Terminal utilisateur | Architecte Logiciel/Développeur |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 3 | 3 | 3 |
| Ressources Economisées |
|:----------------------:|
| Réseau / Mémoire vive |
### Description
Lorsque le serveur fourni à l'application des images à afficher, les mettre en cache via des outils comme [Glide](https://github.com/bumptech/glide) ou [Coil](https://coil-kt.github.io/coil/) permet de ne pas re-solliciter inutilement le réseau lorsque ces images doivent à nouveau être affichées. De plus ces outils apporte des optimisations supplémentaires comme la réduction de la taille des images et leur compression, ainsi qu'un mécanisme de pause/annulation des requêtes si par exemple l'utilisateur quitte l'écran avant la fin du chargement.
### Exemple
// TODO
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|--------------------------------------------------------|:-----------------------:|
| d'images obtenues par le serveur et non mises en cache | 0 |

39
chapters/MBP_026_fr.md Normal file
View File

@ -0,0 +1,39 @@
## Assurer la compatibilité avec les plus anciens appareils du parc
### Identifiants
// TODO
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 3. Réalisation (fabrication / développement) | Utilisateur/Terminal | PO/AMOA |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 4 | 3 | 5 |
|Ressources Economisées |
|:----------------------------------------------------------:|
| Déchets électroniques |
### Description
Il faut s'assurer de la compatibilité d'une application avec les versions d'OS les plus anciennes, pour ne pas contribuer à l'obsolescence matérielle, forçant les utilisateurs à renouveler un appareil qui est encore en état de marche, générant alors inutilement un déchet électronique.
Prendre en compte ce critère dans le choix du `minSdk` lors de la configuration du projet est donc primordiale. Se baser sur les statistiques de répartition fournies par Google au sein d'Android Studio lors de la création d'un projet peut aider à la prise de décision, en gardant en tête qu'un utilisateur final préférera toujours avoir un service minimum que pas de service du tout.
### Exemple
En février 2024, configurer le `minSdk` d'une application à `21`, permet une compatibilité sur 99,3% des appareils sur le marché. Cette version permet également d'utiliser Jetpack Compose.
### Principe de validation
| Le nombre ... | est égal à |
|-------------------|:-------------------------:|
| de pages non testées sur les configurations les plus contraignantes du contexte projet | 0 |

42
chapters/MBP_027_fr.md Normal file
View File

@ -0,0 +1,42 @@
## Éviter les traitements trop complexes sur des données provenant d'un serveur
### Identifiants
// TODO
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 3. Réalisation (fabrication / développement) | Utilisateur/Terminal | Architecte Logiciel/Développeur |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 3 | 3 | 3 |
|Ressources Economisées |
|:----------------------------------------------------------:|
| Processeur / Mémoire vive / Réseau |
### Description
Dans le cas de traitements de données issues d'un serveur avec une logique plus ou moins complexe, il est déconseillé de récupérer les données "brutes" et de réaliser toutes les opérations de calcul, de transformation ou encore d'agrégation côté application.
Ces traitements doivent plutôt être réalisés au plus près de la donnée, côté serveur afin de :
- limiter la bande passante à cause du transfert de données non traitées
- profiter des optimisations de la base données distante sur la manipulation des données
- soulager le processeur du téléphone.
Dans ce genre de situation, il peut être intéressant de développer côté serveur un niveau d'API intermédiaire, spécifiquement pour l'application et réduisant la complexité des données. Ce pattern s'appelle Backend for Frontend (BFF).
### Exemple
// TODO
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|-------------------------------------------------------------------------------------|:-------------------------:|
| de traitements de données complexe exécutés en dehors du serveur | 1 |

40
chapters/MBP_028_fr.md Normal file
View File

@ -0,0 +1,40 @@
## Valider le code avec un Linter
### Identifiants
// TODO
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 3. Réalisation (fabrication / développement) | Utilisateur/Terminal | Architecte Logiciel/Développeur |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 3 | 3 | 2 |
|Ressources Economisées |
|:----------------------------------------------------------:|
| Processeur |
### Description
Lanalyse de qualité du code via **lint** permet de vérifier que les fichiers du projet Android ne présentent pas de bugs potentiels et propose des optimisations visant à améliorer la sécurité, les performances, l'utilisation,l'accessibilité et l'internationalisation d'une application.
Il est recommandé d'ajouter à votre processus de développement différents outils permettant d'assurer la qualité de votre code de manière.
Ce processus d'analyse peut être automatisé par exemple en étant exécutée avant chaque commit ([git pre-commit hook](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks)) et sur la CI.
Source :
[Android Developer - Improve your code with lint check](https://developer.android.com/studio/write/lint#pref)
### Exemple
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|-----------------------------------------------------|:-------------------------:|
| de lignes de code source non validées par un linter | 0 |

47
chapters/MBP_029_fr.md Normal file
View File

@ -0,0 +1,47 @@
## Supprimer les traductions non utilisées au sein des bibliothèques tierces (seulement pour les APK)
### Identifiants
// TODO
### Catégories
| Cycle de vie | Tiers | Responsable |
|:-------------:|:----:|:----:|
| 4. Production | Utilisateur/Terminal | Architecte Logiciel/Développeur |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 3 | 3 | 2 |
| Ressources Economisées |
|:----------------------:|
| Stockage |
### Description
Certaines bibliothèques de composant tel que Google Play Service ou Material, inclus automatiquement toutes les traductions des textes standard dans toutes les langues, même si l'application ne supporte que quelques langues. Cela augmente inutilement la taille de l'application.
Remarque : En livrant l'application au format AAB (Android App Bundle), par défaut, seuls les langues configurées sur l'appareil de l'utilisateur sont téléchargées. Cette pratique est donc utile uniquement si l'application est déployée via un APK.
Pour inclure uniquement les fichiers de langues que supporte l'application, il faut le spécifier dans le fichier gradle de l'application. Par exemple, pour inclure uniquement l'anglais et le français :
```kotlin
android {
defaultConfig {
...
resourceConfigurations.addAll(listOf("en", "fr"))
}
```
### Exemple
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|-------------------------------------------------------------------------------|:-------------------------:|
| de fichiers de ressources contenant des traductions de langues non supportées | 0 |

52
chapters/MBP_030_fr.md Normal file
View File

@ -0,0 +1,52 @@
## Favoriser les polices standards ou utiliser les "Downloadable font"
### Identifiants
// TODO
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 3. Réalisation (fabrication / développement) | Réseau | UX/UI Designer |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 4 | 3 | 4 |
| Ressources Economisées |
|:---------------------------------------------------------:|
| Stockage |
### Description
Plutôt que d'inclure les fichiers définissant une police d'écriture au sein de l'application, préférer la police standard Roboto, car elle est déjà présente sur l'appareil de lutilisateur.
On limite ainsi la taille de l'application.
Cependant si vous avez besoin d'une police particulière pour votre application, vous pouvez :
* Utiliser la fonctionnalité des [Downloadable Font](https://developer.android.com/develop/ui/views/text-and-emoji/downloadable-fonts) accessible dès API 14. même si c'est une Api font (google font, adobe font, etc.).
* Si vraiment vous devez inclure la font au package de l'application, voici quelques optimisations :
- Préférer une [variable font](https://developer.android.com/jetpack/compose/text/fonts#variable-fonts) si vous avez besoin de plus d'une font d'une police (ex: regular, bold, etc.)
- Optimiser la font en l'allégeant des caractères non nécessaires (exemple: une application disponible uniquement en français n'a besoin que de caractères latins, l'alphabet cyrillique peut donc être supprimé)
- Enfin, limiter leur nombre à deux maximum. Le moins possible étant le mieux (bonne pratique de design déjà reconnue).
### Exemple
Voici un tableau comparatif de poids des fonts de la police Oswald :
| | Regular | Regular + bold | Variable | Latin Regular | Latin Regular + Latin Bold | Latin Variable |
|------:|:-------:|:--------------:|:--------:|:-------------:|:--------------------------:|:--------------:|
| Poids | 37.6kb | 76.9kb | 66kb | 16kb | 32.5kb | ? |
Aller plus loin :
* [Android Developer Documentation - Work with font](https://developer.android.com/jetpack/compose/text/fonts)
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|-----------------------------------------------------|:-----------------------:|
| de polices inclues dans le package de l'application | 1 |

47
chapters/MBP_031_fr.md Normal file
View File

@ -0,0 +1,47 @@
## Éliminer les fonctionnalités non utilisées
### Identifiants
| GreenIT | V2 | V3 | V4 |
|:-------:|:----:|:----:|:----:|
| | | | |
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 1. Spécification | Utilisateur/Terminal | PO/AMOA |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 5 | 3 | 5 |
| Ressources Economisées |
|:-------------------------------------------------------:|
| Processeur / Mémoire vive / Stockage |
### Description
Éliminer les fonctionnalités non utilisées consiste à :
* Mesurer l'utilisation des fonctionnalités en production.
* Piloter lusage des fonctionnalités, et à supprimer celles qui ne sont pas assez utilisées ou qui ont perdu de la valeur.
Supprimer des fonctionnalités allège le poids de lapplication et sa maintenance.
Comment supprimer une fonctionnalité ?
* la désactiver : sur le principe du feature flipping, empêcher qu'elle soit utilisée avec un flag.
* la désinstaller : supprimer au maximum le code utilisé puis refactorer le code restant.
En fonction du coût environnemental et économique dune suppression, l'une ou l'autre solution sera retenue.
### Exemple
Une application de type e-commerce utilise deux listes différentes : liste de courses et liste de favoris. Lors de la refonte de l'application, au lieu de rester iso-fonctionnel, léquipe mesure la pertinence des fonctionnalités. La mesure montre que la liste de favoris est devenue désuète. Elle est donc supprimée ainsi que toutes les données en base.
### Principe de validation
| Le pourcentage ... | est inférieur ou égale à |
|-----------------------------------------------------------|:------------------------:|
| de fonctionnalités peu utilisées présentent en production | 10% |

37
chapters/MBP_032_fr.md Normal file
View File

@ -0,0 +1,37 @@
## Limiter les outils d'analytics et les données collectées
### Identifiants
| GreenIT | V2 | V3 | V4 |
|:-------:|:----:|:----:|:----:|
| | | | |
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 5. Utilisation | Utilisateur/Terminal | Utilisateur |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 4 | 3 | 4 |
|Ressources Economisées |
|:----------------------------------------------------------:|
| Processeur / Réseau / Stockage |
### Description
Les outils utilisés pour suivre les actions des utilisateurs implique l'exécution de requêtes supplémentaires et l'ajout de dépendances augmentant également la taille de l'application.
Si les informations issues de ce suivi ne sont pas indispensables, ne pas en utiliser. Sinon se limiter à un seul outil et privilégier
une solution qui consomme peu de ressources.
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|-------------------|:-------------------------:|
| d'outils d'analytics | 1 |

37
chapters/MBP_033_fr.md Normal file
View File

@ -0,0 +1,37 @@
## Utiliser la version la plus récente du langage Kotlin et du SDK Android
### Identifiants
// TODO
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:--------------------:|:----:|
| 2. Conception | Utilisateur/Terminal | Architecte Logiciel/Développeur |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 3 | 3 | 3 |
|Ressources Economisées |
|:----------------------------------------------------------:|
|Processeur / Mémoire vive |
### Description
Le langage Kotlin est régulièrement amélioré. Chaque nouvelle version apporte son lot de gain en termes de performances, de gestion mémoire, de stabilité et comble des failles de sécurité. Il est donc conseillé en règle générale d'utiliser la version la plus récente du langage pour bénéficier de ses apports.
De la même façon chaque nouvelle version d'Android introduit des améliorations en termes de sécurité et performance. Certaines de ces améliorations ne s'appliquent a une application que si celle-ci déclare explicitement cette nouvelle version en tant que `targetSdkVersion` dans sa configuration. Cibler la version la plus récente d'Android permet ainsi de bénéficier de ses évolutions sans empêcher l'utilisation de l'application sur des versions plus anciennes.
### Exemple
// TODO
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|-------------------|:-------------------------:|
| de versions majeures de retard sur la dernière version stable du langage | 1 |

39
chapters/MBP_034_fr.md Normal file
View File

@ -0,0 +1,39 @@
## Entretenir son application régulièrement
### Identifiants
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 5. Utilisation | Utilisateur/Terminal | Utilisateur |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 4 | 4 | 4 |
|Ressources Economisées |
|:----------------------------------------------------------:|
| Processeur / Mémoire vive / Réseau / Stockage |
### Description
Entretenir son application régulièrement est essentiel non seulement pour garantir son bon fonctionnement et améliorer l'expérience utilisateur, mais aussi pour réduire son empreinte environnementale. Avec le temps, le contenu et les fonctionnalités d'une application s'accumulent, et le risque d'afficher et stocker des informations inutiles, obsolètes voire contre-productives s'intensifie.
Cette mauvaise pratique est problématique à plusieurs niveaux, car elle peut :
- Donner une mauvaise image ;
- Ralentir son chargement et dégrader l'expérience utilisateur ;
- Perdre des utilisateurs en affichant plein d'informations inutiles/obsolètes ;
- Consommer inutilement des ressources pour le stockage de données inutilisées ;
- Être source d'attaques.
### Exemple
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
| ------------- | :---------------------: |
| de mises à jour et/ou de revues de contenus supérieures à 1 an | 0 |

43
chapters/MBP_035_fr.md Normal file
View File

@ -0,0 +1,43 @@
## Fournir une alternative textuelle aux contenus multimédias
### Identifiants
// TODO
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 3. Réalisation (fabrication / développement) | Utilisateur/Terminal | Utilisateur |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 4 | 3 | 5 |
|Ressources Economisées |
|:----------------------------------------------------------:|
| Réseau |
### Description
Le texte, utilise beaucoup moins de bande passante que des formats multimédias comme l'audio ou la vidéo.
Fournir aux utilisateurs une alternative textuelle à ces contenus leur permet s'ils le souhaitent de lire plutôt que d'écouter ou de visionner, et donc de transférer moins de données.
Si cette alternative textuelle a elle-même une taille importante, elle peut ne pas être chargée par défaut, mais suite à une action utilisateur.
Cette pratique est également bénéfique pour l'accessibilité : les mal entendants pourront lire le contenu et y auront donc accès, de même pour les mal voyants, si le texte inclut une description des éléments des vidéos qui ne sont que visibles.
Voir aussi sur le même sujet la pratique « Eviter la lecture automatique des vidéos et des sons »
### Exemple
Une video de 30 minutes va typiquement faire 500mo, un podcast de la même durée fera 30mo, et l'équivalent texte moins d'1 mo.
### Principe de validation
| Le pourcentage ... | est inférieur ou égal à |
|----------------------------------------------------|:-----------------------:|
| de fichiers multimédias sans alternative textuelle | 10% |

40
chapters/MBP_036_fr.md Normal file
View File

@ -0,0 +1,40 @@
## Éviter la lecture et le chargement automatique des vidéos et des sons
### Identifiants
| GreenIT | V2 | V3 | V4 |
|:-------:|:----:|:----:|:----:|
| | | | |
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 5. Utilisation | Utilisateur/Terminal | Architecte Logiciel/Développeur |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 4 | 3 | 4 |
|Ressources Economisées |
|:----------------------------------------------------------:|
|Processeur / Réseau / Requêtes |
### Description
L'activation automatique des vidéos et des sons au chargement d'un écran implique une utilisation de ressources supplémentaires.
Dans de nombreux cas, la lecture automatique n'est pas nécessaire.
D'autre part, elle attire l'attention de l'utilisateur et peut le détacher du service qui était demandé.
Ces lectures doivent donc dans la mesure du possible se faire à la demande de l'utilisateur.
### Exemple
// TODO
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|---------------------------------|:-------------------------:|
| de média en lecture automatique | 0 |

45
chapters/MBP_037_fr.md Normal file
View File

@ -0,0 +1,45 @@
## Adapter les sons aux contextes d'écoute
### Identifiants
| GreenIT | V2 | V3 | V4 |
|:-------:|:----:|:----:|:----:|
| | | | |
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 5. Utilisation | Utilisateur/Terminal | Utilisateur |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 2 | 2 | 3 |
| Ressources Economisées |
|:----------------------:|
| Réseau/Stockage |
### Description
Les fichiers audio peuvent être volumineux et consommateurs d'espace disque s'ils sont stockés localement ou de bande passante s'ils sont fournis par un serveur. Aussi il est indispensable den optimiser le poids. Privilégier 3 formats couvrant les 3 grandes plates-formes (Windows, Mac OS X et Linux) :
- MP3 (MPEG-1 Audio Layer 3) ;
- AAC (Advanced Audio Coding) ;
- OGG Vorbis.
Ces formats appliquent des algorithmes de compression très évolués permettant des gains de poids significatifs.
### Exemple
Des encodeurs comme LAME permettent de convertir au format MP3 des fichiers audio non compressés, mais également de jouer sur le taux déchantillonnage, afin de gagner encore du poids, au détriment de la qualité audio. À tester sur chaque fichier sonore.
Dans le cas dun fichier de référence WAV son.wav de 63 128 octets, sa conversion en MP3 donne :
- un fichier son-128.mp3 de 10 823 octets (128 kb/s), 6 fois plus léger ;
- un fichier son-64.mp3 de 6 508 octets (64 kb/s), 10 fois plus léger.
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|-------------------|:-------------------------:|
| de codecs audio propriétaires (non libres) et de pistes audio dont le ratio poids en mega octet/durée en minute est superieur a 1 | 0 |

40
chapters/MBP_038_fr.md Normal file
View File

@ -0,0 +1,40 @@
## Optimiser les PDF
### Identifiants
| GreenIT | V2 | V3 | V4 |
|:-------:|:----:|:----:|:----:|
| | | | |
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 5. Utilisation | Utilisateur/Terminal | Utilisateur |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 3 | 3 | 3 |
|Ressources Economisées |
|:----------------------------------------------------------:|
| Réseau / Stockage |
### Description
Sassurer, que les PDF sont réellement optimisés pour le mobile : taux déchantillonnage et de compression des images, polices incorporées, résolution…
De plus, étant donné que la lecture d'un PDF sur un mobile est peu aisée, à choisir, préférez intégrer une fonctionnalité dans votre application qui permet de télécharger ce PDF plutôt que d'incorporer un lecteur de PDF au sein de votre application. Vous obtiendrez ainsi les bénéfices suivants :
* Vous n'aurait pas besoin d'inclure ce lecteur de PDF à votre application, réduisant ainsi sa taille,
* L'utilisateur pourra choisir de l'ouvrir avec une application conçue spécialement pour la lecture,
* Vous améliorerez l'accessibilité de votre application, car les bibliothèques permettant d'inclure un PDF dans une application le sont rarement, contrairement aux applications dédiées qui offrent des fonctionnalités qui adaptent par exemple la taille du texte pour être lisible à l'écran sans devoir zoomer.
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|-----------------------------------------------------|:-------------------------:|
| de PDF non optimisés | 0 |
| de visionneuse de PDF inclue directement dans l'app | 0 |

35
chapters/MBP_039_fr.md Normal file
View File

@ -0,0 +1,35 @@
## Effectuer le téléchargement de fichiers volumineux via un réseau Wifi
### Identifiants
| GreenIT | V2 | V3 | V4 |
|:-------:|:----:|:----:|:----:|
| | | | |
### Catégories
| Cycle de vie | Tiers | Responsable |
|:---------:|:----:|:----:|
| 5. Utilisation | Utilisateur/Terminal | Utilisateur |
### Indications
| Degré de priorité | Mise en oeuvre | Impact écologique |
|:-------------------:|:-------------------------:|:---------------------:|
| 3 | 3 | 3 |
| Ressources Economisées |
|:----------------------:|
| Batterie |
### Description
Le téléchargement de données sur un réseau GSM consomme plus d'énergie que via un réseau Wi-fi. Cette énergie est fournie par la batterie du téléphone, qui selon la qualité du signal et la distance avec l'antenne émettrice doit parfois redoubler d'effort. Si votre application propose de télécharger depuis un serveur un contenu relativement volumineux (par exemple un fichier audio ou vidéo), il convient de laisser la possibilité à l'utilisateur d'effectuer cette action que s'il dispose d'une connexion Wi-fi
[Article - Does mobile battery drain faster on mobile data or Wi-fi](https://www.scienceabc.com/innovation/does-mobile-battery-drain-faster-on-mobile-data-or-wi-fi.html)
### Principe de validation
| Le nombre ... | est inférieur ou égal à |
|-------------------------------------------------------------------------------------------------------------------------------|:-------------------------:|
| de fonctionnalité proposant le téléchargement de fichiers volumineux ne s'effectuant pas systématiquement via un réseau Wi-fi | 0 |

36
chapters/MBP_WIP_fr.md Normal file
View File

@ -0,0 +1,36 @@
* // TODO : à écrire
// Don't access the Android UI toolkit from outside the UI thread. => risque de fuite mémoire (la vue est détruite avant que le traitement se termine, la référence a cet objet est maintenu)
// Limiter l'utilisation des Service (https://developer.android.com/topic/performance/memory#Services)
// Préférer l'utilisation de SparseArray, SparseBooleanArray, et LongSparseArray plutôt que `HashMap` (https://developer.android.com/topic/performance/memory#DataContainers)
// Abstraction à éviter si peu utile - https://developer.android.com/topic/performance/memory#Abstractions
// A creuser : https://developer.android.com/topic/performance/memory#Abstractions
// Notifications
// ABI ??
// Feature play store
// Utiliser correctement implementation testImplementation debugImplementation pour ne pas packager des librairies utilisées uniquement pour les tests ou le debug
// Reduce cost of inflation (ex ConstraintLayout instead of nested LinearLayout) https://developer.android.com/topic/performance/vitals/render#recyclerview_too_much_inflation_or_create_is_taking_too_long If your view types look good, look at reducing the cost of your inflation. Reducing unnecessary container and structural views can help. Consider building itemViews with ConstraintLayout, which can help reduce structural views.
* Monitorer la taille de l'application dans le temps (ex d'outil : https://github.com/JakeWharton/diffuse + le playstore)
* Reduire la taille de l'application : https://engineering.atspotify.com/2023/11/the-what-why-and-how-of-mastering-app-size/ https://medium.com/googleplaydev/shrinking-apks-growing-installs-5d3fcba23ce2
* [Limiter le recours aux canvas](/chapters/BP_4013_fr.md)
* [Coroutines / Scope / Lifecycle / annulation de traitement](/chapters/BP_4032_fr.md) // https://developer.android.com/topic/libraries/architecture/coroutines
* https://developer.android.com/kotlin/coroutines
* https://developer.android.com/kotlin/coroutines/coroutines-adv#main-safety
* [Préférer la pagination au défilement infini](/chapters/BP_4035_fr.md)
* Ne pas effectuer de traitements asynchrones conséquents sur le thread principal (Dispatcher.MAIN avec les coroutines)
Avoid delivering all of your assets and resources at install time if they aren't needed immediately. Reduce your app's install size by placing the largest files in dynamic feature module(s) and delivering them conditionally or on demand, or consider alternatives such as streaming media files. Learn more

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB