En tant qu'auteur de bibliothèque, vous devez vous assurer que les développeurs d'applications peuvent facilement intégrer votre bibliothèque dans leur application tout en maintenant une expérience utilisateur finale de haute qualité. Vous devez vous assurer que votre bibliothèque est compatible avec l'optimisation Android sans configuration supplémentaire, ou indiquer que la bibliothèque peut ne pas être adaptée à une utilisation sur Android.
Cette documentation s'adresse aux développeurs de bibliothèques publiées, mais peut également être utile aux développeurs de modules de bibliothèque internes dans une grande application modularisée.
Si vous êtes développeur d'applications et que vous souhaitez en savoir plus sur l'optimisation de votre application Android, consultez Activer l'optimisation des applications. Pour savoir quelles bibliothèques utiliser, consultez Choisir judicieusement les bibliothèques.
Utiliser la génération de code plutôt que la réflexion
Dans la mesure du possible, utilisez la génération de code (codegen) plutôt que la réflexion. La génération de code et la réflexion sont deux approches courantes pour éviter le code passe-partout lors de la programmation, mais la génération de code est plus compatible avec un optimiseur d'application comme R8 :
- Avec la génération de code, le code est analysé et modifié pendant le processus de compilation. Comme il n'y a pas de modifications majeures après la compilation, l'optimiseur sait quel code est finalement nécessaire et ce qui peut être supprimé sans risque.
- Avec la réflexion, le code est analysé et manipulé au moment de l'exécution. Étant donné que le code n'est pas réellement finalisé tant qu'il n'est pas exécuté, l'optimiseur ne sait pas quel code peut être supprimé en toute sécurité. Il supprimera probablement le code utilisé de manière dynamique par réflexion lors de l'exécution, ce qui entraînera des plantages de l'application pour les utilisateurs.
De nombreuses bibliothèques modernes utilisent la génération de code au lieu de la réflexion. Consultez KSP pour obtenir un point d'entrée commun, utilisé par Room, Dagger2 et bien d'autres.
Quand la réflexion est acceptable
Si vous devez utiliser la réflexion, vous ne devez le faire que dans l'un des éléments suivants :
- Types ciblés spécifiques (implémenteurs d'interface ou sous-classes spécifiques)
- Coder à l'aide d'une annotation d'exécution spécifique
L'utilisation de la réflexion de cette manière limite le coût d'exécution et permet d'écrire des règles de conservation ciblées pour les consommateurs.
Cette forme de réflexion spécifique et ciblée est un modèle que vous pouvez voir à la fois dans le framework Android (par exemple, lors de l'inflation d'activités, de vues et de drawables) et dans les bibliothèques AndroidX (par exemple, lors de la construction de WorkManager
ListenableWorkers
ou RoomDatabases
). En revanche, la réflexion ouverte de Gson ne convient pas à l'utilisation dans les applications Android.
Types de règles de conservation dans les bibliothèques
Il existe deux types distincts de règles de conservation que vous pouvez appliquer dans les bibliothèques :
- Les règles de conservation des consommateurs doivent spécifier les règles qui conservent ce que la bibliothèque reflète. Si une bibliothèque utilise la réflexion ou JNI pour appeler son code ou le code défini par une application cliente, ces règles doivent décrire le code à conserver. Les bibliothèques doivent regrouper les règles de conservation des consommateurs, qui utilisent le même format que les règles de conservation des applications. Ces règles sont regroupées dans des artefacts de bibliothèque (AAR ou JAR) et sont utilisées automatiquement lors de l'optimisation de l'application Android lorsque la bibliothèque est utilisée. Ces règles sont conservées dans le fichier spécifié avec la propriété
consumerProguardFiles
dans votre fichierbuild.gradle.kts
(oubuild.gradle
). Pour en savoir plus, consultez Écrire des règles de conservation pour les consommateurs. - Les règles de conservation des compilations de bibliothèque sont appliquées lorsque votre bibliothèque est compilée. Elles ne sont nécessaires que si vous décidez d'optimiser partiellement votre bibliothèque au moment de la compilation.
Ils doivent veiller à ce que l'API publique de la bibliothèque ne soit pas supprimée, sinon elle ne sera pas présente dans la distribution de la bibliothèque, ce qui signifie que les développeurs d'applications ne pourront pas l'utiliser. Ces règles sont conservées dans le fichier spécifié avec la propriété
proguardFiles
dans votre fichierbuild.gradle.kts
(oubuild.gradle
). Pour en savoir plus, consultez Optimiser la compilation de la bibliothèque AAR.
Écrire des règles de conservation pour les consommateurs
En plus des consignes générales concernant les règles de conservation, voici des recommandations spécifiques aux auteurs de bibliothèques.
- N'utilisez pas de règles globales inappropriées. Évitez de placer des paramètres globaux tels que
-dontobfuscate
ou-allowaccessmodification
dans le fichier de règles de conservation du consommateur de votre bibliothèque, car ils affectent toutes les applications qui utilisent votre bibliothèque. - N'utilisez pas
-repackageclasses
dans le fichier de règles de conservation du consommateur de votre bibliothèque. Toutefois, pour optimiser la compilation de votre bibliothèque, vous pouvez utiliser-repackageclasses
avec un nom de package interne, tel que<your.library.package>.internal
, dans le fichier de règles de conservation de la compilation de votre bibliothèque. Cela peut rendre votre bibliothèque plus efficace, même si les applications qui la consomment ne sont pas optimisées. Toutefois, ce n'est généralement pas nécessaire, car les applications doivent également être optimisées. Pour en savoir plus sur l'optimisation des bibliothèques, consultez Optimisation pour les auteurs de bibliothèques. - Déclarez tous les attributs dont votre bibliothèque a besoin pour fonctionner dans les fichiers de règles de conservation de votre bibliothèque, même s'il peut y avoir un chevauchement avec les attributs définis dans
proguard-android-optimize.txt
. - Si vous avez besoin des attributs suivants dans la distribution de votre bibliothèque, conservez-les dans le fichier de règles de conservation de compilation de votre bibliothèque, et non dans le fichier de règles de conservation du consommateur de votre bibliothèque :
AnnotationDefault
EnclosingMethod
Exceptions
InnerClasses
RuntimeInvisibleAnnotations
RuntimeInvisibleParameterAnnotations
RuntimeInvisibleTypeAnnotations
RuntimeVisibleAnnotations
RuntimeVisibleParameterAnnotations
RuntimeVisibleTypeAnnotations
Signature
- Les auteurs de bibliothèques doivent conserver l'attribut
RuntimeVisibleAnnotations
dans leurs règles de conservation des consommateurs si des annotations sont utilisées au moment de l'exécution. - Les auteurs de bibliothèques ne doivent pas utiliser les options globales suivantes dans leurs règles de conservation des consommateurs :
-include
-basedirectory
-injars
-outjars
-libraryjars
-repackageclasses
-flattenpackagehierarchy
-allowaccessmodification
-overloadaggressively
-renamesourcefileattribute
-ignorewarnings
-addconfigurationdebugging
-printconfiguration
-printmapping
-printusage
-printseeds
-applymapping
-obfuscationdictionary
-classobfuscationdictionary
-packageobfuscationdictionary
Bibliothèques AAR
Pour ajouter des règles de consommateur à une bibliothèque AAR, utilisez l'option consumerProguardFiles
dans le script de compilation du module de bibliothèque Android. Pour en savoir plus, consultez nos conseils sur la création de modules de bibliothèque.
Kotlin
android { defaultConfig { consumerProguardFiles("consumer-proguard-rules.pro") } ... }
Groovy
android { defaultConfig { consumerProguardFiles 'consumer-proguard-rules.pro' } ... }
Bibliothèques JAR
Pour regrouper des règles avec votre bibliothèque Kotlin/Java fournie sous forme de fichier JAR, placez votre fichier de règles dans le répertoire META-INF/proguard/
du fichier JAR final, avec n'importe quel nom de fichier.
Par exemple, si votre code se trouve dans <libraryroot>/src/main/kotlin
, placez un fichier de règles du consommateur à l'adresse <libraryroot>/src/main/resources/META-INF/proguard/consumer-proguard-rules.pro
. Les règles seront alors regroupées au bon endroit dans votre fichier JAR de sortie.
Vérifiez que les règles sont correctement regroupées dans les fichiers JAR finaux en vous assurant qu'elles se trouvent dans le répertoire META-INF/proguard
.
Optimiser la compilation de la bibliothèque AAR (avancé)
En général, vous n'avez pas besoin d'optimiser directement la compilation d'une bibliothèque, car les optimisations possibles au moment de la compilation de la bibliothèque sont très limitées. Ce n'est que lors de la compilation d'une application, lorsqu'une bibliothèque est incluse dans une application, que R8 peut savoir comment tous les méthodes de la bibliothèque sont utilisées et quels paramètres sont transmis. En tant que développeur de bibliothèque, vous devez réfléchir aux différentes étapes d'optimisation et conserver le comportement, à la fois au moment de la compilation de la bibliothèque et de l'application, avant d'optimiser cette bibliothèque.
Si vous souhaitez toujours optimiser votre bibliothèque au moment de la compilation, le plug-in Android Gradle est compatible.
Kotlin
android { buildTypes { release { isMinifyEnabled = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) } configureEach { consumerProguardFiles("consumer-rules.pro") } } }
Groovy
android { buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } configureEach { consumerProguardFiles "consumer-rules.pro" } } }
Notez que le comportement de proguardFiles
est très différent de celui de consumerProguardFiles
:
- Les
proguardFiles
sont utilisés au moment de la compilation, souvent avecgetDefaultProguardFile("proguard-android-optimize.txt")
, pour définir la partie de votre bibliothèque qui doit être conservée lors de la compilation de la bibliothèque. Il s'agit au minimum de votre API publique. - Les
consumerProguardFiles
sont, en revanche, empaquetés dans la bibliothèque pour affecter les optimisations qui se produiront plus tard, lors de la compilation d'une application qui consomme votre bibliothèque.
Par exemple, si votre bibliothèque utilise la réflexion pour construire des classes internes, vous devrez peut-être définir les règles de conservation à la fois dans proguardFiles
et consumerProguardFiles
.
Si vous utilisez -repackageclasses
dans la compilation de votre bibliothèque, reconditionnez les classes dans un sous-package à l'intérieur du package de votre bibliothèque. Par exemple, utilisez -repackageclasses
'com.example.mylibrary.internal'
au lieu de -repackageclasses 'internal'
.
Assurer la compatibilité avec différentes versions de R8 (avancé)
Vous pouvez adapter les règles pour cibler des versions spécifiques de R8. Cela permet à votre bibliothèque de fonctionner de manière optimale dans les projets qui utilisent des versions R8 plus récentes, tout en permettant aux règles existantes de continuer à être utilisées dans les projets avec des versions R8 plus anciennes.
Pour spécifier des règles R8 ciblées, vous devez les inclure dans le répertoire META-INF/com.android.tools
à l'intérieur de classes.jar
d'un fichier AAR ou dans le répertoire META-INF/com.android.tools
d'un fichier JAR.
In an AAR library:
proguard.txt (legacy location, the file name must be "proguard.txt")
classes.jar
└── META-INF
└── com.android.tools (location of targeted R8 rules)
├── r8-from-<X>-upto-<Y>/<R8-rule-files>
└── ... (more directories with the same name format)
In a JAR library:
META-INF
├── proguard/<ProGuard-rule-files> (legacy location)
└── com.android.tools (location of targeted R8 rules)
├── r8-from-<X>-upto-<Y>/<R8-rule-files>
└── ... (more directories with the same name format)
Dans le répertoire META-INF/com.android.tools
, il peut y avoir plusieurs sous-répertoires dont le nom est au format r8-from-<X>-upto-<Y>
pour indiquer les versions R8 pour lesquelles les règles sont écrites. Chaque sous-répertoire peut contenir un ou plusieurs fichiers contenant les règles R8, avec n'importe quel nom de fichier et extension.
Notez que les parties -from-<X>
et -upto-<Y>
sont facultatives, que la version <Y>
est exclusive et que les plages de versions sont généralement continues, mais peuvent également se chevaucher.
Par exemple, r8
, r8-upto-8.0.0
, r8-from-8.0.0-upto-8.2.0
et r8-from-8.2.0
sont des noms de répertoire représentant un ensemble de règles R8 ciblées. Les règles du répertoire r8
peuvent être utilisées par toutes les versions de R8. Les règles du répertoire r8-from-8.0.0-upto-8.2.0
peuvent être utilisées par R8 à partir de la version 8.0.0 jusqu'à la version 8.2.0 non incluse.
Le plug-in Android Gradle utilise ces informations pour sélectionner toutes les règles qui peuvent être utilisées par la version R8 actuelle. Si une bibliothèque ne spécifie pas de règles R8 ciblées, le plug-in Android Gradle sélectionne les règles à partir des anciens emplacements (proguard.txt
pour un fichier AAR ou META-INF/proguard/<ProGuard-rule-files>
pour un fichier JAR).