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 conservant une expérience utilisateur 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 à l'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 interne dans une grande application modulaire.
Si vous êtes développeur d'applications et que vous souhaitez découvrir comment optimiser votre application Android, consultez Activer l'optimisation des applications. Pour savoir quelles bibliothèques sont appropriées, consultez la section Choisir judicieusement des bibliothèques.
Utiliser le codegen 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 standard lors de la programmation, mais la génération de code est plus compatible avec un optimiseur d'application tel que R8:
- Avec le codegen, le code est analysé et modifié pendant le processus de compilation. Étant donné qu'il n'y a pas de modifications majeures après le temps de compilation, l'optimiseur sait quel code est finalement nécessaire et ce qui peut être supprimé en toute sécurité.
- Avec la réflexion, le code est analysé et manipulé au moment de l'exécution. Étant donné que le code n'est pas vraiment finalisé tant qu'il ne s'exécute pas, l'optimiseur ne sait pas quel code peut être supprimé en toute sécurité. Il est probable qu'il supprime le code utilisé de manière dynamique via la réflexion au moment de l'exécution, ce qui entraîne 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 un point d'entrée commun, utilisé par Room, Dagger2 et bien d'autres.
Quand la réflexion est appropriée
Si vous devez utiliser la réflexion, vous ne devez la refléter que dans l'une des options suivantes:
- Types ciblés spécifiques (implémentateurs d'interface ou sous-classes spécifiques)
- Code utilisant 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 création de WorkManager ListenableWorkers ou de RoomDatabases). En revanche, la réflexion ouverte de Gson n'est pas adaptée à l'utilisation dans les applications Android.
Écrire des règles de conservation pour le consommateur
Les bibliothèques doivent empaqueter des règles de conservation "consommateur", 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 consommées automatiquement lors de l'optimisation de l'application Android lorsque la bibliothèque est utilisée.
Bibliothèques AAR
Pour ajouter des règles de consommation pour 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 pour créer des 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 un nom de fichier quelconque.
Par exemple, si votre code est dans <libraryroot>/src/main/kotlin
, placez un fichier de règles de consommation à <libraryroot>/src/main/resources/META-INF/proguard/consumer-proguard-rules.pro
. Les règles seront regroupées à l'emplacement approprié dans votre fichier JAR de sortie.
Vérifiez que le fichier JAR final regroupe correctement les règles en vérifiant qu'elles se trouvent dans le répertoire META-INF/proguard
.
Optimiser la compilation de la bibliothèque AAR (option avancée)
En règle générale, vous ne devez pas 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 toutes 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 à plusieurs é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 avec cette opération.
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 quelle partie de votre bibliothèque doit être conservée lors de la compilation de la bibliothèque. Il s'agit au minimum de votre API publique. - À l'inverse, les
consumerProguardFiles
sont empaquetées dans la bibliothèque pour influer sur les optimisations ultérieures, lors de la compilation d'une application qui consomme votre bibliothèque.
Par exemple, si votre bibliothèque utilise la réflexion pour créer 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, réemballez 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 (option avancée)
Vous pouvez adapter des 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
dans classes.jar
d'un 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 se présente sous la forme 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 et extension de fichier.
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épertoires 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 pouvant être utilisées par la version actuelle de R8. Si une bibliothèque ne spécifie pas de règles R8 ciblées, le plug-in Android Gradle sélectionnera les règles à partir des emplacements précédents (proguard.txt
pour un AAR ou META-INF/proguard/<ProGuard-rule-files>
pour un JAR).