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.

Privilégier 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) plutôt que la réflexion. Avec le codegen, l'optimiseur peut déterminer plus facilement le code réellement utilisé au moment de l'exécution et le code pouvant être supprimé. Il peut être difficile de savoir si une bibliothèque utilise la génération de code ou la réflexion, mais il existe certains signes. Pour en savoir plus, 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.

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, consultez le suivi des problèmes et les discussions en ligne de la bibliothèque pour vérifier si des problèmes liés à la minification ou à la configuration de l'optimisation des applications existent. Si c'est le cas, vous devriez essayer de trouver d'autres alternatives à cette bibliothèque. Gardez à l'esprit les points suivants:

  • 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épétitif lors de l'instanciation ou de la sérialisation d'objets. Au lieu d'utiliser des 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 la sérialisation Kotlin à la place.
  • Si possible, évitez les bibliothèques qui incluent des règles de conservation à l'échelle du package. Les règles de conservation au niveau 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.

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 si des erreurs se produisent. 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 à son sujet.

Les règles sont cumulatives

N'oubliez pas que 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 avoir un impact sur la compilation d'autres parties de votre application. Par exemple, si une bibliothèque inclut une règle pour 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ée)

Vous pouvez peut-être 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 comme chaîne, par exemple:

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

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

Évitez les bibliothèques avec des règles de conservation qui conservent du code qui devrait vraiment être supprimé. Toutefois, si vous devez les utiliser, vous pouvez les filtrer 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 plante-t-il avec les optimisations ?

Gson est une bibliothèque de sérialisation qui entraîne 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 provoquer 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 une fabrique à la fonction fromJson(). La création ou la consommation de classes définies par l'application sans l'un des éléments suivants est un signe que la bibliothèque utilise peut-être 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 l'instanciation de UserList ou User, 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 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 le codegen pour éviter la réflexion.