Suivre les bonnes pratiques

Lorsque vous utilisez des règles de conservation, il est important d'atteindre le bon niveau de spécificité pour vous assurer de bénéficier d'avantages tout en conservant le comportement de votre application. Consultez les sections suivantes pour découvrir les bonnes pratiques et les éléments à éviter dans les règles de conservation.

Bonnes pratiques dans les règles de conservation

Les règles de conservation bien définies sont aussi spécifiques que possible et respectent les formats suivants :

  • Pour la spécification de classe, spécifiez toujours une classe spécifique, une classe de base ou une classe annotée si possible, comme illustré dans les exemples suivants :

    -keepclassmembers class com.example.MyClass {
      void someSpecificMethod();
    }
    
    -keepclassmembers ** extends com.example.MyBaseClass {
      void someSpecificMethod();
    }
    
    -keepclassmembers @com.example.MyAnnotation class ** {
      void someSpecificMethod();
    }
    
  • Dans la mesure du possible, utilisez des annotations dans votre code source, puis ciblez-les directement dans vos règles de conservation. Cela crée un lien clair et explicite entre votre code et les règles qui le conservent, ce qui rend votre configuration plus robuste, plus facile à comprendre et moins sujette aux erreurs lorsque le code change.

    Par exemple, l'extrait suivant montre comment conserver la classe MyClass, ainsi que d'autres classes annotées avec @com.example.DisplayComponent :

    // In the source code
    @com.example.DisplayComponent
    class MyClass { /* ... */ }
    
    // In the keep rules
    -keep @com.example.DisplayComponent class * {*;}
    

    Nous vous recommandons de nommer vos annotations de manière à ce qu'elles fournissent un contexte significatif sur la raison pour laquelle certaines parties du code sont conservées. Par exemple, utilisez @DisplayComponent pour une application dans laquelle les composants d'affichage nécessitent la conservation de certaines parties.

  • Dans la mesure du possible, la spécification de membre doit être déclarée et ne faire référence qu'aux parties de la classe qui doivent être conservées pour que l'application fonctionne. Il est recommandé de ne pas appliquer de règle à une classe entière en définissant le champ d'application de membre facultatif sur { *; }, sauf si cela est strictement nécessaire.

    -keepclassmembers com.example.MyClass {
      void someSpecificMethod();
      void @com.example.MyAnnotation *;
    }
    
  • Si vous utilisez l'option globale repackageclasses, évitez de spécifier le nom de package facultatif. Cela permet de réduire la taille des fichiers DEX, car le préfixe du package est omis dans les noms de classes reconditionnés.

  • Conservez les règles de conservation pour tous les éléments auxquels vous accédez par réflexion. Même si ces éléments sont conservés par R8 sans règles de conservation explicites, il est essentiel de conserver les règles réelles, car les futures modifications du code pourraient entraîner la non-conservation de ces éléments par R8.

    Pour la réflexion dans votre propre code, vous pouvez utiliser Jetpack Keep pour générer des règles de conservation plus faciles à gérer. Chaque annotation se trouve dans le code qui effectue la réflexion. Ainsi, lorsque vous modifiez ce code, vous mettez à jour ou supprimez son annotation au même endroit. De cette façon, les règles ne conservent pas le code sur lequel votre application ne se reflète plus. Lint signale également les appels réflexifs qui ne sont pas annotés.

  • Dans la mesure du possible, utilisez -assumenosideeffects ou -convertchecknotnull pour optimiser les vérifications de valeur nulle, car cela peut réduire considérablement la taille du DEX de votre application. Pour en savoir plus, consultez Types de règles supplémentaires.

Si vous ne pouvez pas respecter ces consignes, vous pouvez isoler temporairement le code à conserver dans un package dédié et appliquer votre règle de conservation au package. Toutefois, il ne s'agit pas d'une solution à long terme. Pour en savoir plus, consultez Adopter les optimisations de manière progressive. Pour utiliser une règle de conservation pour un package, définissez-la comme indiqué dans l'exemple suivant :

-keepclassmembers class com.example.pkg.** { *; }

Ce qu'il faut éviter

La syntaxe des règles de conservation comporte de nombreuses options, mais pour obtenir des avantages mesurables et durables en termes de performances, nous vous recommandons de ne pas utiliser les éléments suivants :

  • N'utilisez pas de règles de conservation à l'échelle du package à long terme, telles que -keep class com.example.pkg.** { *; }. Elles peuvent être utilisées temporairement pour contourner les problèmes lors de la configuration de R8. Pour en savoir plus, consultez Limiter le champ d'application de l'optimisation. En règle générale, soyez prudent avec les caractères génériques. Assurez-vous de ne conserver que le code dont vous avez besoin.
  • Dans la mesure du possible, évitez les bibliothèques qui vous suggèrent de copier-coller des règles de conservation lorsque vous les utilisez, en particulier les règles de conservation à l'échelle du package. Les bibliothèques conçues pour fonctionner correctement sur Android doivent éviter la réflexion dans la mesure du possible et intégrer des règles de conservation du consommateur si nécessaire.
  • Évitez d'utiliser l'opérateur d'inversion ! dans les règles de conservation, car vous pourriez appliquer involontairement une règle à presque toutes les classes de votre application.

Si vous ne parvenez pas à suivre ces règles, vous utilisez peut-être beaucoup de réflexion ouverte . Vous devez alors éviter la réflexion ou éviter la bibliothèque qui l'utilise (consultez l'étude de cas Gson).