Comment arrêter d'avoir peur du progrès et commencer à vivre


Bonjour, je suis développeur Android et je n'ai plus peur de ProGuard ...


Habituellement, cet utilitaire est mémorisé lorsqu'il est confronté à un problème de limite dex dalvik ou à une exigence pour améliorer la sécurité des applications. Malheureusement, c'est loin d'être la première fois de configurer correctement Proguard. J'ai souvent vu combien, après avoir interrompu un projet, désactivé Proguard et activé le support Mulditex, et chaque fois j'étais un peu triste, car Proguard aide à la fois à réduire la taille de l'application et à augmenter ses performances.


En fin de compte, j'ai décidé d'écrire un article dans lequel je peux mettre toutes les informations utiles que j'ai apprises au cours de plusieurs années de travail avec Proguard et qui pourraient aider les débutants et ceux qui savent déjà quelque chose.


De quoi s'agit-il


Proguard est un utilitaire open source pour l'optimisation et l'obscurcissement du code Java. Cet outil traite le code Java déjà compilé, il devrait donc fonctionner avec n'importe quel langage JVM. Plus précisément, le langage lui-même pour Proguard est indifférent, seul le bytecode est important. Toutes les manipulations de bytecode proguard peuvent être divisées en 3 catégories principales: réduction du code , optimisation et obscurcissement .


Rétrécissement du code


Oui, c'est une entreprise plutôt étrange d'écrire du code puis de le supprimer, mais c'est la réalité du développement Android. Bien sûr, cela ne concerne pas le code écrit à la main (bien que cela se produise), mais des tonnes de cargaisons mortes apportées par des bibliothèques de toutes sortes. La goyave , Apache Commons , les services Google Play et d'autres types peuvent gonfler la taille du fichier apk de 500 Ko à quelques dizaines de mégaoctets. Parfois, cela va si loin qu'un programme ne peut pas compiler en raison du dépassement de la limite des méthodes Dalvik . Proguard aidera à supprimer tout le code inutilisé et à réduire la taille de l'application à quelques mégaoctets.


Optimisation


En plus de supprimer le code inutile, Proguard peut optimiser le code restant. Son arsenal comprend l'analyse de flux de contrôle, l'analyse de flux de données, l'évaluation partielle, l'affectation unique statique, la numérotation des valeurs globales, l'analyse de la vivacité. Proguard peut effectuer des optimisations de judas, réduire le nombre d'allocations de variables, simplifier les récursions de queue et bien plus encore ( wiki ). En plus de ces opérations générales, Proguard possède des optimisations utiles spécifiquement pour la plate-forme Android, par exemple, en remplaçant les classes enum par des entiers, en supprimant les instructions de journalisation.


Obfuscation


Enfin, Proguard peut transformer tout votre code en un désordre illisible en renommant toutes les classes, méthodes et champs en ensembles de lettres aléatoires (en fait pas tout à fait aléatoires). C'est une option très utile, car n'importe qui peut décompiler votre fichier apk, et tout le monde n'a pas la patience de comprendre le code obscurci.


Principe de fonctionnement


Proguard fonctionne en 3 étapes dans l'ordre décrit ci-dessus: réduction du code → optimisation → obfuscation . Chacune des étapes est facultative.


L'étape d'optimisation dans le cas du SDK Android est désactivée par défaut.


Pour que Proguard fonctionne, vous devez fournir 3 composants:


  • Votre code compilĂ© est une archive avec les fichiers de class de votre programme et toutes les bibliothèques que vous utilisez (jar, aar, apk, war, zip, etc.). Proguard ne modifie que le code dĂ©jĂ  compilĂ© et n'a rien Ă  voir avec la source.
  • Fichier (s) de configuration - fichier (s) contenant toutes les règles, options et paramètres avec lesquels vous souhaitez commencer le traitement.
  • Bocaux de bibliothèque (aar, apks, ...) - classes de la plateforme sur laquelle votre programme s'exĂ©cute. Dans le cas d'Android, c'est android.jar . Ces archives ne sont nĂ©cessaires que pour l'analyse correcte de votre code, elles ne seront pas modifiĂ©es (cela n'a aucun sens, puisque android.jar est "au tĂ©lĂ©phone", nous n'y avons pas accès).

Image de la présentation de Jeb Ware, lien à la fin de l'article
(Image de la présentation de Jeb Ware, lien à la fin de l'article)


À l'aide des classes de bibliothèque et de vos fichiers de configuration, Proguard définit tous les points d'entrée de votre programme ( graines ). En d'autres termes, il définit les classes et méthodes qui peuvent être appelées de l'extérieur et qui ne peuvent pas être touchées. Ensuite, en commençant par les graines découvertes, Proguard parcourt récursivement tout votre code, signalant «utilisé» tout ce qu'il peut atteindre. Tout autre code sera supprimé. Vous devez spécifier au moins un point d'entrée dans la configuration. Pour un programme java standard, c'est la fonction main . Dans Android, il n'y a pas de point d'entrée unique dans le programme; au lieu de cela, nous avons des composants standard (activité, service, etc.) qui sont créés et appelés par le système. Heureusement, nous n'avons pas besoin de spécifier quoi que ce soit ici, le SDK Android créera la configuration nécessaire pour nous.


Fichiers compagnon


Après avoir détecté tous les points d'entrée, Proguard les enregistrera dans le fichier seeds.txt .


Tout le code que Proguard a trouvé inutile est écrit dans le fichier usage.txt . C'est un nom assez étrange pour un fichier contenant du code à distance, il serait plus correct de l'appeler unsage.txt, mais nous avons ce que nous avons, rappelez-vous juste cela.


À l'étape d'obscurcissement, un fichier mapping.txt sera créé contenant les paires <nom de classe d'origine | méthode | champ> -> <nom de classe obscurci | méthode | champ>. Ce fichier est utile lorsque vous devez désobfusquer un programme, par exemple, lire stacktrace. Le mappage manuel des fichiers n'est pas nécessaire; le SDK Android a des utilitaires de retrace et proguardui pour vous aider. De plus, si vous utilisez Fabric Crashlytics, leur plugin Gradle peut rechercher et charger ce fichier de manière indépendante dans la console, vous n'avez donc pas à vous en préoccuper.


Dans le cas d'Android, ces fichiers se trouvent généralement dans app/build/output/mapping/<product-flavor-name>/ .


Proguard crée également un fichier dump.txt qui contient tout ce que Proguard a mis dans l'archive finale. Il ne m'a jamais été utile, mais peut-être que cela sera utile à quelqu'un.


Comment ça se passe sur Android


Le plugin Android Gradle peut exécuter Proguard seul. Il vous suffit d'activer cette option et de spécifier les fichiers de configuration.


 buildTypes { <...> release { minifyEnabled true proguardFiles 'proguard-rules.pro', getDefaultProguardFile('proguard-android.txt') } } 

minifyEnabled true - activer Proguard au moment de la construction


proguardFiles - une liste de fichiers de configuration. Les règles de tous les fichiers de configuration seront ajoutées à la liste générale dans l'ordre où elles apparaissent.


proguard-rules.pro est notre fichier de configuration avec des règles spécifiques au projet


getDefaultProguardFile('proguard-android.txt') - une fonction qui renvoie le fichier de configuration standard pour les applications Android. Il se trouve dans AndroidSDK/tools/proguard


En fait, le SDK Android possède deux proguard-android.txt : proguard-android.txt et proguard-android-optimize.txt . Le premier a l'option -dontoptimize , qui désactive toutes les optimisations. Si vous souhaitez activer l'optimisation, utilisez la deuxième configuration.


En plus de ces configurations standard, le SDK Android (aapt) génère automatiquement un ensemble de règles pour les ressources: aapt vérifie tous les fichiers xml (y compris le manifeste) pour trouver toutes les activités, services, vues, etc. et générer les règles nécessaires pour eux. Les règles générées se trouvent dans app/build/intermediates/proguard-rules/<flavor>/aapt_rules.txt . Vous n'avez pas besoin de le spécifier vous-même, le plugin Android Gradle ajoutera automatiquement ces règles.


Image de la présentation de Jeb Ware, lien à la fin de l'article
(Image de la présentation de Jeb Ware, lien à la fin de l'article)


Configurations


La configuration de Proguard est la partie la plus élémentaire de son utilisation et en même temps la plus difficile. Une configuration incorrecte peut facilement interrompre la compilation de l'application et de l'application elle-même lors de l'exécution. Toutes les options de configuration disponibles sont documentées en détail sur off. site.


Parmi toutes les options, je citerais les 3 groupes les plus importants:


  • garder les règles - Tous les points d'entrĂ©e possibles au programme. Règles indiquant Ă  Proguard quelles classes ou parties de classes ne doivent pas ĂŞtre modifiĂ©es ou quelles modifications sont valables pour des classes spĂ©cifiques.
  • optimisation - indiquez quelles optimisations sont acceptables, combien de cycles d'optimisation doivent ĂŞtre effectuĂ©s.
  • travailler avec les avertissements, les erreurs et le dĂ©bogage

Gardez les règles


Il s'agit d'un ensemble d'options conçues pour protéger votre code contre le Progressive impitoyable. Dans sa forme la plus générale, cette règle ressemble à ceci:


 -keep [,modifier,...] class_specification 

keep est la plus courante de ces options (il y en a d'autres), indiquant à Proguard de sauvegarder la classe elle-même et tous ses membres: champs et méthodes.


class_specification - un modèle pointant vers la (les) classe (s) ou ses parties (membres de la classe). La vue générale du modèle est très grande, elle peut être visualisée . documentation . Vous pouvez le contacter, mais en général, vous vous souvenez simplement que nous avons la possibilité de composer un modèle de ces composants:


  • sĂ©lectionner toutes les classes avec un nom spĂ©cifique, un package
  • sĂ©lectionner toutes les classes qui hĂ©ritent / implĂ©mentent certaines classes / interfaces
  • sĂ©lectionner toutes les classes avec des modificateurs et / ou des annotations spĂ©cifiques
  • sĂ©lectionner toutes les mĂ©thodes avec un nom, des modificateurs, des arguments et une valeur de retour spĂ©cifiques
  • sĂ©lectionnez tous les champs avec un nom spĂ©cifique, des modificateurs, d'un type spĂ©cifique.
  • il est possible d'utiliser des caractères gĂ©nĂ©riques


    Encore une fois, ce n'est pas une description stricte du modèle, c'est plutôt une liste des fonctionnalités que nous avons. Voici quelques exemples:



-keep public class com.example.MyActivity
enregistrer la classe com.example.MyActivity


-keep public class * extends android.app.Activity
enregistrer toutes les classes publiques héritant android.app.Activity


 -keep public class * extends android.view.View { public <init>(android.content.Context); public <init>(android.content.Context, android.util.AttributeSet); public <init>(android.content.Context, android.util.AttributeSet, int); public void set*(...); } 

trouver toutes les classes publiques qui héritent de android.view.View et enregistrer 3 constructeurs avec certains paramètres en eux + toutes les méthodes publiques, avec le modificateur void , tous les arguments et un nom commençant par set . Toutes les autres parties de la classe peuvent être modifiées.


-keep class com.habr.** { *; }
enregistrer toutes les classes et tout leur contenu dans le package com.habr


modifiers - ajout à la règle de conservation:


  • includedescriptorclasses - en plus de la classe / mĂ©thode / champ spĂ©cifiĂ©, vous devez enregistrer toutes les classes prĂ©sentes dans leurs descripteurs.
  • includecode - le contenu de la mĂ©thode pointĂ©e par cette règle particulière ne peut pas non plus ĂŞtre touchĂ©.
  • allowshrinking - les classes pointĂ©es par cette règle ne sont pas des graines et peuvent ĂŞtre supprimĂ©es, mais seulement si elles ne sont pas utilisĂ©es dans le programme lui-mĂŞme. Cependant, si après la rĂ©duction du code, ce code persiste (du fait que quelqu'un l'utilise), il est impossible d'optimiser / de masquer ce code.
  • allowoptimization - les classes rĂ©fĂ©rencĂ©es par cette règle peuvent uniquement ĂŞtre optimisĂ©es, mais ne peuvent pas ĂŞtre supprimĂ©es ou masquĂ©es.
  • allowobfuscation - les classes pointĂ©es par cette règle peuvent uniquement ĂŞtre obscurcies, mais ne peuvent pas ĂŞtre supprimĂ©es ou optimisĂ©es.

En plus de keep , il y a quelques autres options:


-keepclassmembers - indique que les membres de la classe doivent être enregistrés si la classe elle-même est préservée après la réduction du code.


-keepclasseswithmembers - indique que vous souhaitez enregistrer les classes dont le contenu relève du modèle spécifié. Par exemple, -keepclasseswithmembers class * { public <init>(android.content.Context); } -keepclasseswithmembers class * { public <init>(android.content.Context); } - enregistre toutes les classes qui ont un constructeur public avec un argument de type Context .


-keepnames - abréviation de -keep,allowshrinking .


-keepclassmembernames - raccourci pour -keepclassmembers,allowshrinking .


keepclasseswithmembernames - abréviation de -keepclasseswithmembers,allowshrinking .


Réglage d'optimisation


L'option la plus importante ici est le drapeau -dontoptimize . S'il est présent, aucune optimisation ne sera effectuée et toutes les autres options d'optimisation seront ignorées.


Il existe de nombreuses options d'optimisation, mais les suivantes me sont très utiles:


-optimizations optimization_filter - répertoriant toutes les façons dont vous souhaitez utiliser. Il est préférable d'utiliser l'ensemble spécifié dans proguard-android-optimize.txt ou un sous-ensemble de celui-ci. Une liste de toutes les optimisations peut être trouvée ici .


-optimizationpasses n - nombre de cycles d'optimisation. Plusieurs cycles peuvent améliorer le résultat. En même temps, Proguard est suffisamment intelligent pour arrêter les cycles s'il constate que le résultat ne s'est pas amélioré depuis la dernière fois.


-assumenosideeffects class_specification - indique que cette méthode n'a aucun effet secondaire et ne renvoie qu'une valeur. Proguard supprimera les appels à cette méthode s'il détecte que le résultat qu'il renvoie n'est pas utilisé. L'utilisation la plus courante de cette option est peut-être de supprimer tous les journaux de débogage: -assumenosideeffects class android.util.Log { public static int d(...); } -assumenosideeffects class android.util.Log { public static int d(...); }


-allowaccessmodification - affiche tout ce qui est caché :) Une excellente option qui vous permet de vous débarrasser d'un tas de méthodes d'accessoires artificiels pour les classes imbriquées. -repackageclasses uniquement en conjonction avec -repackageclasses


-repackageclasses - -repackageclasses de déplacer toutes les classes vers un package spécifié. Cela s'applique davantage à l'obscurcissement, mais en même temps, donne de bons résultats en optimisation.


Autres options utiles


-dontwarn et -dontnote


Proguard est très intelligent et signale toujours des endroits suspects lors de l'analyse de code, parfois ce sont des notes, parfois des avertissements. Si votre build ne fonctionne pas avec Proguard activé, assurez-vous de lire tous les journaux qu'il produit, il écrira ce qui s'est mal passé et, très probablement, vous indiquera même comment le corriger. Après avoir lu tous les messages, vous pouvez soit résoudre le problème, soit ignorer le message de l'une de ces options si vous êtes sûr qu'il n'y a pas de problème.


Par exemple, il arrive que certaines bibliothèques java utilisent des classes de plate-forme qui ne sont pas dans android.jar et Proguard vous en avertira. Si vous êtes sûr que cette bibliothèque fonctionne correctement dans un environnement Android, vous pouvez désactiver cet avertissement -dontwarn java.lang.management.**


-whyareyoukeeping class_specification est une option utile qui affichera la raison pour laquelle Proguard a décidé de ne pas toucher à cette classe / méthode.


-verbose - imprimer des journaux et des exceptions plus détaillés


-printconfiguration - imprime une liste complète des options de tous les fichiers de configuration qui ont été utilisés, y compris les règles des bibliothèques et générées via aapt.


-keepattributes SourceFile, LineNumberTable - enregistre les méta-informations (noms de fichiers, numérotation des lignes) pour pouvoir déboguer le code dans l'EDI et obtenir une trace de pile significative. Assurez-vous d'ajouter cette option.


Pratique


Cela se produit généralement: allumez Proguard et cela vous casse tout le projet en donnant une tonne d'erreurs. Beaucoup à cette étape désactivent Proguard et essaient de ne pas y revenir. Je vais essayer de vous donner quelques conseils pour faciliter ce processus de transition.


Décidez des points d'entrée de départ


Si vous êtes un développeur Android, tout est extrêmement élémentaire - il suffit de sélectionner l'une des deux configurations standard du SDK Android: proguard-android.txt ou proguard-android-optimize.txt , ils s'occuperont de tout ce qui devrait rester intact.


Vérifiez toutes les bibliothèques


Récemment, de plus en plus de bibliothèques sont distribuées avec des proguard-configs prêtes à l'emploi. Proguard peut regarder à l'intérieur de l'archive, trouver la configuration de la bibliothèque et l'ajouter à d'autres options. Vérifiez chaque bibliothèque que vous utilisez pour une telle configuration.


(contenu d'un fichier aar d'une des bibliothèques)
(contenu d'un fichier aar d'une des bibliothèques)


Si vous utilisez les services Google Play, le plug com.google.gms.google-services in com.google.gms.google-services sélectionnera vous-même la configuration dont vous avez besoin.


Si les auteurs de la bibliothèque n'emballent pas la config dans l'archive, ils ont peut-être fait attention et écrit les règles sur leur site web, sur la page du référentiel ou dans le fichier README. Essayez de trouver vous-même la configuration de la version de la bibliothèque que vous utilisez.


Si vous ne trouvez aucune règle prête à l'emploi, vous devrez lire les journaux et résoudre le problème individuellement. Très probablement, vous devrez ajouter des règles de conservation pour le code de bibliothèque cassé. Ou ignorez les erreurs si elles n'interfèrent pas avec le programme.


Inspectez votre code


Vous savez mieux quel code vous pouvez envoyer sous le couteau, mais vous devriez quand même regarder attentivement tous les endroits où la réflexion est utilisée:


  • Class.forName (...) (la documentation promet que Proguard est capable de dĂ©finir un tel code, cependant, il y a eu des cas, cela vaut la peine d'ĂŞtre vĂ©rifiĂ©)
  • Modèles / classes d'entitĂ© utilisĂ©s pour la sĂ©rialisation, le mappage. Toutes les classes dont les noms de champs (parfois les classes elles-mĂŞmes) sont importantes Ă  conserver (Gson, RealmIO, etc.)
  • appels de bibliothèque natifs via JNI

Les tests


Si une classe / méthode est utilisée uniquement dans les tests et nulle part ailleurs, Proguard supprimera ce code. C'est une situation courante si vous avez TDD :) Pour ce cas, j'ai une configuration séparée, où j'ajoute des classes qui ne sont pas encore intégrées dans le projet, ne sont utilisées nulle part, mais qui doivent être testées.


Dans le plugin Android Gradle, en plus des instructions proguardFiles , il y a toujours testProguardFiles . Cette instruction est nécessaire pour spécifier les configurations qui seront appliquées à l'application de test générée afin de tester votre application lorsque vous exécutez des tests d'instrumentation. Habituellement, ceci est utilisé afin d'obtenir la même optimisation / obscurcissement dans les deux fichiers apk afin qu'il n'y ait pas de désynchronisation entre eux. Lien


Analyseur APK


Android Studio a un excellent outil. Vous pouvez l'ouvrir soit via Find Action -> Analyze APK, soit en ouvrant le fichier apk lui-même dans Android Studio. Analyzer affiche de nombreuses informations utiles, mais nous nous intéressons maintenant au code. Pour voir ce qui a finalement été intégré dans un fichier APK, vous devez sélectionner le fichier classes.dex



Par défaut, vous verrez exactement le code résultant qui a passé les étapes de réduction et d'optimisation. Cependant, vous pouvez cliquer sur le bouton Charger les mappages de Proguard ... , ajouter seeds.txt et usage.txt pour voir le code qui a été supprimé.



Si, pour une raison quelconque, Proguard a modifié le code dont vous avez besoin, recherchez-le dans Analyzer et sélectionnez Générer la règle de conservation Proguard via RMB. Analyzer générera un choix de plusieurs options pour les règles, de la plus générale à la plus spécifique, sélectionnez UNE d'entre elles.




Pour les auteurs de bibliothèque


Si vous créez une bibliothèque Android, vous pouvez ajouter une configuration proguard pour vos clients comme suit:


 buildTypes { release { consumerProguardFiles 'proguard-rules.pro' } } 

À mon avis, il vaut mieux ne pas optimiser et obscurcir votre bibliothèque avec zèle, mais offrir cette opportunité à vos clients. Un bon ton est d'ajouter à la configuration ce que les clients devront encore ajouter s'ils incluent Proguard. Cependant, si vous souhaitez toujours ajouter de la sécurité, il est évident que vous devez protéger l'intégralité de l'API pulic de votre bibliothèque de Proguard, y compris les descripteurs et les signatures.


R8, DexGuard et Redex


R8 est le nouvel outil de Google pour remplacer l'actuel Proguard. Attendez, n'essayez pas d'oublier tout ce que vous venez de lire dans l'article, traitez-le comme le nouveau Proguard. Google promet de conserver l'intégralité de l'API publique, afin que toutes les configurations fonctionnent comme avant. Le projet est toujours en version bêta, mais vous pouvez l'essayer vous-même.


DexGuard est un utilitaire payant des développeurs de Proguard. Il peut être utilisé ensemble ou à la place de Proguard. On prétend que DexGuard peut faire tout ce que Proguard peut faire, mais en mieux. Malheureusement, je n'ai pas eu l'occasion de l'essayer, si quelqu'un a de l'expérience, merci de la partager.


Redex est un autre optimiseur dex de Facebook. Il est rapporté qu'avec cela, vous pouvez atteindre jusqu'à 25% d'augmentation de la productivité et réduire la taille de l'application en appliquant l'outil au code déjà traité par Proguard.


Au lieu d'une conclusion


N'ayez pas peur d'utiliser Proguard, ne soyez pas paresseux et passez du temps à vous installer. Cela réduira sa taille, augmentera la vitesse de travail, puis ajoutera la fidélité de vos utilisateurs. En même temps, essayez de créer des configurations de Proguard efficaces, n'écrivez pas de règles de "tapis", sinon un Jake Wharton en colère viendra vers vous et vous réprimandera.



Les ressources


Site Web de Proguard . Il existe également des informations sur DexGuard.
Divers exemples de règles
R8
Enregistrement d'une présentation du fonctionnement de Proguard avec DroidCon
ProGuard efficace conserve les règles pour l'enregistrement de présentation de petites applications (Google I / O '18)
Instructions pour activer et configurer Proguard pour Android
Page wiki
Redex

Source: https://habr.com/ru/post/fr415499/


All Articles