Pour activer l'optimisation des applications, vous devez utiliser des bibliothèques compatibles avec l'optimisation Android. Si une bibliothèque n'est pas configurée pour l'optimisation Android (par exemple, si elle utilise la réflexion sans regrouper les règles de conservation associées), elle n'est peut-être pas adaptée à une application Android. Cette page explique pourquoi certaines bibliothèques sont mieux adaptées à l'optimisation des applications et fournit des conseils généraux pour vous aider à faire votre choix.
Conseils généraux pour choisir des bibliothèques
Suivez ces conseils pour vous assurer que vos bibliothèques sont compatibles avec l'optimisation des applications.
Privilégier la génération de code à la réflexion
Choisissez des bibliothèques qui utilisent la génération de code (codegen) plutôt que la réflexion. Avec la génération de code, l'optimiseur peut déterminer quel code est réellement utilisé au moment de l'exécution et quel code peut être supprimé. Il peut être difficile de déterminer si une bibliothèque utilise la génération de code ou la réflexion, mais certains signes peuvent vous aider. Consultez les conseils.
Pour en savoir plus sur la génération de code par rapport à la réflexion, consultez la section Optimisation pour les auteurs de bibliothèques.
Vérifier l'utilisation de la réflexion (avancé)
Vous pouvez déterminer si une bibliothèque utilise la réflexion en inspectant son code. Si la bibliothèque utilise la réflexion, vérifiez qu'elle fournit des règles de conservation associées. Une bibliothèque utilise probablement la réflexion si elle effectue les opérations suivantes :
- Utilise des classes ou des méthodes à partir des packages
kotlin.reflectoujava.lang.reflect. - Utilise les fonctions
Class.forNameouclassLoader.getClass. - Lit les annotations au moment de l'exécution, par exemple si elle stocke une valeur d'annotation
à l'aide de
val value = myClass.getAnnotation()ouval value = myMethod.getAnnotation(), puis effectue une action avecvalue. Appelle des méthodes en utilisant le nom de la méthode sous forme de chaîne, comme dans l'exemple suivant :
// Calls the private `processData` API with reflection myObject.javaClass.getMethod("processData", DataType::class.java) ?.invoke(myObject, data)
Vérifier les problèmes d'optimisation
Lorsque vous envisagez d'utiliser une nouvelle bibliothèque, consultez son outil de suivi des problèmes et les discussions en ligne pour vérifier s'il existe des problèmes liés à la minification ou à la configuration de l'optimisation des applications. Si c'est le cas, essayez de trouver des alternatives à cette bibliothèque. Tenez compte des informations suivantes :
- Les bibliothèques AndroidX et les bibliothèques telles que Hilt fonctionnent bien avec l'optimisation des applications, car elles utilisent principalement la génération de code au lieu de la réflexion. Lorsqu'elles utilisent la réflexion, elles fournissent des règles de conservation minimales pour ne conserver que le code nécessaire.
- Les bibliothèques de sérialisation utilisent fréquemment la réflexion pour éviter le code récurrent lors de l'instanciation ou de la sérialisation d'objets. Au lieu d'approches basées sur la réflexion (telles que Gson pour JSON), recherchez des bibliothèques qui utilisent la génération de code pour éviter ces problèmes, par exemple en utilisant Kotlin Serialization {:.external} ou Moshi avec la génération de code.
- Si possible, évitez les bibliothèques qui incluent des règles de conservation à l'échelle du package. Les règles de conservation à l'échelle du package peuvent aider à résoudre les erreurs, mais les règles de conservation générales doivent être affinées pour ne conserver que le code nécessaire. Pour en savoir plus, consultez la section Adopter les optimisations de manière incrémentielle.
- Avant de publier une application qui utilise une bibliothèque tierce, utilisez l'analyseur de configuration R8 pour auditer les règles de conservation fournies. En examinant le rapport, vous pouvez vérifier si les règles de conservation de la bibliothèque sont trop générales, ce qui empêche R8 d'effectuer des optimisations critiques sur votre codebase. Cette vérification garantit que les bibliothèques que vous sélectionnez correspondent aux objectifs de performances de votre application et n'introduisent pas de gonflement de configuration inutile.
- Les bibliothèques ne doivent pas vous obliger à copier-coller des règles de conservation de la documentation dans un fichier de votre projet, en particulier les règles de conservation à l'échelle du package. À long terme, ces règles deviennent un fardeau de maintenance pour le développeur d'applications et sont difficiles à optimiser et à modifier au fil du temps.
Activer l'optimisation après avoir ajouté une nouvelle bibliothèque
Lorsque vous ajoutez une nouvelle bibliothèque, activez l'optimisation et vérifiez s'il y a des erreurs. Si des erreurs se produisent, recherchez des alternatives à cette bibliothèque ou écrivez des règles de conservation. Si une bibliothèque n'est pas compatible avec l'optimisation, signalez un bug à cette bibliothèque.
Filtrer les règles de conservation incorrectes (avancé)
Les règles de conservation se cumulent. Cela signifie que certaines règles incluses dans une dépendance de bibliothèque ne peuvent pas être supprimées et peuvent affecter la compilation d'autres parties de votre application. Par exemple, si une bibliothèque comprend une règle permettant de désactiver les optimisations de code, cette règle désactive les optimisations pour l'ensemble de votre projet.
Vous devez éviter les bibliothèques dont les règles de conservation conservent du code qui devrait être supprimé. Toutefois, si vous devez les utiliser, vous pouvez filtrer les règles comme indiqué dans le code suivant :
// If you're using AGP 8.4 and higher
buildTypes {
release {
optimization.keepRules {
it.ignoreFrom("com.somelibrary:somelibrary")
}
}
}
// If you're using AGP 7.3-8.3
buildTypes {
release {
optimization.keepRules {
it.ignoreExternalDependencies("com.somelibrary:somelibrary")
}
}
}
Étude de cas : Pourquoi Gson ne fonctionne pas avec les optimisations
Gson est une bibliothèque de sérialisation qui pose souvent des problèmes d'optimisation des applications, car elle utilise beaucoup la réflexion. L'extrait de code suivant montre comment Gson est généralement utilisé, ce qui peut entraîner des plantages au moment de l'exécution. Notez que lorsque vous utilisez Gson pour obtenir une liste d'objets utilisateur, vous n'appelez pas le constructeur ni ne transmettez de fabrique à la fonction fromJson(). La construction ou la consommation de classes définies par l'application sans l'un des éléments suivants indique qu'une bibliothèque peut utiliser une réflexion ouverte :
- Classe d'application implémentant une bibliothèque, ou interface ou classe standard
- Plug-in de génération de code tel que KSP
class User(val name: String)
class UserList(val users: List<User>)
// This code runs in debug mode, but crashes when optimizations are enabled
Gson().fromJson("""[{"name":"myname"}]""", User::class.java).toString()
Pour comprendre comment R8 fonctionne sur Gson, consultez les règles de consommateur Gson. Lorsque R8
analyse ce code et ne voit pas UserList ou User instancié
, il peut renommer des champs ou supprimer des constructeurs qui ne semblent pas être
utilisés, ce qui entraîne le plantage de votre application. Si vous utilisez d'autres bibliothèques de manière similaire, vous devez vérifier qu'elles n'interfèrent pas avec l'optimisation des applications et, si c'est le cas, les éviter.
Pour définir les classes d'une manière compatible avec les règles de consommateur de Gson, utilisez l'extrait suivant comme référence :
class User(@com.google.gson.annotations.SerializedName("name") val name: String)
class UserList(@com.google.gson.annotations.SerializedName("users") val users: List<User>)
Notez que Room, Hilt et Moshi avec la génération de code construisent des types définis par l'application, mais utilisent la génération de code pour éviter d'avoir recours à la réflexion.