ajout de nouvelle practice
parent
977658caf3
commit
0cab6b3d21
131
README.md
131
README.md
|
@ -15,21 +15,13 @@ N'hésitez pas à lire [le guide des contributeurs](CONTRIBUTING.md).
|
|||
|
||||
## La liste des Bonnes Pratiques
|
||||
|
||||
|
||||
Produit :
|
||||
* [Éliminer les fonctionnalités non essentielles](chapters/MBP_001_fr.md)
|
||||
* [Quantifier précisément le besoin](chapters/MBP_002_fr.md)
|
||||
|
||||
|
||||
UI/UX :
|
||||
* [Optimiser le parcours utilisateur](chapters/MBP_003_fr.md)
|
||||
* [Préférer la saisie assistée à l'autocomplétion](chapters/MBP_004_fr.md)
|
||||
* [Favoriser un design simple, épuré, adapté au mobile](/chapters/MBP_005_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_006bis_fr.md)
|
||||
|
||||
|
||||
* [Stocker localement les données de configuration non liées aux données du serveur](chapters/MBP_007_fr.md) // TODO à discuter car côté mobile, beaucoup des données statiques sont déjà sur l'appareil.
|
||||
* [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)
|
||||
|
@ -39,111 +31,44 @@ UI/UX :
|
|||
* [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)
|
||||
* [avoriser 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 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_035_fr.md)
|
||||
|
||||
// TODO https://developer.android.com/topic/performance/reduce-apk-size
|
||||
// TODO
|
||||
* // TODO : à écrire
|
||||
// MAIN/IO thread ?
|
||||
// Repository (stratégie offline)
|
||||
// Eviter image non vectoriel (/chapters/BP_023_fr.md)
|
||||
// Etudier si l'usage de style/theme plutôt que paramètre individuel est plus efficace (BP_024_fr BP_025_fr)
|
||||
// Utiliser les paramètres de style abrégés // [Utiliser les notations CSS abrégées](/chapters/BP_026_fr.md)
|
||||
// Utiliser glide ou équivalent pour mettre en cache les images et éviter de les recharger
|
||||
// Notifications
|
||||
// ABI ??
|
||||
// Feature play store
|
||||
// Base de données: ne pas garder en base de données des données plus utilisées
|
||||
// Si fonctionnalité de mise en offline de fichier volumineux (ex media), associer une durée de vie.
|
||||
|
||||
// Liste performance (notifyDataSetChange, id in lazy list to avoir rendering everything. Type in lazy list)
|
||||
https://developer.android.com/topic/performance/vitals/render#recyclerview_notifydatasetchanged
|
||||
|
||||
// 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.
|
||||
|
||||
// TODO pas encore fait la suite
|
||||
* [N'utilisez que les portions indispensables des librairies JavaScript et frameworks CSS](/chapters/BP_040_fr.md)
|
||||
* [Ne pas faire de modification du DOM lorsqu’on le traverse](/chapters/BP_041_fr.md)
|
||||
* [Rendre les éléments du DOM invisibles lors de leur modification](/chapters/BP_042_fr.md)
|
||||
* [Réduire au maximum le repaint (appearence) et le reflow (layout)](/chapters/BP_043_fr.md)
|
||||
// TODO pas encore fait mais applicable au mobile
|
||||
* [Utiliser la délégation d'évènements](/chapters/BP_044_fr.md)
|
||||
* [Modifier plusieurs propriétés CSS en 1 seule fois](/chapters/BP_045_fr.md)
|
||||
* [Valider votre code avec un Linter](/chapters/BP_046_fr.md)
|
||||
* [Mettre en cache les objets souvent accédés en JavaScript](/chapters/BP_049_fr.md)
|
||||
* [Réduire les accès au DOM via JavaScript](/chapters/BP_054_fr.md)
|
||||
* [Utiliser tous les niveaux de cache du CMS](/chapters/BP_057_fr.md)
|
||||
* [Optimiser et générer les médias avant importation sur un CMS](/chapters/BP_058_fr.md)
|
||||
* [Encoder les sons en dehors du CMS](/chapters/BP_060_fr.md)
|
||||
* [Mettre en cache les données calculées souvent utilisées](/chapters/BP_064_fr.md)
|
||||
* [Supprimer tous les warning et toutes les notices](/chapters/BP_070_fr.md)
|
||||
* [Éviter d'effectuer des requêtes SQL à l’intérieur d’une boucle](/chapters/BP_072_fr.md)
|
||||
* [Ne se connecter à une base de données que si nécessaire](/chapters/BP_073_fr.md)
|
||||
* [Optimiser les requêtes aux bases de données](/chapters/BP_075_fr.md)
|
||||
* [Éviter le transfert d'une grande quantité de données pour réaliser un traitement](/chapters/BP_076_fr.md)
|
||||
* [Minifier les fichiers CSS, JavaScript, HTML et SVG](/chapters/BP_077_fr.md)
|
||||
* [Compresser les fichiers CSS, JavaScript, HTML et SVG](/chapters/BP_078_fr.md)
|
||||
* [Combiner les fichiers CSS et JavaScript](/chapters/BP_079_fr.md)
|
||||
* [Optimiser les images](/chapters/BP_080_fr.md)
|
||||
* [Optimiser la taille des cookies](/chapters/BP_082_fr.md)
|
||||
* [Favoriser HSTS Preload list aux redirections 301](/chapters/BP_084_fr.md)
|
||||
* [Mettre en place un plan de fin de vie du site](/chapters/BP_085_fr.md)
|
||||
* [Choisir un hébergeur "éco-responsable"](/chapters/BP_086_fr.md)
|
||||
* [Privilégier un fournisseur d'électricité écoresponsable](/chapters/BP_087_fr.md)
|
||||
* [Adapter la qualité de service et le niveau de disponibilité](/chapters/BP_088_fr.md)
|
||||
* [Utiliser des serveurs virtualisés](/chapters/BP_089_fr.md)
|
||||
* [Optimiser l'efficacité énergétique des serveurs](/chapters/BP_090_fr.md)
|
||||
* [Installer le minimum requis sur le serveur](/chapters/BP_091_fr.md)
|
||||
* [Mettre les caches entièrement en RAM (opcode et kvs)](/chapters/BP_092_fr.md)
|
||||
* [Stocker les données dans le cloud](/chapters/BP_093_fr.md)
|
||||
* [Héberger les ressources (CSS/JS) sur un domaine sans cookie](/chapters/BP_094_fr.md)
|
||||
* [Éviter les redirections](/chapters/BP_095_fr.md)
|
||||
* [Afficher des pages d'erreur statiques](/chapters/BP_096_fr.md)
|
||||
* [Utiliser un serveur asynchrone](/chapters/BP_097_fr.md)
|
||||
* [Utiliser un CDN](/chapters/BP_098_fr.md)
|
||||
* [Utiliser un cache HTTP](/chapters/BP_099_fr.md)
|
||||
* [Ajouter des entêtes Expires ou Cache-Control](/chapters/BP_101_fr.md)
|
||||
* [Mettre en cache les réponses Ajax](/chapters/BP_102_fr.md)
|
||||
* [Réduire au nécessaire les logs des serveurs](/chapters/BP_103_fr.md)
|
||||
* [Désactiver le DNS lookup d’Apache](/chapters/BP_104_fr.md)
|
||||
* [Apache Vhost : désactiver le AllowOverride](/chapters/BP_105_fr.md)
|
||||
* [Désactiver les logs binaires](/chapters/BP_106_fr.md)
|
||||
* [Compresser les documents](/chapters/BP_107_fr.md)
|
||||
* [Optimiser les PDF](/chapters/BP_108_fr.md)
|
||||
* [Limiter les e-mails lourds et redondants](/chapters/BP_109_fr.md)
|
||||
* [N'utiliser que des fichiers double opt-in](/chapters/BP_110_fr.md)
|
||||
* [Limiter la taille des e-mails envoyés](/chapters/BP_111_fr.md)
|
||||
* [Adapter les sons aux contextes d'écoute](/chapters/BP_112_fr.md)
|
||||
* [Adapter les textes au web](/chapters/BP_113_fr.md)
|
||||
* [Adapter les vidéos aux contextes de visualisation](/chapters/BP_114_fr.md)
|
||||
* [Limiter les outils d'analytics et les données collectées](/chapters/BP_4001_fr.md)
|
||||
* [Limiter l'utilisation des GIFs animés](/chapters/BP_4002_fr.md)
|
||||
* [Éviter la lecture et le chargement automatique des vidéos et des sons](/chapters/BP_4003_fr.md)
|
||||
* [Utiliser les compartiments CSS](/chapters/BP_4004_fr.md)
|
||||
* [Fournir une alternative textuelle aux contenus multimédias](/chapters/BP_4005_fr.md)
|
||||
* [Privilégier HTTP/2 à HTTP/1](/chapters/BP_4006_fr.md)
|
||||
* [Économiser de la bande passante grace à un ServiceWorker](/chapters/BP_4007_fr.md)
|
||||
* [Mettre en place un sitemap efficient](/chapters/BP_4008_fr.md)
|
||||
* [Assurer la compatibilité avec les plus anciens appareils et logiciels du parc](/chapters/BP_4009_fr.md)
|
||||
* [Réduire le volume de données stockées au strict nécessaire](/chapters/BP_4011_fr.md)
|
||||
* [Optimiser les PDF](/chapters/BP_108_fr.md) // proposer le téléchargement de PDF plutôt que d'inclure un viewer de PDF (=> lib en moins, visibilité de toute façon inadaptée au mobile, peu accessible, personnes ont déjà une app permettant de lire les PDF)
|
||||
* [Adapter les sons/vidéo aux contextes d'écoute](/chapters/BP_112_fr.md) // et 114
|
||||
* [Mettre en place une politique d'expiration et suppression des données](/chapters/BP_4012_fr.md)
|
||||
* [Limiter le recours aux canvas](/chapters/BP_4013_fr.md)
|
||||
* [S'assurer que les parcours utilisateurs permettent de réaliser leur action prévue](/chapters/BP_4014_fr.md)
|
||||
* [Avoir un titre de page et une metadescription pertinents avec le contenu de la page](/chapters/BP_4015_fr.md)
|
||||
* [Utiliser la version la plus récente du langage](/chapters/BP_4016_fr.md)
|
||||
* [Ne charger des données/du code que lorsqu'elles sont/il est nécessaire](/chapters/BP_4017_fr.md)
|
||||
* [Éliminer les fonctionnalités non utilisées](/chapters/BP_4018_fr.md)
|
||||
* [Préférer une PWA à une application mobile native similaire au site web](/chapters/BP_4019_fr.md)
|
||||
* [Éviter les temps de blocages par des traitements JavaScript trop longs](/chapters/BP_4020_fr.md)
|
||||
* [Mettre en place une architecture élastique](/chapters/BP_4021_fr.md)
|
||||
* [Limiter le nombre d'appels aux API HTTP](/chapters/BP_4022_fr.md)
|
||||
* [Limiter le recours aux carrousels](/chapters/BP_4030_fr.md)
|
||||
* [Avoir une stratégie de fin de vie des contenus](/chapters/BP_4031_fr.md)
|
||||
* [Mettre en place un "Circuit breaker"](/chapters/BP_4032_fr.md)
|
||||
* [Favoriser le "Request collapsing"](/chapters/BP_4033_fr.md)
|
||||
* [S’appuyer sur les services managés](/chapters/BP_4034_fr.md)
|
||||
* [Coroutines / Scope / Lifecycle / annulation de traitement](/chapters/BP_4032_fr.md) // https://developer.android.com/topic/libraries/architecture/coroutines
|
||||
* [Préférer la pagination au défilement infini](/chapters/BP_4035_fr.md)
|
||||
* [Entretenir son site régulièrement](/chapters/BP_4036_fr.md)
|
||||
* [Limiter le nombre d'extensions dans un CMS](/chapters/BP_4037_fr.md)
|
||||
* [Sécuriser l'accès à l'administration](/chapters/BP_4038_fr.md)
|
||||
* [Ne pas afficher les documents à l'intérieur des pages](/chapters/BP_4039_fr.md)
|
||||
|
||||
|
||||
## Licence
|
||||
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
## Optimiser les PDF
|
||||
|
||||
### Identifiants
|
||||
|
||||
| GreenIT | V2 | V3 | V4 |
|
||||
|:-------:|:----:|:----:|:----:|
|
||||
| 98 | 109 | 108 | |
|
||||
|
||||
### 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
|
||||
|
||||
S’assurer, avant leur mise en ligne, que les PDF sont réellement optimisés pour le Web : taux d’échantillonnage et de compression des images, polices incorporées, résolution…
|
||||
|
||||
Le cas échéant, proposer le téléchargement des PDF chapitre par chapitre.
|
||||
|
||||
Si vous souhaitez offrir à l’utilisateur de télécharger un lecteur PDF, préférer un logiciel léger tel que Sumatra (4,3 Mo) au lecteur d’Adobe (48 Mo), soit une bande passante divisée par 10 à chaque téléchargement et, surtout, une plus faible consommation de mémoire vive (ce qui permet de lutter contre la fracture numérique et l’obsolescence programmée).
|
||||
|
||||
### Exemple
|
||||
|
||||
Pour un rapport annuel en PDF :
|
||||
- vérifier que les images sont fortement compressées et à une résolution maximale de 72 dpi ;
|
||||
- n’inclure que les principales polices ;
|
||||
- découper le rapport en chapitres, afin de limiter les téléchargements inutiles.
|
||||
|
||||
|
||||
### Principe de validation
|
||||
|
||||
| Le nombre ... | est inférieur ou égal à |
|
||||
|-------------------|:-------------------------:|
|
||||
| de PDF non optimisés | 0 |
|
|
@ -1,46 +0,0 @@
|
|||
## 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, ces 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 traitement lourds pour lesquels la mémoire n'est pas libéré dans les fonctions `onPause` ou `onStop` | 0% |
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
## Stocker localement les données de l'utilisateur
|
||||
## Mettre en pause les traitements consommateurs lorsque l'application est en arrière-plan
|
||||
|
||||
### Identifiants
|
||||
|
||||
|
@ -6,36 +6,41 @@
|
|||
|
||||
### Catégories
|
||||
|
||||
| Cycle de vie | Tiers | Responsable |
|
||||
|:---------:|:----:|:----:|
|
||||
| 3. Réalisation (fabrication / développement) | Datacenter | Architecte Logiciel/Développeur |
|
||||
| Cycle de vie | Tiers | Responsable |
|
||||
|:---------:|:----:|:------------------------:|
|
||||
| 2. Conception | Utilisateur/Terminal | Developpeur/Développeuse |
|
||||
|
||||
### Indications
|
||||
|
||||
| Degré de priorité | Mise en oeuvre | Impact écologique |
|
||||
|:-------------------:|:-------------------------:|:---------------------:|
|
||||
| 4 | 3 | 4 |
|
||||
| 3 | 4 | 3 |
|
||||
|
||||
|Ressources Economisées |
|
||||
|:----------------------------------------------------------:|
|
||||
| Réseau / Requêtes |
|
||||
| Ressources Economisées |
|
||||
|:----------------------:|
|
||||
| Mémoire / Batterie |
|
||||
|
||||
### Description
|
||||
|
||||
Il est inutile de stocker sur le serveur des données qui n'ont de sens que pour un utilisateur donné.
|
||||
Lorsqu'une personne navigue entre plusieurs applications sur son mobile, ces 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.
|
||||
|
||||
Il existe différents moyens permettant de stocker ce type de données en local sur un téléphone :
|
||||
* Le stockage sous forme de clé valeur, grâce par exemples aux SharedPreferences.
|
||||
* Le stockage au sein de la base de données locale (via SQLite ou Room par exemple).
|
||||
Au sein de ses deux callbacks, une bonne pratique est de libérer ou d'ajuster les ressources consommatrices initialisées précédemment.
|
||||
|
||||
L’intérêt du stockage local est double. D’une part, on évite les allers-retours inutiles avec le serveur, ce qui économise des ressources et du temps de réponse.
|
||||
D’autre part, comme les données sont locales, il est plus facile et plus rapide de les manipuler au sein de l’interface.
|
||||
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.
|
||||
|
||||
Le gain potentiel est la réduction de la charge serveur, donc du nombre d’équipements nécessaires (de leur empreinte environnementale et économique),
|
||||
des serveurs HTTP jusqu’aux serveurs de base de données.
|
||||
### 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 données statiques non stockées localement | 25% |
|
||||
| Le nombre ... | est inférieur ou égal à |
|
||||
|----------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------:|
|
||||
| de traitement lourds pour lesquels la mémoire n'est pas libéré dans les fonctions `onPause` ou `onStop` | 0% |
|
||||
|
||||
|
|
|
@ -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 à l’emploi 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 |
|
|
@ -0,0 +1,35 @@
|
|||
## 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 :
|
||||
* [Fair gradle analysis](https://plugins.gradle.org/plugin/com.faire.gradle.analyze)
|
||||
* [Autonomous App - Dependency Analysis Gradle Plugin](https://github.com/autonomousapps/dependency-analysis-gradle-plugin)
|
||||
* [Nebula - Gradle Lint Plugin](https://github.com/nebula-plugins/gradle-lint-plugin/wiki/Unused-Dependency-Rule)
|
||||
|
||||
À 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 |
|
|
@ -0,0 +1,72 @@
|
|||
## 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 n’est 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.
|
||||
|
||||
### 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 à |
|
||||
|-------------------|:-------------------------:|
|
||||
| de modifications n'affectant pas le layout et occasionnant un repaint (ex: color, background, visibility) | 1 |
|
|
@ -0,0 +1,56 @@
|
|||
## 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'éviter ce genre de pratique :
|
||||
* 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 de la `TextView` en noir, et de lui appliquer un alpha
|
||||
|
||||
For example, you can get gray text by drawing black text in a TextView with a translucent alpha value set on it. However, you can get the same effect with better performance by drawing the text in gray.
|
||||
|
||||
|
||||
A pixel on the screen can be drawn multiple times during the process of showing the exact layout of the UI in a frame, These multiple draws on pixel called as overdraw, represented in four stages which are 1x, 2x, 3x and 4x. It doesn’t mean that x number of nested view groups will cause x number of overdraws. In other words, we may have a complicated nested view group, but there may be no overdraws at all. It is not about the nested view groups but the pixel that is used to draw the UI element. If a pixel is drawn for the first time we call it a “true color”, if the same pixel is drawn the second time, it is called as 1x overdraw. As many times as a pixel has drawn on top of an existing pixel, that many overdraws are made. So drawing it 1 time after the original drawing means the UI has 1x overdraws, 2 times means 2x overdraws and so on. Overdraw is unavoidable sometimes, but it has to be kept at a minimum.
|
||||
|
||||
Le repaint est le changement d’apparence d’un élément du DOM (Document Object Model), tandis que le reflow est le changement/recalcul de la position des éléments dans le DOM. Ces deux opérations sont coûteuses en ressources, notamment en cycles CPU : il faut donc éviter de les déclencher.
|
||||
|
||||
### Exemple
|
||||
|
||||
Pour éviter les repaint, ne pas modifier les propriétés stylistiques d’un élément (couleur de fond, style de bordure, couleur du texte, taille, etc.).
|
||||
|
||||
Pour éviter les reflow, limiter les changements de propriétés de position, de dimension, de type de positionnement, de contenu, etc. Cette suggestion est notamment valable pour certains éléments HTML tels que les tables, dont le reflow peut nécessiter jusqu’à trois fois plus de temps qu’un élément équivalent avec un block display.
|
||||
|
||||
Pour aller plus loin :
|
||||
https://developers.google.com/speed/articles/reflow
|
||||
|
||||
|
||||
### 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 |
|
|
@ -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 |
|
||||
|:---------:|:----:|:----:|
|
||||
| 2. Conception | Réseau | 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 |
|
|
@ -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 nombre ... | est inférieur ou égal à |
|
||||
|--------------------------------------------------------|:-----------------------:|
|
||||
| de réponse du serveur non mis en base de données local | 20% |
|
|
@ -0,0 +1,37 @@
|
|||
## 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 d’optimiser 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 associe 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.
|
||||
|
||||
### Principe de validation
|
||||
|
||||
| Le nombre ... | est inférieur à |
|
||||
|-------------------|:-------------------------:|
|
||||
| de données stockées et non utiles pour le service numérique | 0 |
|
|
@ -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 |
|
|
@ -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'image obtenue par le serveur et non mise en cache | 0 |
|
|
@ -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 |
|
|
@ -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 |
|
||||
|:---------:|:----:|:----:|
|
||||
| 2. Conception | Réseau | 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 |
|
|
@ -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
|
||||
|
||||
L’analyse 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 |
|
|
@ -0,0 +1,62 @@
|
|||
## 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 :
|
||||
|
||||
```kt
|
||||
android {
|
||||
defaultConfig {
|
||||
...
|
||||
resourceConfigurations.addAll(listOf("en", "fr"))
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
For example, if you are using a library that includes language resources (such as AppCompat or Google Play Services), then your app includes all translated language strings for the messages in those libraries whether the rest of your app is translated to the same languages or not. If you'd like to keep only the languages that your app officially supports, you can specify those languages using the resConfig property. Any resources for languages not specified are removed.
|
||||
|
||||
The following snippet shows how to limit your language resources to just English and French:
|
||||
|
||||
Kotlin
|
||||
Groovy
|
||||
|
||||
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ée | 0 |
|
|
@ -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 est est déjà présente sur l'appareil de l’utilisateur.
|
||||
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 |
|
|
@ -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 l’usage 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 l’application 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 d’une 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 nombre ... | est inférieur ou égale à |
|
||||
|-------------------|:-------------------------:|
|
||||
| de fonctionnalités peu utilisées présentent en production | 10% |
|
|
@ -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 |
|
|
@ -0,0 +1,37 @@
|
|||
## Utiliser la version la plus récente du langage 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és. Chaque nouvelle 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 terme 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 ces é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 |
|
|
@ -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 |
|
|
@ -0,0 +1,44 @@
|
|||
## 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.
|
||||
|
||||
Cette pratique est également bénéfique pour le référencement, les moteurs de recherche pouvant plus facilement analyser le texte que l'audio et la vidéo.
|
||||
|
||||
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 nombre ... | est inférieur ou égal à |
|
||||
|-------------------|:-------------------------:|
|
||||
| de fichiers multimédias sans alternative textuelle | 10% |
|
|
@ -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 |
|
Loading…
Reference in New Issue