Exploration et pratique de l'Android Minceur de JD Finance

Auteur: JD Technology Feng Jianhua

1. Origines

Avec la mise à jour itérative continue de l'entreprise, la taille de l'application augmente également rapidement. De 2019 à 2022, elle a déjà dépassé 117 M. Au cours de cette période, nous avons également procédé à quelques optimisations, comme le montre la partie rouge de la figure 1. Cependant , nous sommes confrontés à de nouveaux défis tout en optimisant. Code incrémental, la taille des packages n'a cessé d'augmenter . La taille du package affecte directement ou indirectement des indicateurs importants tels que le taux de conversion de téléchargement, le temps d'installation et l'espace disque, il est donc nécessaire de consacrer de l'énergie à explorer une optimisation plus approfondie de la taille du package d'installation. Selon les données internes de Google Store, chaque réduction de 10 millions de la taille de l'APK peut augmenter le taux de conversion de téléchargement d'environ 1,5 % en moyenne, comme le montre la figure 2 :

Figure 1 Le processus de changement de volume de JD Finance Android version 2019-2022 (la partie rouge fait partie de l'optimisation faite sur la période, mais elle a vite rebondi)

Figure 2 Augmentation du taux de conversion de l'application Google Store / 10M [1]

Par conséquent, à partir de septembre 2022, nous avons effectué une rectification spéciale pour l'amincissement de l'APP financière. Sans tenir compte de l'augmentation de la situation et sans supprimer le code des affaires, nous avons réalisé l'amincissement de 117M à 74M. bas le package d'installation cette fois, nous avons rencontré beaucoup de pièges, mais aussi accumulé une certaine expérience, ici pour partager avec vous.

2. Analyse APK

Ensuite, nous analyserons brièvement les différents composants de l'Apk et la structure standard de l'Apk en tant que ZIP, afin de fournir un support de données pour la définition des objectifs et le démontage des tâches du package minceur.

2.1 Analyse du contenu APK

Figure 3 Structure du fichier APK

• L'APK classes.dex peut contenir un ou plusieurs fichiers classes.dex, et le code source Java/Kotlin de l'application existera éventuellement dans le fichier classes.dex sous forme de bytecode.

•resources.arsc  L'outil aapt empaquetera certaines ressources ou index de ressources dans resources.arsc lors de la compilation des ressources.

•res/ Fichiers de ressources à l'exception des valeurs sous le répertoire res dans le projet de code source, et les chemins de ces fichiers seront enregistrés dans resources.arsc en même temps.

• lib/nativeLibraries, c'est-à-dire le fichier so sous le répertoire jni du projet de code source, et le répertoire secondaire est l'ABI pris en charge par NDK.

•assets/ est différent du répertoire de ressources res/. Les fichiers de ressources sous assets/ ne généreront pas d'entrées de requête dans resources.arsc, et le répertoire de ressources sous assets/ peut être entièrement personnalisé et obtenu via l'objet AssetManager dans le programme.

•META-INF/  Ce dossier contient principalement les fichiers de signature CERT.SF et CERT.RSA, et le fichier manifeste MANIFEST.MF .

• Fichier manifeste d'application AndroidManifest.xml, utilisé pour décrire les informations de base de l'application, notamment le nom du package d'application, l'ID de l'application, les composants de l'application, les autorisations requises, la compatibilité de l'appareil, etc.

2.2 Analyse de la taille du SDK

Grâce à notre plate-forme d'amélioration de l'efficacité énergétique Pandora[7], nous pouvons voir intuitivement la taille du SDK, comme le montre la figure 4 :

Figure 4 Tri par taille du SDK (y compris le numéro de version)

 

Figure 5 Liste et taille des bibliothèques SO incluses dans le SDK

Selon l'analyse du SDK et combinée avec l'entreprise, déterminez quelle entreprise convient au plug-in, puis réduisez intuitivement la taille du package.

2.3 Analyse de la structure ZIP

Vous pouvez utiliser la commande zipinfo pour générer le journal d'informations détaillées de chaque fichier dans le package compressé, utilisation : zipinfo -l --t --h test.apk > test.txt

Le fichier journal de sortie est ouvert comme illustré à la figure 6. Il existe une ligne d'informations de compression pour chaque fichier, y compris le nom du fichier, la taille d'origine, la taille compressée et d'autres indicateurs :

 

Figure 6 La taille des informations de fichier dans l'APK

Analysez les informations de journal ci-dessus ligne par ligne, et classez et comptez en fonction du chemin d'accès au nom de fichier et du type de fichier désobscurcis, et vous pouvez obtenir les informations générales d'Apk, y compris le nombre de différents types de fichiers, la taille totale, la taille du fichier unique et d'autres indicateurs Et créez un index de taille de fichier.

3. Pratique minceur

Le chemin de mise en œuvre global est illustré à la figure 7, qui est principalement divisée en :

1. Solutions techniques conventionnelles, via le plug-in Gradle (pas d'intrusion de code, automatisation) pour compléter l'amincissement de l'APP lors de la compilation ;

2. Des solutions techniques avancées, transforment certains métiers différemment grâce au plug-in ou au téléchargement dynamique SO Plus il y a de transformations métiers, plus les bénéfices sont importants ;

3. Le plan d'optimisation des activités, basé sur le point d'enfouissement des données de la ligne métier, génère une UV d'accès pour le classement et renvoie la ligne métier avec une UV inférieure au comité d'architecture pour évaluer si elle peut être hors ligne ou transformée via la solution technique avancée (2), puis Réduire la taille du paquet.

 

Figure 7 chemin de mise en œuvre global

3-1 Solutions techniques classiques

3-1-1 Traitement des images

Après l'analyse ci-dessus de l'APP, il est conclu que l'image occupe le plus grand volume. Par conséquent, toutes les images de l'APP, y compris le SDK, sont automatiquement optimisées grâce à la tâche d'amincissement pendant le processus de compilation et d'emballage. Le schéma d'optimisation global est affiché dans la Figure 8 :

Figure 8 Schéma d'optimisation d'image

1. Optimisation multi-DPI :

Afin de s'adapter aux appareils avec différentes résolutions ou modes, Android a conçu des chemins de ressources pour les développeurs avec plusieurs configurations de la même ressource. Lorsque l'application obtient des ressources d'image via des ressources, elle charge automatiquement les ressources adaptées en fonction de la configuration de l'appareil, mais celles-ci Le problème évident est que les appareils à haute résolution contiennent des images inutiles à basse résolution ou que les appareils à basse résolution contiennent des images inutiles à haute résolution.

Dans des circonstances normales, pour le marché national des applications, afin de réduire la taille du package, l'application choisira un ensemble de dpi avec la part de marché la plus élevée (google recommande xxhdpi) pour être compatible avec tous les appareils. La plupart des applications ciblant le marché des applications à l'étranger seront packagées et téléchargées sur Google Play via AppBundle, et peuvent profiter de la fonction de distribution dynamique des dpi. Les téléphones mobiles avec différentes résolutions peuvent télécharger des ressources d'image de différents dpi, nous devons donc fournir plusieurs ensembles de dpi pour satisfaire tous les appareils. . Dans le projet, certaines de nos images n'ont qu'un seul ensemble de dpi, et certaines ont plusieurs ensembles de dpi. Pour les deux scénarios ci-dessus, nous avons fusionné des ressources et copié des ressources lors de l'empaquetage, réduisant ainsi la taille de l'emballage.

2. Convertir au format webp :

WebP est un format de fichier image fourni par Google qui prend en charge la compression avec perte et la compression sans perte, et peut fournir une meilleure compression que JPEG ou PNG. Prend en charge les images WebP avec perte dans Android 4.0 (niveau API 14), les images WebP sans perte et transparentes dans Android 4.3 (niveau API 18) et supérieur

Par conséquent : nous utilisons le plug-in pour convertir le format de l'image via le programme shell fourni par Google pendant la période de compilation, et la conversion supprime avec succès l'ancienne image, obtenant ainsi l'effet d'amincissement APK

compression 3.png

Pngquant est un outil de compression png facile à utiliser, un outil de ligne de commande qui peut effectuer une compression d'image avec perte, donc après le traitement de 1 et 2, vous pouvez utiliser Pngquant pour effectuer une compression secondaire afin d'obtenir un meilleur amincissement de l'image.

Optimisation en ligne du fichier 3-1-2 R

DEX est le fichier de bytecode compilé à partir du code source Java/Kotlin. L'optimisation de DEX consiste en fait à optimiser le fichier de bytecode. DEX contient un grand nombre de fichiers d'index de ressources R. Ici, nous parlons principalement de la façon d'intégrer l'ID de ressource. Supprimez le fichier R pour atteindre l'objectif d'amincissement de l'APK :

Analyse de faisabilité de l'amincissement du dossier R

Dans l'étape de développement quotidien, référencez les ressources dans le projet principal via R.xx.xx. Après compilation, les constantes correspondant aux références de classe R seront compilées dans la classe.

setContentView(2131427356);

Ce changement est appelé inlining, qui est un mécanisme de java (si une constante est marquée comme static final, la constante sera inline dans le code lors de la compilation java, réduisant l'adressage mémoire des variables une fois). Dans le projet non principal, l'ID de ressource de classe R est compilé dans la classe par référence et aucune inlining ne sera générée.

setContentView(R.layout.activity_main);

La raison de ce phénomène est causée par l'outil de conditionnement AGP. Pour plus de détails, vous pouvez vérifier le processus de traitement du plugin Android Gradle sur le fichier R. Conclusion : Le programme peut s'exécuter après l'intégration de l'identifiant de classe R, mais tous les projets ne généreront pas automatiquement un phénomène d'intégration. Nous devons utiliser des moyens techniques pour intégrer l'identifiant de classe R dans le programme au bon moment. Si vous comptez sur des fichiers R encore une fois, vous pouvez supprimer les fichiers R pour atteindre l'objectif de réduire la taille du package pendant que l'application s'exécute normalement. Comme le montre la figure 9, un grand nombre de fichiers R seront générés une fois la compilation terminée :

Figure 9 Diagramme schématique de la génération du fichier R du projet

Le schéma général est illustré à la figure 10 :

Figure 10 Processus d'optimisation du fichier R

Remarque : dans la phase de remplacement, une vérification secondaire doit être ajoutée pour empêcher l'exception ResourceNotFind d'apparaître lors de l'exécution une fois le remplacement terminé, comme indiqué ci-dessous :

try {
    int value = RManager.checkInt(type, name);
}catch (Exception e){
    String errorMsg = "resource is not found(I),className="+className+",fieldName="+owner+"."+name;
    throw new ResourceNotFoundException(errorMsg);
}
try {
    int[] value = RManager.checkIntArray(type, name);
}catch (Exception e){
    String errorMsg = "resource is not found(I[]),className="+className+",fieldName="+owner+"."+name;
    throw new ResourceNotFoundException(errorMsg);
}

 

3-1-3 AndResGuard pour la confusion des ressources

1. Analyse du processus de chargement des ressources

Au cours du processus de développement, nous utilisons les constantes dans R.java générées par aapt pour utiliser les ressources, et les endroits où les constantes sont utilisées après la compilation seront remplacés par les valeurs constantes, comme indiqué ci-dessous :

final View layout = inflater.inflate(2131165182, container, false);

C'est-à-dire que nous utilisons une valeur int pour trouver la ressource via Resource. Alors, comment Resource trouve-t-il des ressources spécifiques via des valeurs int ? Lorsque nous décompressons l'apk, nous pouvons voir qu'il contient un fichier resources.arsc. Ce fichier est également généré par aapt. La relation de mappage entre l'ID de ressource et la clé de ressource est stockée dans le fichier. La ressource trouve les ressources en fonction de cette relation de mappage. .

2.ressources.arsc:

La figure 11 montre la relation de mappage stockée dans resources.arsc. resources.arsc peut être compris comme une base de données de mappage de ressources, et le chemin et le nom spécifiques sont mappés en fonction de l'ID.

 

Figure 11 analyse resources.arsc

Après avoir décompressé l'APK, convertissez le nom du fichier de ressources en une chaîne courte, telle que res/layout/hello.xml en r/l/a.xml, puis modifiez la valeur correspondant à resources.arsc pour obtenir l'effet amincissant global.

AndResGuard[5] est un outil d'optimisation des ressources lancé par WeChat. Son idée de base est similaire à l'obfuscation de ProGuard, qui peut réaliser les solutions ci-dessus.

Compression 3-1-4 7zip

Explication de la commande 7zip :

-t : spécifie le type de compression, prend en charge 7z, xz, split, zip, gzip, bzip2, tar, ....

-m : spécifie l'algorithme de compression, la valeur par défaut est Deflate

Le processus spécifique est le suivant :

Étape 1 : Utilisez la commande 7z pour décompresser le package non signé dans le répertoire spécifié : 7za x ${unsigned package} -o${7z decompressed directory}

Étape 2 : Tout d'abord, utilisez la commande 7z pour compresser tous les répertoires décompressés : 7za a -tzip -mx9 ${target 7z file name} ${7z decompressed directory}

Étape 3 : Obtenez des fichiers de type de stockage et obtenez une liste des fichiers dont le mode de compression est stocké via la commande aapt dans le SDK Android : aapt l -v ${unsigned package}

Étape 4 : mettez à jour le fichier de type de stockage et utilisez la commande 7z pour mettre à jour le fichier de type de stockage vers le package d'installation 7zip généré à la deuxième étape : 7za a -tzip -mx0 ${target 7z file name} ${storage type file directory }

3-1-5 Configuration de l'architecture CPU

Créez différents types de packages d'installation en fonction de différentes architectures de processeur. À l'heure actuelle, les appareils grand public sont des machines 64 bits, de sorte que le marché Android publie principalement des packages d'installation compilés et construits sur la base de arm64-v8a.

ndk {
    abiFilters arm64-v8a
}

Compression 3-1-6 arcsc

resources.arsc a un gain de volume élevé pour la compression, mais sa compression affecte la vitesse de démarrage et les métriques de mémoire. La raison est la suivante : lorsque le système charge le fichier arsc, si le fichier arsc n'est pas compressé, mmap peut être utilisé pour le mappage de la mémoire ; si le fichier arsc est compressé, il doit être décompressé et lu dans le tampon RAM, ce qui augmentera l'utilisation de la mémoire et cela ralentira également la vitesse de démarrage.

Pour la même considération, l'officiel ne peut pas utiliser cette méthode pour forcer resources.arsc après targetSdkVersion>=30, sinon l'installation échouera directement, donc cet article ne détaillera pas.

3-1-7 Traitement linguistique internationalisé

L'application JD Financial ne fonctionne actuellement que sur le marché intérieur, mais des dizaines de langues ont été ajoutées à un grand nombre de SDK connectés , ce qui entraîne une taille globale plus importante. Après évaluation, les ressources linguistiques inutiles peuvent être supprimées en configurant resConfigs.

defaultConfig {
    resConfigs "zh","en"
}

3-1-8 shrinkRessources

shrinkResources : utilisé pour détecter et supprimer les fichiers de ressources inutiles lors de la compilation, c'est-à-dire les ressources qui ne sont pas référencées

minifyEnabled : utilisé pour activer la suppression des codes inutiles, tels que les codes qui ne sont pas référencés, donc si vous avez besoin de savoir si une ressource est référencée, vous devez l'utiliser en conjonction avec minifyEnabled. Ce n'est que lorsque les deux sont vrais qu'il supprimera vraiment invalide codes et ressources non référencées le but de.

Sa fonction est de remplacer les fichiers de ressources non référencés par un fichier de petit format (il y a toujours une empreinte, tout en conservant l'entrée de la ressource, donc le volume de resources.arsc ne sera pas réduit) , accessible via res/raw/ Le keep Le fichier .xml configure le mode de rétrécissement et la liste blanche.

buildTypes {
   release {
      // 不显示Log
      buildConfigField "boolean", "LOG_DEBUG", "false"
      //混淆
      minifyEnabled true
      // 移除无用的resource文件
      shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig sign.release
   }
}

3-1-9 Contraintes de codage

• Utilisez les types d'énumération le moins possible, car l'énumération augmentera beaucoup de volume après avoir été compilée en bytecode, comme illustré à la figure 12 (le bytecode après la compilation de 22 lignes de code est de 86 lignes)

Figure 12 Comparaison des bytecodes compilés des types énumérés

• Supprimer la sortie LOG inutile

3-2 Solutions techniques avancées

La technologie de téléchargement dynamique et de plug-in de la bibliothèque SO est essentiellement une catégorie de téléchargement dynamique. Les deux solutions peuvent être utilisées en continu dans l'entreprise pendant une longue période. La figure 13 montre comment choisir dans le processus d'utilisation spécifique :

Figure 13 Comment les entreprises choisissent les solutions avancées

3-2-1 Chargement dynamique de la bibliothèque SO

Certaines entreprises de l'APP ne sont pas adaptées à la transformation de plug-in. Après le démantèlement, on constate que la bibliothèque SO représente une grande proportion. Par conséquent, le téléchargement dynamique peut être envisagé pour la transformation afin de réduire la taille.

Deux façons de charger la bibliothèque SO

Dans le premier cas, nous pouvons télécharger directement la bibliothèque SO et la placer dans le répertoire spécifié.

La deuxième façon est de charger la bibliothèque SO dans le répertoire défini par la variable d'environnement, nous devons donc ajouter le répertoire spécifié à la variable d'environnement pour charger la bibliothèque SO normalement

System.load("{安全路径}/libxxx.so") 
System.load("xxx") 

1. Comment définir l'emplacement de la variable d'environnement de la bibliothèque SO dans l'APP (en référence à Tinker) :

final Field pathListField = ShareReflectUtil.findField(classLoader, "pathList");
final Object dexPathList = pathListField.get(classLoader);

final Field nativeLibraryDirectories = ShareReflectUtil.findField(dexPathList, "nativeLibraryDirectories");

List<File> origLibDirs = (List<File>) nativeLibraryDirectories.get(dexPathList);
if (origLibDirs == null) {
    origLibDirs = new ArrayList<>(2);
}
final Iterator<File> libDirIt = origLibDirs.iterator();
while (libDirIt.hasNext()) {
    final File libDir = libDirIt.next();
    if (folder.equals(libDir)) {
        libDirIt.remove();
        break;
    }
}
origLibDirs.add(0, folder);

final Field systemNativeLibraryDirectories = ShareReflectUtil.findField(dexPathList, "systemNativeLibraryDirectories");
List<File> origSystemLibDirs = (List<File>) systemNativeLibraryDirectories.get(dexPathList);
if (origSystemLibDirs == null) {
    origSystemLibDirs = new ArrayList<>(2);
}

final List<File> newLibDirs = new ArrayList<>(origLibDirs.size() + origSystemLibDirs.size() + 1);
newLibDirs.addAll(origLibDirs);
newLibDirs.addAll(origSystemLibDirs);

final Method makeElements = ShareReflectUtil.findMethod(dexPathList, "makePathElements", List.class);

final Object[] elements = (Object[]) makeElements.invoke(dexPathList, newLibDirs);

final Field nativeLibraryPathElements = ShareReflectUtil.findField(dexPathList, "nativeLibraryPathElements");
nativeLibraryPathElements.set(dexPathList, elements);

2. Comment supprimer la bibliothèque SO spécifiée et l'ensemble du processus de chargement, comme illustré à la Figure 14 :

Figure 14 Processus de suppression et de chargement de la bibliothèque SO

3-2-2 Plugin

Qu'est-ce qu'un plug-in :

Le plug-in consiste à diviser un Apk en différents sous-Apks (c'est-à-dire différents plug-ins) en fonction des fonctions de l'entreprise. Chaque sous-Apk peut être compilé et empaqueté indépendamment, et l'Apk intégré est finalement publié et lancé. Lorsqu'Apk est utilisé, chaque plug-in est chargé dynamiquement, et les plug-ins peuvent également être corrigés à chaud et mis à jour à chaud.

• Hôte : l'application principale peut être utilisée pour charger des plug-ins et également devenir un hôte

• Plug-in : l'application plug-in, l'application chargée par l'hôte, peut être le même fichier Apk que l'application normale

Quelle forme d'entreprise convient à la transformation de plug-in :

• L'entreprise est relativement indépendante et complètement découplée de l'application hôte

• Faible coût de rénovation et revenus relativement élevés

• Occupe un grand volume

Après une série d'évaluations, l'activité vidéo répond aux points ci-dessus, et l'effet après transformation est illustré à la figure 15 :

 

Figure 15 L'effet de la transformation plug-in de la salle d'affaires vidéo

3-3 Plan d'optimisation des affaires

Avec de plus en plus d'entreprises, certaines anciennes UV d'entreprise deviennent de plus en plus faibles, de sorte qu'un ensemble de processus d'optimisation hors ligne d'entreprise a été formulé, comme le montre la figure 16 :

Figure 16 Processus de la solution d'optimisation métier

4. Contrôle

La mise en place du plan minceur est très importante, et il est plus important que le contrôle de suivi ne rebondisse pas.En faisant de la gouvernance minceur, nous explorons un mécanisme de contrôle normalisé d'autre part, et finalement précipité un ensemble de normes de contrôle et mécanismes de contrôle. Le but de la gestion et du contrôle n'est pas de limiter l'itération métier ou le nouveau code, mais comment réaliser ses fonctions dans un code limité et améliorer la sensibilisation des ingénieurs à l'amincissement dans le codage quotidien.

4.1 Spécification d'accès au SDK

Afin d'empêcher l'expansion désordonnée du SDK, la spécification d'accès au SDK a été formulée. Sur le principe d'assurer la fonction, la taille du SDK est strictement contrôlée et le rebond du volume APP est contrôlé au maximum.

4.2 Processus de contrôle

 

Figure 17 Processus de contrôle

Selon les modifications des fichiers de ressources telles que l'ajout de contenu, la suppression de contenu, l'augmentation de contenu, la réduction de contenu, les fichiers en double, la gestion du code, etc., combinées aux spécifications de contrôle de gouvernance, etc., l'emballage et la construction seront comparés à la version historique pour obtenir le contenu modifié. Pour évaluer s'il y a place à l'optimisation, et donner l'objectif d'optimisation, et reconstruire l'intégration de l'emballage après l'optimisation.

5. Réalisations et planification du suivi

5.1 Réalisations

Grâce aux mesures ci-dessus, la version Android de JD Finance a été itérée de cinq versions en deux trimestres, de 117M à l'actuel 74M (Figure 18), et l'ensemble a été maintenu dans une plage contrôlable. Dans le même temps, dans les prochaines itérations de la version, nous normaliserons l'amincissement de l'APK et maintiendrons toujours la taille du paquet dans une plage contrôlable.

 

Figure 18 Résultats minceur de l'APP financière

5.2 Planification ultérieure

Optimisation continue des moyens techniques :

L'accumulation et l'itération continues des activités généreront toujours des ressources inutiles. Ces fichiers et codes inutiles doivent donc être nettoyés régulièrement pour alléger le package d'installation.

Effectuez un bon travail de surveillance de chaque version, comparez les différences entre les versions et constatez qu'elle peut être optimisée à l'aide de moyens techniques sans affecter l'entreprise.

Construction de la plateforme de gestion et de contrôle en ligne :

Au début, la gestion et le contrôle hors ligne étaient utilisés, ce qui prenait un peu de temps à mettre en œuvre. À l'avenir, nous améliorerons la construction de la plate-forme de gestion et de contrôle en ligne, l'intégrerons à l'ensemble de la plate-forme de publication et de construction de l'application, former un mécanisme de chaîne de montage et faire un bon travail de gestion et de contrôle.

Résumé : Il reste encore un long chemin à parcourir pour explorer l'allègement des packages d'installation. Cet article ne liste que quelques solutions d'allègement couramment utilisées. Outre l'optimisation pour les projets de grande envergure, il est également nécessaire de faire un bon travail de gouvernance entre les projets. et continuer à optimiser la taille de l'APP, pour améliorer l'expérience utilisateur.

【Référence】

[1] Taille du package et taux de conversion d'installation
https://medium.com/googleplaydev/shrinking-apks- growing-installs-5d3fcba23ce2

[2] ProGuard  https://www.guardsquare.com/proguard

[3] R8 https://r8.googlesource.com/r8

[4] Comparaison de ProGuard et R8
https://www.guardsquare.com/blog/proguard-and-r8

[5] AndResGuard https://github.com/shwenzhang/AndResGuard

[6] AGP https://developer.android.com/studio/releases/gradle-plugin

[7] Pandora : un outil d'amélioration de l'efficacité énergétique dans les phases de R&D et de test basé sur une technologie décentralisée

{{o.name}}
{{m.name}}

Je suppose que tu aimes

Origine my.oschina.net/u/4090830/blog/8590988
conseillé
Classement