Exploración y práctica del adelgazamiento de Android de JD Finance

Autor: JD Tecnología Feng Jianhua

1. Antecedentes

Con la actualización iterativa continua del negocio, el tamaño de la aplicación también está aumentando rápidamente. De 2019 a 2022, una vez superó los 117 millones. Durante este período, también realizamos algunas optimizaciones, como se muestra en la parte roja de la Figura 1. Sin embargo , nos enfrentamos a nuevos desafíos mientras optimizamos Código incremental, el tamaño del paquete ha seguido aumentando . El tamaño del paquete afecta directa o indirectamente indicadores importantes como la tasa de conversión de descarga, el tiempo de instalación y el espacio en disco, por lo que es necesario dedicar energía a explorar una optimización más profunda del tamaño del paquete de instalación. Según los datos internos de Google Store, cada reducción de 10 millones en el tamaño del APK puede aumentar la tasa de conversión de descarga en un promedio de ~1,5 %, como se muestra en la Figura 2:

Figura 1 El proceso de cambio de volumen de JD Finance Android versión 2019-2022 (la parte roja es parte de la optimización realizada durante el período, pero se recuperó pronto)

Figura 2 Aumento de la tasa de conversión de la aplicación Google Store/10 millones [1]

Por lo tanto, a partir de septiembre de 2022, hemos realizado una rectificación especial para el adelgazamiento de la aplicación financiera. Sin considerar el aumento de la situación y sin eliminar el código comercial, realizamos el adelgazamiento de 117M a 74M. En proceso de adelgazamiento Al bajar el paquete de instalación esta vez, encontramos muchas trampas, pero también acumulamos algo de experiencia, aquí para compartir con ustedes.

2. Análisis de APK

A continuación, analizaremos brevemente los diversos componentes del Apk y la estructura estándar del Apk como un ZIP, para brindar soporte de datos para el establecimiento de objetivos y el desmontaje de tareas del paquete de adelgazamiento.

2.1 Análisis de contenido APK

Figura 3 Estructura del APK

•classes.dex APK puede contener uno o más archivos classs.dex, y el código fuente de Java/Kotlin en la aplicación finalmente existirá en el archivo classes.dex en forma de código de bytes.

•resources.arsc  La herramienta aapt empaquetará algunos recursos o índices de recursos en resources.arsc al compilar recursos.

•res/ Los archivos de recursos excepto los valores del directorio res en el proyecto de código fuente, y las rutas de estos archivos se registrarán en resources.arsc al mismo tiempo.

• lib/ nativeLibraries, es decir, el archivo so en el directorio jni del proyecto de código fuente y el directorio secundario es la ABI admitida por NDK.

•assets/ es diferente del directorio de recursos res/ Los archivos de recursos en assets/ no generarán entradas de consulta en resources.arsc, y el directorio de recursos en assets/ se puede personalizar completamente y obtener a través del objeto AssetManager en el programa.

•META-INF/  Esta carpeta contiene principalmente archivos de firma CERT.SF y CERT.RSA, y el archivo de manifiesto MANIFEST.MF .

• Archivo de manifiesto de la aplicación AndroidManifest.xml, utilizado para describir la información básica de la aplicación, que incluye principalmente el nombre del paquete de la aplicación, la identificación de la aplicación, los componentes de la aplicación, los permisos necesarios, la compatibilidad del dispositivo, etc.

2.2 Análisis del tamaño del SDK

A través de Pandora[7], nuestra plataforma de mejora de la eficiencia energética de desarrollo propio, podemos ver intuitivamente el tamaño del SDK, como se muestra en la Figura 4:

Figura 4 Ordenación por tamaño de SDK (incluido el número de versión)

 

Figura 5 Lista y tamaño de las bibliotecas SO incluidas en el SDK

De acuerdo con el análisis del SDK y combinado con el negocio, determine qué negocio es adecuado para el complemento y luego reduzca intuitivamente el tamaño del paquete.

2.3 Análisis de estructura ZIP

Puede usar el comando zipinfo para generar el registro de información detallada de cada archivo en el paquete comprimido, uso: zipinfo -l --t --h test.apk > test.txt

El archivo de registro de salida se abre como se muestra en la Figura 6. Hay una línea de información de compresión para cada archivo, incluido el nombre del archivo, el tamaño original, el tamaño comprimido y otros indicadores:

 

Figura 6 El tamaño de la información del archivo en el APK

Analice la información de registro anterior línea por línea, y clasifique y cuente de acuerdo con la ruta del nombre de archivo desofuscado y el tipo de archivo, y puede obtener la información general de Apk, incluida la cantidad de varios tipos de archivos, tamaño total, tamaño de archivo único y otros indicadores Y cree un índice de tamaño de archivo.

3. Práctica de adelgazamiento

La ruta de implementación general se muestra en la Figura 7, que se divide principalmente en:

1. Soluciones técnicas convencionales, a través del complemento Gradle (sin intrusión de código, automatización) para completar el adelgazamiento de la aplicación durante la compilación;

2. Soluciones técnicas avanzadas, transforme algunas líneas de negocios de manera diferente a través de complementos o descargas dinámicas SO. Cuantas más transformaciones comerciales, mayores serán los beneficios;

3. Plan de optimización comercial, basado en el punto de enterramiento de datos de la línea comercial, generar acceso UV para la clasificación y retroalimentar la línea comercial con menor UV al Comité de Arquitectura para evaluar si puede estar fuera de línea o transformarse a través de la solución técnica avanzada. (2) y luego Reducir el tamaño del paquete.

 

Figura 7 ruta de implementación general

3-1 Soluciones técnicas convencionales

3-1-1 Procesamiento de imágenes

Después del análisis anterior de la aplicación, se concluye que la imagen ocupa el mayor volumen. Por lo tanto, todas las imágenes de la aplicación, incluido el SDK, se optimizan automáticamente a través de la tarea de adelgazamiento durante el proceso de compilación y empaquetado. Se muestra el esquema de optimización general. en la figura 8:

Figura 8 Esquema de optimización de imagen

1. Optimización multi-DPI:

Para adaptarse a dispositivos con diferentes resoluciones o modos, Android ha diseñado rutas de recursos para desarrolladores con múltiples configuraciones de un mismo recurso, cuando la aplicación obtiene recursos de imagen a través de recursos, carga automáticamente los recursos adaptados según la configuración del dispositivo, pero estos El problema obvio es que los dispositivos de alta resolución contienen imágenes inútiles de baja resolución o los dispositivos de baja resolución contienen imágenes inútiles de alta resolución.

En circunstancias normales, para el mercado de aplicaciones domésticas, con el fin de reducir el tamaño del paquete, la aplicación elegirá un conjunto de dpi con la participación de mercado más alta (google recomienda xxhdpi) para que sea compatible con todos los dispositivos. La mayoría de las aplicaciones dirigidas al mercado de aplicaciones en el extranjero se empaquetarán y cargarán en Google Play a través de AppBundle, y pueden disfrutar de la función de distribución dinámica de dpi. Los teléfonos móviles con diferentes resoluciones pueden descargar recursos de imagen de diferentes dpi, por lo que debemos proporcionar varios conjuntos. de dpi para satisfacer todos los dispositivos. En el proyecto, algunas de nuestras imágenes tienen solo un conjunto de dpi, y algunas tienen varios conjuntos de dpi. Para los dos escenarios anteriores, fusionamos recursos y copiamos recursos al empaquetar, reduciendo el tamaño del paquete.

2. Convertir a formato webp:

WebP es un formato de archivo de imagen proporcionado por Google que admite compresión con pérdida y compresión sin pérdida, y puede proporcionar una mejor compresión que JPEG o PNG. Admite imágenes WebP con pérdida en Android 4.0 (nivel de API 14), imágenes WebP transparentes y sin pérdida en Android 4.3 (nivel de API 18) y superior

Por lo tanto: usamos el complemento para convertir el formato de la imagen a través del programa shell proporcionado por Google durante el período de compilación, y la conversión elimina con éxito la imagen anterior, logrando así el efecto de reducción de APK

Compresión 3.png

Pngquant es una herramienta de compresión png fácil de usar, una herramienta de línea de comandos que puede realizar una compresión de imágenes con pérdida, por lo que después del procesamiento de 1 y 2, puede usar Pngquant para realizar una compresión secundaria para lograr una mejor reducción de la imagen.

Optimización en línea de archivos 3-1-2 R

DEX es el archivo de código de bytes compilado a partir del código fuente de Java/Kotlin. La optimización de DEX es en realidad cómo optimizar el archivo de código de bytes. DEX contiene una gran cantidad de archivos de índice de recursos R. Aquí hablamos principalmente sobre cómo insertar en línea a través de la ID de recurso. Elimine el archivo R para lograr el propósito de adelgazar APK:

Análisis de viabilidad de adelgazamiento de archivos R

En la etapa de desarrollo diario, los recursos de referencia en el proyecto principal a través de R.xx.xx Después de la compilación, las constantes correspondientes a las referencias de la clase R se compilarán en la clase.

setContentView(2131427356);

Este cambio se denomina inserción, que es un mecanismo de Java (si una constante se marca como estática final, la constante se incorporará en el código durante la compilación de Java, reduciendo una vez el direccionamiento de memoria de las variables). En el proyecto no principal, el Id. de recurso de la clase R se compila en la clase por referencia y no se generará ninguna inserción.

setContentView(R.layout.activity_main);

El motivo de este fenómeno es causado por la herramienta de empaquetado AGP. Para obtener más información, puede consultar el proceso de procesamiento del complemento gradle de Android en el archivo R. Conclusión: el programa puede ejecutarse después de que la identificación de la clase R esté en línea, pero no todos los proyectos generarán automáticamente un fenómeno en línea. Necesitamos usar medios técnicos para incorporar la identificación de la clase R en el programa en el momento adecuado. Si confía en los archivos R nuevamente, puede eliminar los archivos R para lograr el propósito de reducir el tamaño del paquete mientras la aplicación se ejecuta normalmente.Como se muestra en la Figura 9, se generará una gran cantidad de archivos R después de que se complete la compilación:

Figura 9 Diagrama esquemático de la generación del archivo R del proyecto

El esquema general se muestra en la Figura 10:

Figura 10 Proceso de optimización de archivos R

Nota : en la fase de reemplazo, se debe agregar una verificación secundaria para evitar que aparezca la excepción ResourceNotFind en el tiempo de ejecución después de que se complete el reemplazo, como se muestra a continuación:

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 por confusión de recursos

1. Análisis del proceso de carga de recursos

Durante el proceso de desarrollo, usamos las constantes en R.java generadas por aapt para usar recursos, y los lugares donde se usan las constantes después de la compilación se reemplazarán con los valores constantes, como se muestra a continuación:

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

Es decir, usamos un valor int para encontrar el recurso a través de Resource. Entonces, ¿cómo encuentra Resource recursos específicos a través de valores int? Cuando descomprimimos el apk, podemos ver que hay un archivo resources.arsc adentro. Este archivo también es generado por aapt. La relación de mapeo entre la identificación del recurso y la clave del recurso se almacena en el archivo. El recurso encuentra los recursos de acuerdo con esta relación de mapeo. .

2.recursos.arsc:

La Figura 11 muestra la relación de mapeo almacenada en resources.arsc. resources.arsc puede entenderse como una base de datos de mapeo de recursos, y la ruta y el nombre específicos se asignan de acuerdo con la ID.

 

Figura 11 análisis de resources.arsc

Después de descomprimir el APK, convierta el nombre del archivo de recursos en una cadena corta, como res/layout/hello.xml en r/l/a.xml, y luego cambie el valor correspondiente a resources.arsc para lograr el efecto adelgazante general.

AndResGuard[5] es una herramienta de optimización de recursos lanzada por WeChat. Su idea básica es similar a la ofuscación en ProGuard, que puede realizar las soluciones anteriores.

Compresión 3-1-4 7zip

Explicación del comando 7zip:

-t: Especifica el tipo de compresión, admite 7z, xz, split, zip, gzip, bzip2, tar, ....

-m: especifica el algoritmo de compresión, el valor predeterminado es Deflate

El proceso específico es el siguiente:

Paso 1: utilice el comando 7z para descomprimir el paquete sin firmar en el directorio especificado: 7za x ${paquete sin firmar} -o${7z directorio descomprimido}

Paso 2: Primero, use el comando 7z para comprimir todos los directorios descomprimidos: 7za a -tzip -mx9 ${nombre de archivo de destino 7z} ${directorio descomprimido 7z}

Paso 3: Obtenga archivos de tipo de almacenamiento y obtenga una lista de archivos cuyo modo de compresión se almacena a través del comando aapt en el SDK de Android: aapt l -v ${paquete sin firmar}

Paso 4: actualice el archivo de tipo de almacenamiento y use el comando 7z para actualizar el archivo de tipo de almacenamiento al paquete de instalación de 7zip generado en el segundo paso: 7za a -tzip -mx0 ${nombre de archivo de destino 7z} ${directorio de archivo de tipo de almacenamiento }

3-1-5 Configuración de la arquitectura de la CPU

Cree diferentes tipos de paquetes de instalación de acuerdo con las diferentes arquitecturas de CPU. En la actualidad, los dispositivos principales son máquinas de 64 bits, por lo que el mercado de Android lanza principalmente paquetes de instalación compilados y construidos en base a arm64-v8a.

ndk {
    abiFilters arm64-v8a
}

Compresión arsc 3-1-6

resources.arsc tiene una gran ganancia de volumen para la compresión, pero comprimirlo afecta la velocidad de inicio y las métricas de memoria. La razón es: cuando el sistema carga el archivo arsc, si el archivo arsc no está comprimido, se puede usar mmap para el mapeo de memoria; si el archivo arsc está comprimido, debe descomprimirse y leerse en el búfer de RAM, lo que aumentará uso de memoria y también ralentizará la velocidad de inicio.

Por la misma consideración, el oficial no puede usar este método para forzar resources.arsc después de targetSdkVersion>=30, de lo contrario, la instalación fallará directamente, por lo que este artículo no dará más detalles.

3-1-7 Procesamiento de idiomas internacionalizado

Actualmente, la aplicación JD Financial solo funciona en el mercado nacional, pero se han agregado docenas de idiomas a una gran cantidad de SDK que están conectados , lo que resulta en un tamaño general más grande. Después de la evaluación, los recursos de idioma inútiles se pueden eliminar configurando resConfigs.

defaultConfig {
    resConfigs "zh","en"
}

3-1-8 encogerRecursos

ShrinkResources: se utiliza para detectar y eliminar archivos de recursos inútiles durante la compilación, es decir, recursos a los que no se hace referencia.

minifyEnabled: se usa para habilitar la eliminación de códigos inútiles, como códigos que no están referenciados, por lo que si necesita saber si un recurso está referenciado, debe usarlo junto con minifyEnabled. Solo cuando ambos son verdaderos, realmente eliminará los no válidos. códigos y recursos no referenciados el objetivo de.

Su función es reemplazar los archivos de recursos no referenciados con un archivo de formato pequeño (todavía hay una huella, mientras se retiene la entrada del recurso, por lo que el volumen de resources.arsc no se reducirá) , al que se puede acceder a través de res/raw/ The keep El archivo .xml configura el modo de contracción y la lista blanca.

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 Restricciones de codificación

• Use los tipos de enumeración lo menos posible, porque la enumeración aumentará mucho el volumen después de compilarse en código de bytes, como se muestra en la Figura 12 (el código de bytes después de compilar 22 líneas de código es de 86 líneas)

Figura 12 Comparación de bytecodes compilados de tipos enumerados

• Eliminar la salida de REGISTRO innecesaria

3-2 Soluciones técnicas avanzadas

La descarga dinámica de la biblioteca SO y la tecnología de complementos son esencialmente una categoría de descarga dinámica. Las dos soluciones se pueden usar continuamente en el negocio durante mucho tiempo. En la Figura 13 se muestra cómo elegir en el proceso de uso específico:

Figura 13 Cómo las empresas eligen soluciones avanzadas

3-2-1 Carga dinámica de la biblioteca SO

Algunas empresas en la aplicación no son adecuadas para la transformación de plug-in. Después del desmantelamiento, se encuentra que la biblioteca SO representa una gran proporción. Por lo tanto, se puede considerar la descarga dinámica para la transformación para reducir el tamaño.

Dos formas de cargar la biblioteca SO

En la primera forma, podemos descargar directamente la biblioteca SO y colocarla en el directorio especificado.

La segunda forma es cargar la biblioteca SO en el directorio establecido por la variable de entorno, por lo que debemos agregar el directorio especificado a la variable de entorno para cargar la biblioteca SO normalmente.

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

1. Cómo configurar la ubicación de la variable de entorno de la biblioteca SO en la aplicación (refiriéndose a 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. Cómo eliminar la biblioteca SO especificada y todo el proceso de carga, como se muestra en la Figura 14:

Figura 14 Proceso de eliminación y carga de la biblioteca SO

Complemento 3-2-2

Qué es el complemento:

El complemento es para dividir un Apk en diferentes sub-Apks (es decir, diferentes complementos) de acuerdo con las funciones comerciales. Cada sub-Apk se puede compilar y empaquetar de forma independiente, y el Apk integrado finalmente se libera y se lanza. Cuando se usa Apk, cada complemento se carga dinámicamente y los complementos también se pueden reparar y actualizar en caliente.

•Anfitrión: la aplicación principal se puede usar para cargar complementos y también convertirse en un anfitrión

• Complemento: la aplicación de complemento, la aplicación cargada por el host, puede ser el mismo archivo Apk que la aplicación normal

Qué forma de negocio es adecuada para la transformación de plug-in:

• El negocio es relativamente independiente y está completamente desvinculado de la aplicación principal.

• Bajo costo de renovación e ingresos relativamente altos

• Ocupa un gran volumen

Después de una serie de evaluaciones, el negocio del video cumple con los puntos anteriores, y el efecto después de la transformación se muestra en la Figura 15:

 

Figura 15 El efecto de la transformación enchufable de la sala de negocios de video

3-3 Plan de optimización empresarial

Con más y más negocios, algunos UV de negocios antiguos son cada vez más bajos, por lo que se formuló un conjunto de procesos de optimización fuera de línea de negocios, como se muestra en la Figura 16:

Figura 16 Proceso de solución de optimización empresarial

4. Controlar

La implementación del plan de adelgazamiento es muy importante, y es más importante que el control de seguimiento no rebote.Mientras hacemos la gobernanza de adelgazamiento, estamos explorando un mecanismo de control normalizado por otro lado, y finalmente precipitamos un conjunto de normas de control y mecanismos de control. El propósito de la gestión y el control no es limitar la iteración comercial o el nuevo código, sino cómo realizar sus funciones en un código limitado y mejorar la conciencia de los ingenieros sobre la reducción en la codificación diaria.

4.1 Especificación de acceso SDK

Para evitar la expansión desordenada del SDK, se formuló la especificación de acceso SDK.Bajo la premisa de garantizar la función, el tamaño del SDK se controla estrictamente y el rebote del volumen de la aplicación se controla en la mayor medida.

4.2 Proceso de control

 

Figura 17 Proceso de control

De acuerdo con los cambios de los archivos de recursos, como agregar contenido, eliminar contenido, aumentar contenido, reducir contenido, archivos duplicados, administración de código, etc., combinado con especificaciones de control de gobierno, etc., el empaque y la construcción se compararán con la versión histórica. para obtener el contenido cambiado Para evaluar si hay espacio para la optimización, y dar el objetivo de optimización, y reconstruir la integración del paquete después de la optimización.

5. Logros y planificación de seguimiento

5.1 Logros

A través de las medidas anteriores, la versión de Android de JD Finance ha sido iterada por cinco versiones en dos trimestres, de 117M a los 74M actuales (Figura 18), y el total se ha mantenido dentro de un rango controlable. Al mismo tiempo, en las iteraciones de la próxima versión, normalizaremos la reducción de APK y siempre mantendremos el tamaño del paquete dentro de un rango controlable.

 

Figura 18 Resultados de adelgazamiento de APP financiera

5.2 Planificación posterior

Optimización continua de los medios técnicos:

La acumulación e iteración continuas del negocio siempre generará algunos recursos inútiles, por lo que estos archivos y códigos inútiles deben limpiarse periódicamente para reducir el paquete de instalación;

Haga un buen trabajo de seguimiento de cada versión, compare las diferencias entre las versiones y descubra que se puede optimizar utilizando medios técnicos sin afectar el negocio.

Construcción de plataforma de gestión y control en línea:

En la etapa inicial, se utilizaron la gestión y el control fuera de línea, cuya implementación llevó un poco de tiempo. En el futuro, mejoraremos la construcción de la plataforma de gestión y control en línea, la integraremos con toda la plataforma de lanzamiento y construcción de aplicaciones, formar un mecanismo de línea de montaje y hacer un buen trabajo en gestión y control.

Resumen : todavía queda un largo camino por recorrer para explorar la reducción de paquetes de instalación. Este artículo solo enumera algunas soluciones de reducción de uso común. Además de la optimización para proyectos grandes, también es necesario hacer un buen trabajo en la gobernanza entre proyectos. y continúe optimizando el tamaño de la aplicación para mejorar la experiencia del usuario.

【Referencia】

[1] Tamaño del paquete y tasa de conversión de instalación
https://medium.com/googleplaydev/shrinking-apks-growing-installs-5d3fcba23ce2

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

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

[4] Comparación de ProGuard y 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: Una herramienta para la mejora de la eficiencia energética en las fases de I+D y ensayo basada en tecnología descentralizada

{{o.nombre}}
{{m.nombre}}

Supongo que te gusta

Origin my.oschina.net/u/4090830/blog/8590988
Recomendado
Clasificación