Choisir judicieusement des bibliothèques

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.

Préférer la génération de code à la réflexion

En règle générale, vous devez choisir des bibliothèques qui utilisent la génération de code (codegen) au lieu de la réflexion. Avec la génération de code, l'optimiseur peut plus facilement 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.

Pour en savoir plus sur la génération de code par rapport à la réflexion, consultez Optimisation pour les auteurs de bibliothèques.

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.

Vérifier les problèmes d'optimisation

Lorsque vous envisagez d'utiliser une nouvelle bibliothèque, parcourez l'outil de suivi des problèmes et les discussions en ligne de la bibliothèque pour vérifier s'il existe des problèmes liés à la minification ou à la configuration de l'optimisation de l'application. Si c'est le cas, vous devez essayer de trouver des alternatives à cette bibliothèque. Notez les informations suivantes :

  • Les bibliothèques AndroidX et les bibliothèques telles que Hilt fonctionnent bien avec l'optimisation des applications, car elles utilisent la génération de code au lieu de la réflexion. Lorsqu'ils utilisent la réflexion, ils 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.
  • Dans la mesure du 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 Adopter les optimisations de manière incrémentielle.
  • Les bibliothèques ne doivent pas vous obliger à copier et coller des règles de conservation issues 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 une charge de maintenance pour le développeur de l'application. Il est difficile de les optimiser et de les modifier au fil du temps.

Activer l'optimisation après avoir ajouté une bibliothèque

Lorsque vous ajoutez une bibliothèque, activez l'optimisation par la suite et vérifiez s'il y a des erreurs. En cas d'erreurs, 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 pour cette bibliothèque.

Les règles sont cumulables

Notez que les règles de conservation sont cumulables. Cela signifie que certaines règles incluses dans une dépendance de bibliothèque ne peuvent pas être supprimées et peuvent avoir un impact sur la compilation d'autres parties de votre application. Par exemple, si une bibliothèque inclut une règle permettant de désactiver les optimisations de code, cette règle désactive les optimisations pour l'ensemble de votre projet.

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 des packages kotlin.reflect ou java.lang.reflect
  • Utilise les fonctions Class.forName ou classLoader.getClass
  • Lit les annotations au moment de l'exécution, par exemple s'il stocke une valeur d'annotation à l'aide de val value = myClass.getAnnotation() ou val value = myMethod.getAnnotation(), puis effectue une action avec value
  • Appelle des méthodes en utilisant le nom de la méthode sous forme de chaîne, par exemple :

    // Calls the private `processData` API with reflection
    myObject.javaClass.getMethod("processData", DataType::class.java)
    ?.invoke(myObject, data)
    

Filtrer les règles de conservation incorrectes (paramètre avancé)

Vous devez éviter les bibliothèques avec des règles de conservation qui conservent du code qui devrait en réalité ê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 facilement entraîner des plantages au moment de l'exécution. Notez que lorsque vous utilisez Gson pour obtenir une liste d'objets User, 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, une interface ou une 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()

Lorsque R8 analyse ce code et ne voit pas les UserList ou User instanciés ailleurs, il peut renommer des champs ou supprimer des constructeurs qui ne semblent pas être utilisés, ce qui provoque le plantage de votre application. Si vous utilisez d'autres bibliothèques de manière similaire, vérifiez qu'elles n'interfèrent pas avec l'optimisation de l'application. Si c'est le cas, évitez-les.

Notez que Room et Hilt construisent tous deux des types définis par l'application, mais utilisent la génération de code pour éviter le besoin de réflexion.