El sistema de compilación de la plataforma de Android (Soong) ejecuta el compilador R8 en los destinos que compilan código de bytes DEX, incluidas las apps para Android (android_app), las pruebas (android_test) y las bibliotecas Java instalables con compile_dex: true (como services.jar), para reducir, optimizar y quitar el código y los recursos no utilizados. En las bibliotecas estáticas (android_library y java_library estático), Soong no ejecuta R8 directamente, sino que usa el bloque de propiedades optimize para adjuntar y propagar reglas de conservación del consumidor (export_proguard_flags_files: true) a los destinos de DEX descendentes que los vinculan de forma estática.
Para los ingenieros de plataformas que compilan imágenes del sistema o paquetes del proveedor, eliminar las reglas de conservación demasiado amplias ofrece beneficios directos para la salud del sistema:
- Menor espacio de la partición del sistema: R8 quita las clases, los métodos y los recursos inactivos antes de empaquetar los APKs y los archivos JAR en
/system,/system_ext,/producty/vendor. - Artefactos compilados más pequeños: Menos métodos DEX significan archivos
.odexy.vdexmás pequeños generados pordex2oatdurante el tiempo de compilación o la compilación integrado en el dispositivo. - Menor presión de memoria en el tiempo de ejecución: Debido a que Android asigna las tablas de recursos de
.odex,.vdexy APK a la memoria del proceso con llamadas demmappaginadas a demanda, los archivos binarios más pequeños reducen los errores de página graves durante el inicio de la app y disminuyen la memoria residente de código y recursos en todos los procesos. Para obtener más información sobre cómo el código de la app compilado afecta la memoria del dispositivo, consulta El código de la app es memoria. - Optimización más eficaz de todo el programa: Las restricciones de conservación estrechas permiten que R8 inserte métodos intercalados, desvirtualice interfaces y llamadas virtuales, elimine campos no utilizados y propague constantes entre clases.
Cómo configurar la optimización de R8 en Soong
En los archivos Android.bp, configura la optimización de R8 con el bloque de propiedades optimize en los destinos android_app y java_library instalables (o en los módulos android_library y java_library estáticos para exportar reglas de conservación del consumidor):
android_app {
name: "MySystemApp",
srcs: ["src/**/*.java"],
optimize: {
obfuscate: true,
shrink_resources: true,
},
}
En la siguiente tabla, se resumen las propiedades de optimize más comunes en Soong (definidas en build/soong/java/dex.go):
| Propiedad | Descripción |
|---|---|
enabled |
Controla si R8 se ejecuta en el destino. El valor predeterminado es true para todos los objetivos de android_app.
|
shrink |
Controla la eliminación de código no utilizado para quitar las clases, los campos y los métodos a los que no se puede acceder. El valor predeterminado es true para los destinos android_app (false para los módulos de prueba y java_library independientes, que pasan -dontshrink, a menos que se establezca de forma explícita).
|
optimize |
Controla las optimizaciones de código de bytes, como la inserción de métodos, la combinación de clases, la propagación de constantes y la eliminación de ramas inactivas. El valor predeterminado es true para los destinos android_app (controlado por la marca de compilación de lanzamiento RELEASE_R8_OPTIMIZE_BY_DEFAULT).
|
obfuscate |
Controla la minimización y el cambio de nombre del identificador. De forma predeterminada, se establece en false para la compatibilidad histórica, pero se debe establecer en true siempre que sea posible para reducir el tamaño del DEX y desbloquear optimizaciones más profundas (consulta Habilita la ofuscación siempre que sea posible).
|
shrink_resources |
Quita los recursos sin usar (entradas res/) del APK empaquetado después de la reducción de código. La configuración predeterminada es false.
|
optimized_shrink_resources |
Ejecuta la canalización integrada de reducción de código y recursos de R8 para que R8 realice un seguimiento de las referencias de código y recursos de forma conjunta en una sola pasada. Cuando se establece shrink_resources: true, el valor predeterminado es RELEASE_USE_OPTIMIZED_RESOURCE_SHRINKING_BY_DEFAULT (true en las compilaciones de la plataforma estándar).
|
proguard_flags_files |
Enumera los archivos .flags o .pro específicos del módulo que contienen reglas de conservación personalizadas. Evita agregar archivos personalizados cuando las anotaciones o los valores predeterminados estándar sean suficientes.
|
export_proguard_flags_files |
Propaga el proguard_flags_files de esta biblioteca a los módulos descendentes que dependen de ella de forma estática.
|
trace_references_from |
Enumera los destinos complementarios de la biblioteca Java cuyo código de bytes hace referencia a este destino que R8 rastrea y conserva automáticamente. |
Habilita la ofuscación siempre que sea posible
En Soong, obfuscate se establece de forma predeterminada en false para la compatibilidad histórica, pero debes establecer obfuscate: true de forma explícita en android_app y en los destinos DEX independientes siempre que sea posible:
- Menor tamaño del archivo DEX y menor huella de
.vdex: Cambiar el nombre de los paquetes, las clases, los campos y los métodos a identificadores cortos (a,b) reduce el grupo de cadenas de DEX (string_idsystring_data_item) y los descriptores de tipo. Dado que Android asigna archivos.vdexy.odexa la memoria del proceso, las tablas de símbolos más pequeñas reducen directamente el tamaño de la partición del sistema y el espacio en memoria del tiempo de ejecución. - Optimizaciones más profundas de todo el programa: Permitir que R8 cambie el nombre de los identificadores desbloquea la combinación de clases, el aplanamiento de paquetes y la deduplicación de clases sintéticas en paquetes que R8 debe omitir para evitar colisiones de nombres.
- Simbolización de seguimiento de pila completo: Habilitar la ofuscación en Soong no reduce la capacidad de depuración. Para cada destino compilado con R8, Soong emite un archivo de asignación
proguard_dictionaryen el directorio intermedio del módulo, agrupa todos los diccionarios de módulos en el artefactoproguard-dict.zipde la compilación, incorpora un hash de asignación (--map-id-template) en el encabezado DEX y reescribe los atributos de claseSourceFile(--source-file-template) para queretracepueda simbolizar automáticamente los seguimientos de pila.
Conserva obfuscate: false (o conserva explícitamente los nombres de las APIs públicas) solo para lo siguiente:
- Bibliotecas compartidas en la ruta de acceso de inicio o la ruta de acceso de clase
system_server: Los módulos (java_libraryojava_sdk_library) que exponen una superficie de API externa vinculada de forma dinámica por otros módulos en el tiempo de ejecución (como los destinosframework.jar,services.jaro<uses-library>) deben mantenerobfuscate: falseo conservar de forma explícita su superficie de API con las reglas@KeepForApio-keep(junto conprotect_api_surface: truepara los destinos de la ruta de acceso de inicio) para que los llamadores compilados en función de sus stubs puedan resolver los nombres de clase y miembro en el tiempo de ejecución.
Antes de habilitar obfuscate: true en una aplicación, verifica los siguientes requisitos previos para la migración:
- Dependencias externas del APK de prueba: Si un APK de
android_testexterno estableceinstrumentation_for: "MyApp"y, luego, invoca directamente clases o métodos internos deMyApp, cambiar el nombre de esos símbolos provocaNoSuchMethodErroroNoClassDefFoundErroren el tiempo de ejecución de la prueba. Es preferible estructurar la prueba como un APK autoinstrumentado que vinculeMyApp.implde forma estática, anotar los hooks de prueba con@VisibleForTestingo configurartrace_references_from(consulta Paso 2: Migra las reglas de conservación basadas en pruebas) para que los símbolos internos que necesitan las pruebas se conserven mientras se ofusca el resto de la app. - Reflexión basada en cadenas y JNI: Si un módulo busca clases, métodos o campos por nombres de cadenas literales (
Class.forName,getDeclaredMethodo JNIFindClassyGetMethodID) sin reglas de conservación ni anotaciones, la ofuscación cambia el nombre de esos destinos y detiene las búsquedas en el tiempo de ejecución. (Ten en cuenta que la reflexión sin anotar también falla enshrink: trueyoptimize: true). Anota esos puntos de entrada con la Guía para conservar anotaciones (@UsesReflection,@UsedByReflectiono@UsedByNative) para que R8 conserve sus nombres y ofusque el resto del módulo.
Sigue el principio de cero marcas personalizadas
El módulo ideal de la plataforma de Android no tiene archivos proguard.flags o keep.xml personalizados. En la compilación de la plataforma de Android, la gran mayoría de las reglas de conservación personalizadas son redundantes:
- Valores de referencia de la plataforma y AAPT2: La mayoría de los puntos de entrada ya se conservan automáticamente mediante los valores de referencia de la plataforma global (como
@Keep, los métodos JNInativey@VisibleForTesting) o AAPT2 los genera a partir de los recursos deAndroidManifest.xmly de diseño (consulta Cómo comprender las reglas de conservación predeterminadas de la plataforma). - Alternativas segmentadas: Cuando se deben conservar los puntos de entrada, se prefieren las anotaciones de sitios de declaración (
keepanno,@VisibleForTesting), las reglas de bibliotecas exportadas (export_proguard_flags_files: true) o la vinculación de pruebas estáticas en lugar de los archivos.flagsseparados (consulta Audita y migra las reglas de conservación existentes).
Antes de agregar o conservar un archivo proguard.flags o keep.xml personalizado, verifica si la regla ya se controla con los valores de referencia predeterminados o si se puede migrar a las anotaciones de código.
Comprende las reglas predeterminadas de conservación de la plataforma
Soong pasa automáticamente las siguientes reglas de referencia globales a cada invocación de R8 en los destinos de compilación de DEX (android_app, android_test y módulos java_library instalables), configurados en build/soong/java/dex.go:
build/make/core/proguard.flags: Conserva las clases y los miembros anotados con@com.android.internal.annotations.VisibleForTestingen todos los paquetes y con@VisibleForTesting(androidx.annotation.VisibleForTestingocom.google.common.annotations.VisibleForTesting) dentro de los paquetesandroid.**,com.android.**ycom.google.android.**. También conserva@TestApi,@Keep(androidx.annotation,android.support.annotation,com.android.internal.annotations),@KeepForWeakReference,@WeaklyReferencedCallbacky@dalvik.annotation.optimization.**.build/make/core/proguard_basic_keeps.flags: Conserva los atributosSourceFilepara los seguimientos de pila, las anotaciones de visibilidad del tiempo de ejecución (RuntimeVisible*Annotations), los atributosExceptionsyAnnotationDefault, los métodosnative, los miembrosSerializable, los métodos@JavascriptInterface, los constructoresThrowable(String), los camposParcelable$CREATORy los camposMessageLitede Protobuf.build/make/core/proguard/kotlin.flags: Silencia las advertencias inofensivas para las metaanotaciones específicas de Kotlin (kotlin.Metadataykotlin.annotation.{AnnotationRetention,AnnotationTarget,Retention,Target}) y quita las anotacionesDebugMetadatade Kotlin en las compilaciones de lanzamiento.build/make/core/proguard/checknotnull.flags: Reemplaza las llamadas auxiliares comunes de verificación de nulos (com.google.common.base.Preconditions.checkNotNullydagger.internal.Preconditions.checkNotNull*) por verificaciones de nulos concisas de código de bytes. Este archivo omite intencionalmenteObjects.requireNonNullpara conservar mensajes de excepción explícitos en los límites de la API del framework.build/make/core/proguard/enumvalues.flags: Conserva los métodosvaluesyvalueOfen los tiposenum, a menos que se inhabiliten en la configuración de compilación.- Reglas generadas automáticamente por AAPT2: AAPT2 inspecciona los archivos
AndroidManifest.xml, XML de diseño y XML de preferencias combinados para generar reglas de conservación exactas para cada métodoActivity,Service,BroadcastReceiver,ContentProvider,BackupAgent,Application, subclaseViewpersonalizada, subclasePreferenceyandroid:onClickde XML registrados.
Audita y migra las reglas de conservación existentes
Cuando audites archivos proguard.flags o keep.xml existentes en un repositorio de la plataforma, evalúa cada regla en orden según la siguiente jerarquía de cuatro pasos:
Paso 1: Borra las reglas redundantes o obsoletas
Borra las reglas que ya están cubiertas por los valores de referencia globales, las reglas de diseño y manifiesto de AAPT2, o los valores predeterminados de R8, así como las reglas que hacen referencia a clases o paquetes que ya no existen.
Si todas las reglas de un archivo .flags son redundantes, borra el archivo y quita proguard_flags_files de Android.bp. Si el bloque optimize restante solo repite la configuración predeterminada, quita el bloque redundante y aplica el formato al archivo de compilación con bpfmt -w Android.bp. Cuando limpies un paquete, verifica si los módulos complementarios en subdirectorios (como las variantes de destino de Kotlin) hacen referencia al mismo archivo de marcas y actualízalos juntos.
Paso 2: Migra las reglas de conservación basadas en pruebas
Quita las reglas de conservación basadas en pruebas de los archivos .flags de producción personalizados con uno de los siguientes patrones:
Vincula la biblioteca de implementación en las pruebas (
static_libs): Cuando unandroid_testejercita detalles de implementación internos o privados del paquete de una app, la arquitectura de plataforma recomendada es colocar los archivos fuente de la app en unandroid_library(MyApp.impl) y vincular estáticamenteMyApp.implenMyAppyMyAppTests:android_library { name: "MyApp.impl", srcs: ["src/**/*.java"], manifest: "AndroidManifest.xml", } android_app { name: "MyApp", static_libs: ["MyApp.impl"], optimize: { obfuscate: true, shrink_resources: true, }, } android_test { name: "MyAppTests", srcs: ["tests/**/*.java"], static_libs: ["MyApp.impl"], }Este patrón de 3 módulos permite que
MyAppTestsse ejecute como una prueba auto-instrumentada con acceso completo a las clases internas, permite queMyApphabiliteobfuscate: truelibremente sin interrumpir las pruebas, evita el envío de puntos de entrada solo para pruebas en el APK de producción y elimina la recompilación deMyAppcuando cambia el código de prueba.Anota los hooks de prueba con
@VisibleForTesting: Si un APK de prueba externo llama a una pequeña cantidad de métodos o constructores internos en un destinoandroid_appy la reestructuración en una biblioteca.implno es práctica, anota esas declaraciones con@VisibleForTesting. El modelo de referencia globalbuild/make/core/proguard.flagsconserva automáticamente los elementos@VisibleForTestingde los paquetesandroid.**,com.android.**ycom.google.android.**sin archivos.flagspersonalizados. (Para los módulos del proveedor fuera de estos espacios de nombres, usa@UsedByReflectiono reglas de biblioteca exportadas).Usa
trace_references_fromen los límites de la biblioteca de la plataforma: Cuando las pruebas no pueden vincular de forma estática la implementación de destino (por ejemplo, las pruebas que ejercen servicios del servidor del sistema o archivos JAR de la plataforma, comoframework-connectivity), configuratrace_references_fromen el módulo de destino que apunta a un módulo complementario de la biblioteca de Java que contiene las fuentes de prueba. R8 rastrea y conserva todas las clases y los miembros a los que se hace referencia en el bytecode complementario.
Paso 3: Exporta reglas de la biblioteca propietaria
Si un java_library o android_library compartido requiere reglas de conservación para su propia reflexión interna o devoluciones de llamada de JNI, define las reglas en el destino de la biblioteca y establece export_proguard_flags_files: true:
java_library {
name: "my-shared-library",
srcs: ["src/**/*.java"],
optimize: {
proguard_flags_files: ["proguard.flags"],
export_proguard_flags_files: true,
},
}
Todos los destinos de android_app y de bibliotecas que se vinculan de forma estática con my-shared-library heredan esas reglas automáticamente, por lo que las apps de nivel inferior no necesitan duplicarlas. Cuando varios módulos en diferentes directorios deben compartir un conjunto de reglas que no está vinculado a una sola biblioteca de código, publica el archivo .flags a través de un filegroup explícito en Android.bp para que los módulos puedan hacer referencia a :my-shared-flags de forma clara en los límites del paquete. Para obtener orientación general sobre cómo crear reglas de conservación del consumidor de bibliotecas sin restringir la optimización de apps descendentes, consulta Optimización para autores de bibliotecas.
Paso 4: Anota las declaraciones en el código fuente
Cuando se accede a una clase, un método, un constructor o un campo a través de la reflexión o JNI, y no están cubiertos por AAPT2 ni por las referencias globales, reemplaza las reglas -keep separadas en los archivos .flags por anotaciones de origen de la Guía de anotaciones de Keep (consulta la referencia de Javadoc de keepanno):
@UsesReflection: Prefiere esta anotación cuando controlas el código que realiza la reflexión. Colócalo en el sitio de la llamada de reflexión para declarar a qué clases, métodos o campos de destino se accede de forma dinámica. Dado que@UsesReflectioncodifica automáticamente una condición previa de que el sitio de llamada anotado en sí es accesible, R8 poda tanto el llamador como el destino reflexivo si el sitio de llamada no se usa.@UsedByReflection: Se coloca en clases, métodos, campos o constructores que se instancian o invocan de forma reflexiva mediante código o bibliotecas externos, como las clases cargadas conClass.forNamedesde clavesBundleoSettings, o las clases de complementos cargadas en cargadores de clases dinámicos. Especificakind(comoKeepItemKind.CLASS_AND_METHODS),preconditions(@KeepCondition) y restricciones de parámetros para que R8 conserve solo el contrato de reflexión exacto y pueda seguir optimizando o quitando los miembros no utilizados de la clase. No agregues@UsedByReflectiona los componentes registrados en el manifiesto, comoJobServiceoBroadcastReceiver, ya que AAPT2 ya los conserva.@UsedByNative: Se coloca en los métodos o campos a los que se accede desde el código JNI de C o C++ conGetMethodID,GetStaticMethodIDoGetFieldID.@KeepForApi: Se coloca en las clases o los miembros de la API de la biblioteca que deben permanecer intactos cuando se reduce una biblioteca antes de la distribución.@Keep(androidx.annotation.Keep): Se usa como alternativa cuandokeepannono es aplicable. Si aplicas@Keepa una clase, se conservará la clase y todos sus miembros de forma incondicional. Aplicar@Keepa un método o campo actúa como un punto de entrada incondicional que conserva el miembro y su clase contenedora, incluso si la clase nunca se instancia, mientras quekeepannoexpresa la accesibilidad condicional.
Para usar las anotaciones keepanno de R8 en un módulo de Soong, haz lo siguiente:
Agrega
"keepanno-annotations"alibsenAndroid.bp:libs: [ "keepanno-annotations", ],Importa
com.android.tools.r8.keepanno.annotations.*y anota el sitio de declaración o llamada en el código fuente de Java o Kotlin:import com.android.tools.r8.keepanno.annotations.KeepItemKind; import com.android.tools.r8.keepanno.annotations.UsedByReflection; public final class CustomPluginController { @UsedByReflection( description = "Instantiated via Class.forName from plugin config", kind = KeepItemKind.CLASS_AND_METHODS) public CustomPluginController(Context context) { // ... } }R8 traduce las anotaciones
keepannodirectamente a su modelo interno de reglas de conservación y quita las anotaciones del resultado final de DEX, lo que no agrega ninguna sobrecarga de código de bytes en el tiempo de ejecución.
Evita errores comunes
En las siguientes secciones, se describen los errores frecuentes de proguard.flags y keep.xml en el código de la plataforma, y cómo corregirlos.
Reglas de conservación de componentes generales
# Don't do this:
-keep class * extends android.app.Activity
-keep class * extends android.app.Service
-keep class * extends android.content.BroadcastReceiver
- Por qué afecta el rendimiento: Esta regla es completamente redundante con AAPT2.
Como usa un comodín sin verificar si el componente está registrado en el
AndroidManifest.xmlfinal combinado, obliga a R8 a conservar cada subclaseActivity,ServiceoBroadcastReceiverque se encuentre en cualquier lugar de la ruta de clase, incluidos los componentes de biblioteca no utilizados y las actividades de depuración inhabilitadas. - Solución recomendada: Borra la regla. AAPT2 inspecciona el manifiesto combinado y genera reglas
-keepexactas para los componentes registrados.
Reglas de conservación del controlador de clics de XML amplias
# Don't do this:
-keepclassmembers class * {
public void *(android.view.View);
}
- Por qué afecta el rendimiento: Conservar cada método
public void *(View)en cada clase del módulo impide que R8 quite o inserte cualquier método que acepte un solo parámetroView. - Solución recomendada: Borra la regla. AAPT2 analiza los archivos XML de diseño y genera reglas de conservación específicas para los métodos a los que se hace referencia en los atributos
android:onClick.
Reglas de conservación de los métodos getter y setter de Broad View
# Don't do this:
-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*(...);
public *** get*();
}
- Por qué afecta el rendimiento: Esta regla obliga a R8 a conservar todos los métodos get y set en cada subclase
Viewde la app y en todas las bibliotecas vinculadas (incluidas las bibliotecas de AndroidX y Material), lo que impide la eliminación de métodos no utilizados y la inserción en línea en el código de la IU. - Solución recomendada: Borra la regla. AAPT2 ya conserva los constructores para las clases
Viewpersonalizadas infladas desde archivos XML de diseño. Si el código anima una propiedad de vista con nombres de cadenas reflectivas, comoObjectAnimator.ofFloat(view, "translationZ", ...), reemplaza el nombre de la cadena por una referencia de propiedad escrita (View.TRANSLATION_Zo una implementación personalizada deFloatPropertyoIntProperty) para evitar la reflexión por completo. Si no se puede evitar el acceso a propiedades reflectivas, anota el getter o setter específico con@UsedByReflection.
Se inhabilitan la ofuscación o la optimización general
# Don't do this in proguard.flags:
-dontoptimize
-dontshrink
-dontobfuscate
- Por qué afecta el rendimiento: Colocar
-dontoptimizeo-dontshrinkdentro de un archivo.flagsanula de forma silenciosa la configuración deAndroid.bpdel módulo y deshabilita los pases de optimización en todo el destino. Peor aún, si una biblioteca exporta un archivo.flagsque contiene-dontoptimize, se inhabilita la optimización de R8 para cadaandroid_appde nivel inferior que vincula la biblioteca. - Corrección recomendada: Quita
-dontoptimize,-dontshrinky-dontobfuscatede los archivos.flags. Controla el comportamiento de la optimización de forma explícita enAndroid.bpcon el bloqueoptimize(shrink,optimize,obfuscate) en el objetivo hoja.
Marcas de diagnóstico en reglas confirmadas
# Don't do this in proguard.flags:
-verbose
-printmapping proguard.map
-printusage usage.txt
-printconfiguration config.txt
- Por qué afecta la compilación: Las marcas de diagnóstico inundan los registros de compilación o intentan escribir archivos de salida en rutas de acceso locales durante las compilaciones de Soong en zona de pruebas.
- Corrección recomendada: Borra estas marcas de los archivos
.flagsconfirmados. Soong escribe automáticamente las asignaciones de R8 y los resultados de uso, comoproguard_dictionaryyproguard_usage.zip, en el directorio intermedio del módulo enout/soong/.intermediates/.
Reglas de conservación de producción para el código de prueba
- Por qué afecta el rendimiento: Agregar reglas
-keeppersonalizadas solo para el acceso de prueba obliga a que ese código permanezca sin ofuscar y se conserve en las compilaciones de producción. Si bien anotar métodos con@VisibleForTestingo configurartrace_references_fromevita mantener reglas-keepmanuales, esos símbolos se siguen enviando en el archivo binario de producción. - Solución recomendada: Para mantener los puntos de entrada solo para pruebas completamente fuera del APK de producción, estructura la aplicación en una biblioteca
.imply vincúlala a un APK de prueba de instrumentación autónoma constatic_libs, como se describe en el paso 2: Migra las reglas de conservación basadas en pruebas.
Comodines de recursos generales en keep.xml
<!-- Don't do this in res/raw/keep.xml: -->
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@raw/*,@drawable/*,@string/*" />
- Por qué afecta el rendimiento: Los comodines generales conservan todos los recursos del tipo coincidente en el módulo y sus dependencias, lo que anula la reducción de recursos (
shrink_resources: true) y aumenta el APK y la tabla deresources.arscasignada. - Solución recomendada:
- Prefiere las referencias a recursos estáticos en lugar de
keep.xml: Evita usar el métodoResources.getIdentifierpara buscar de forma dinámica un conjunto limitado de recursos, como cadenas de experimentos numeradas o elementos de diseño temáticos. En su lugar, usa una instrucciónswitchen tiempo de compilación o asigna constantes estáticasR.id,R.stringoR.drawable. Las referencias estáticas permiten que R8 y AAPT2 rastreen los recursos activos exactos, eliminen la sobrecarga de la búsqueda de cadenas en el tiempo de ejecución y quiten la necesidad dekeep.xmlpor completo. - Enumera nombres de recursos específicos: Si se requiere una búsqueda dinámica, como la de un lector de licencias de terceros, enumera los identificadores de recursos exactos en
tools:keep(por ejemplo,tools:keep="@raw/third_party_licenses"). - Borra los archivos
keep.xmlredundantes: Si ya se hace referencia a los recursos enumerados de forma estática en el código (R.raw.foo) o en XML (@raw/foo), el reductor los conserva automáticamente, por lo que puedes borrarres/raw/keep.xml.
- Prefiere las referencias a recursos estáticos en lugar de
Verifica y audita los cambios en las reglas
Cada vez que quites o restrinjas las reglas de conservación en proguard.flags o keep.xml, verifica que el módulo se compile correctamente, supere las pruebas de unidades y de instrumentación, y conserve todos los puntos de entrada necesarios.
Compila y prueba módulos
Compila el módulo de forma limpia para verificar que R8 y AAPT2 se completen sin advertencias de referencias faltantes:
m <MODULE_NAME>Ejecuta pruebas de unidades y de instrumentación para el módulo con
atest:atest <TEST_MODULE_NAME>En el caso de las apps del sistema, los servicios privilegiados o los módulos dependientes del hardware, ejecuta pruebas de IU y de instrumentación en dispositivos físicos de destino o en tu laboratorio de pruebas de dispositivos para ejercitar las rutas de reflexión del tiempo de ejecución, las vinculaciones de IPC y la expansión de recursos.
Inspecciona las diferencias entre los recursos y los archivos DEX
Compara el APK o JAR compilado antes y después de los cambios en las reglas para confirmar que R8 quita el código y los recursos no utilizados sin quitar los puntos de entrada esperados:
Usa
apkanalyzerodexdumppara inspeccionar las clases, los métodos y los campos conservados en los archivos APK o DEX de salida:apkanalyzer dex packages $OUT/system/priv-app/<APP_NAME>/<APP_NAME>.apkCuando modifiques
keep.xmlo habilitesshrink_resources, usaaapt2 dump resourcespara verificar que la compilación quite los recursos no utilizados y conserve los necesarios:aapt2 dump resources $OUT/system/priv-app/<APP_NAME>/<APP_NAME>.apk
Analiza el radio de conservación y la subsunción de reglas con R8
El compilador de código abierto R8 incluye un analizador de radio de conservación (también expuesto en Android Studio y Gradle como el analizador de configuración de R8) que mide el impacto exacto de cada regla de conservación durante la compilación. Soong integra este analizador directamente en la compilación de la plataforma de Android (configurada en build/soong/java/dex.go).
Para analizar las reglas de conservación de un módulo de la plataforma o de toda una compilación, haz lo siguiente:
Ejecuta la compilación con la variable de entorno
R8_DUMP_KEEP_RADIUS=true:R8_DUMP_KEEP_RADIUS=true m <MODULE_NAME>Cuando se establece
R8_DUMP_KEEP_RADIUS=true, Soong le indica a R8 que registre las métricas de la regla de conservación en un archivor8keepradius.pbintermedio para cada módulo compilado enout/soong/.intermediates/. Para cada regla de conservación y anotaciónkeepanno, R8 registra lo siguiente:- Radio de conservación inmediato: Las clases, los campos y los métodos exactos que conserva la regla, junto con las restricciones específicas que aplica contra la reducción, la optimización o la ofuscación.
- Subsunción de reglas: Qué otras reglas de conservación o anotaciones ya conservan los mismos elementos. Si una regla personalizada está completamente subsumida por una regla de AAPT2 o una regla de referencia global, puedes borrarla sin problemas.
- Reglas globales y para todo el paquete: Son reglas que usan comodines amplios para todo el paquete o aplican directivas de configuración globales.
Convierte el resultado de
r8keepradius.pben un informe HTML interactivo conKeepRadiusHtmlReportGenerator(incluido enprebuilts/r8/r8.jar):# Generate an HTML report for a single module INTERMEDIATES=out/soong/.intermediates/packages/apps/<APP_NAME>/<APP_NAME> java -cp prebuilts/r8/r8.jar \ com.android.tools.r8.keepradius.KeepRadiusHtmlReportGenerator \ $INTERMEDIATES/android_common/r8keepradius.pb \ keep_radius_report.html # Scan all built modules under out/soong/.intermediates and generate # per-module HTML reports plus an aggregate keepradius.html summary java -cp prebuilts/r8/r8.jar \ com.android.tools.r8.keepradius.KeepRadiusHtmlReportGenerator \ out/soong/.intermediates \ out/keep_radius_reportsCuando se le proporciona un directorio,
KeepRadiusHtmlReportGeneratorrecorre todos los archivos*keepradius*.pb, genera un informe HTML para cada módulo y creaout/keep_radius_reports/keepradius.htmlque resume los recuentos de elementos activos, los recuentos de elementos conservados y las reglas de conservación con el radio más alto en toda la compilación. Si quieres obtener más información para interpretar las puntuaciones de reducción, optimización y ofuscación en el informe generado, consulta Cómo usar el Analizador de configuración de R8.
Soong también admite dos variables de entorno de diagnóstico de R8 adicionales en build/soong/java/dex.go:
R8_DUMP_INPUT=true: Escriber8inputs.zipen el directorio intermedio del módulo que contiene todos los archivos JAR de entrada, los archivos JAR de biblioteca y las configuraciones de ProGuard combinadas para la reproducción independiente de R8.R8_DUMP_PERFETTO_TRACE=true: Escriber8trace.ptraceen el directorio intermedio del módulo para inspeccionar los pases de compilación de R8 en Perfetto.
Valida las reglas de consumidor de la biblioteca
Cuando las exportaciones de java_library o android_library conservan reglas para los consumidores posteriores (export_proguard_flags_files: true), esas reglas no deben incluir marcas globales que alteren o inhabiliten la optimización para la app consumidora.
R8 proporciona un analizador de reglas de conservación de código abierto (ProcessKeepRules), que se expone en la compilación de la plataforma de Android como la herramienta de host process-keep-rules (definida en prebuilts/r8/Android.bp):
# Build the R8 keep rules validator host binary
m process-keep-rules
# Validate one or more ProGuard configuration files
out/host/linux-x86/bin/process-keep-rules <PROGUARD_FLAGS_PATH>
La herramienta process-keep-rules analiza cada archivo de configuración y falla con diagnósticos de archivo y línea si encuentra directivas que no están permitidas en las reglas de consumidor de la biblioteca. Las directivas no permitidas incluyen la optimización global, la reducción o la inhabilitación de la ofuscación (como -dontoptimize o -dontshrink), el reempaquetado de paquetes y las marcas de modificación de acceso, las marcas de diagnóstico o de asignación, y -keepattributes a nivel de la app.
Audita árboles de origen con pgaudit.py
Para auditar los directorios de origen sin compilar junto con los analizadores de tiempo de compilación de R8, el árbol de la plataforma de Android incluye la secuencia de comandos pgaudit.py en build/make/core/proguard/tools/. Esta herramienta de análisis estático analiza los archivos Android.bp, proguard.flags y keep.xml en un repositorio para marcar las reglas que ya están cubiertas por AAPT2 o los valores de referencia de la plataforma global, los comodines amplios, las anulaciones de -dontoptimize y las reglas de conservación solo para pruebas:
# Audit a single package directory
./build/make/core/proguard/tools/pgaudit.py packages/apps/Provision/
# Scan a subsystem tree and write a structured JSON report
./build/make/core/proguard/tools/pgaudit.py packages/ --json=audit_report.json
Los equipos de la plataforma y del OEM pueden combinar pgaudit.py para una rápida clasificación del árbol de origen, process-keep-rules para validar las reglas de la biblioteca exportada y R8_DUMP_KEEP_RADIUS=true con KeepRadiusHtmlReportGenerator para medir el radio de retención exacto de la clase, el campo y el método de cada regla en una compilación.