El sistema de compilación de la plataforma de Android (Soong) ejecuta el compilador R8 en destinos que compilan código de bytes DEX, incluidas las apps para Android (android_app), las pruebas (android_test) y las bibliotecas de Java instalables con compile_dex: true (como services.jar), para reducir, optimizar y quitar el código y los recursos que no se usan. En las bibliotecas estáticas (android_library y java_library estática), 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 DEX descendentes que las vinculan de forma estática.
Para los ingenieros de plataformas que compilan imágenes del sistema o paquetes de proveedores, eliminar las reglas de conservación demasiado amplias ofrece beneficios directos para el estado 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 APK y los 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 durante el tiempo de ejecución: Debido a que Android asigna
.odex,.vdexy tablas de recursos de APK a la memoria del proceso con llamadasmmapde paginación por demanda, los objetos binarios más pequeños reducen los errores de página importantes durante el inicio de la app y disminuyen el código residente y la memoria de recursos en 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 más estrictas permiten que R8 incorpore métodos, desvirtualice interfaces y llamadas virtuales, elimine campos sin usar y propague constantes en todas las 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. La configuración predeterminada es true
para todos los destinos de android_app.
|
shrink |
Controla la eliminación de código muerto para quitar clases, campos y métodos inaccesibles. 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 establezcan 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 (controlados por
la marca de compilación de lanzamiento RELEASE_R8_OPTIMIZE_BY_DEFAULT).
|
obfuscate |
Controla la reducción y el cambio de nombre de los identificadores. El valor predeterminado es false para la compatibilidad histórica, pero configúralo como true siempre que sea posible para reducir el tamaño de 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 del reductor de código y recursos de R8 para que este realice un seguimiento de las referencias de código y recursos de forma conjunta en un solo paso. Cuando se configura shrink_resources: true, el valor predeterminado es RELEASE_USE_OPTIMIZED_RESOURCE_SHRINKING_BY_DEFAULT (true en las compilaciones de 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 estándar o los valores predeterminados 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 dependientes de la compilación de DEX directa (conectados a través de instrumentation_for o libs) cuyas referencias de código de bytes en este destino R8 rastrea y retiene automáticamente (también se propagan en las variantes override_android_app y override_android_test, y se admiten para bibliotecas de Java complementarias no dependientes cuando se vinculan con una división .impl).
|
allow_traced_references |
Permite que los destinos dependientes de la compilación DEX directa (conectados a través de
instrumentation_for o libs, como
pruebas de proveedores o entre repositorios que no se pueden incluir en
trace_references_from) habiliten el seguimiento de sus referencias de código de bytes
en este destino configurando
trace_references: true.
|
trace_references |
Cuando se establece en true en un dependiente de compilación de DEX directo de un destino con allow_traced_references: true, ejecuta R8 tracereferences en este módulo y pasa las reglas de conservación generadas al módulo de destino.
|
protect_api_surface |
Pasa --protect-api-surface a R8. Cuando se establece en un
java_sdk_library, también genera y
adjunta automáticamente reglas de conservación de la API de Metalava en todos los permisos de la API habilitados (público,
sistema, module-lib, system-server y prueba) a la biblioteca de
implementación.
|
Las bibliotecas estáticas (android_library, java_library y las importaciones de AAR) también pueden establecer required_downstream_optimization: { enabled: true, optimize: true } en Android.bp para requerir cualquier destino de compilación de DEX descendente que las vincule de forma estática (directa o transitivamente) para habilitar la reducción de R8 y la optimización de código de bytes. Soong habilita esto automáticamente en androidx.compose.runtime_runtime para que los destinos que vinculan Jetpack Compose se optimicen con R8.
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 de forma explícita obfuscate: true en android_app y los destinos DEX independientes siempre que sea posible:
- Menor espacio de DEX y
.vdex: Cambiar el nombre de los paquetes, las clases, los campos y los métodos por identificadores cortos (a,b) reduce el grupo de cadenas de DEX (string_idsystring_data_item) y los descriptores de tipo. Debido a 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 entorno 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 anulación de duplicación de clases sintéticas en los paquetes que, de lo contrario, R8 debe omitir para evitar colisiones de nombres.
- Simbolización de seguimiento de pila completa: 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 la claseSourceFile(--source-file-template) para queretracepueda simbolizar los seguimientos de pila automáticamente.
Mantén obfuscate: false (o conserva de forma explícita los nombres de las APIs públicas) solo para lo siguiente:
- Bibliotecas compartidas en bootclasspath o classpath de
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 entorno 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 reglasprotect_api_surface: true,@KeepForApio-keeppara que los emisores compilados con sus stubs puedan resolver los nombres de clases y miembros en el entorno de ejecución. En los módulosjava_sdk_library, la configuraciónprotect_api_surface: truegenera y adjunta automáticamente reglas de conservación de ProGuard de Metalava en todos los permisos de API habilitados (público, sistema, module-lib, system-server y prueba) a la biblioteca de implementación. Cuando se habilita la optimización generalizada de R8 en destinos de bootclasspath no actualizables (framework-minus-apex,extyframework-location), también incluyenframework-hiddenapi-proguard-rules(generado porhiddenapi_to_proguard) para retener los miembros que se indican en los archivosboot/hiddenapi/*.txtactivos (se omitehiddenapi-max-target-o.txt).
Antes de habilitar obfuscate: true en una aplicación, verifica los siguientes requisitos previos de migración:
- Dependencias externas del APK de prueba: Si un APK de
android_testexterno estableceinstrumentation_for: "MyApp"y, además, invoca directamente clases o métodos internos deMyApp, cambiar el nombre de esos símbolos provoca un error deNoSuchMethodErroroNoClassDefFoundErroren el tiempo de ejecución de la prueba. Configuratrace_references_from(oallow_traced_referencescontrace_references: true), estructura la prueba como un APK de instrumentación automática que vinculeMyApp.implde forma estática o anota hooks de prueba con@VisibleForTesting(consulta el Paso 2: Migra las reglas de conservación basadas en pruebas) para que se conserven los símbolos internos que necesitan las pruebas 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 rompe las búsquedas de tiempo de ejecución. (Ten en cuenta que la reflexión sin anotaciones 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 ni 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:
- Líneas de base de la plataforma y AAPT2: La mayoría de los puntos de entrada ya se conservan automáticamente con las líneas de base de la plataforma global (como
@Keep, métodos JNInativey@VisibleForTesting) o se generan con AAPT2 a partir deAndroidManifest.xmly recursos de diseño (consulta Información sobre las reglas de conservación predeterminadas de la plataforma). - Alternativas orientadas: Cuando se deban conservar los puntos de entrada, prefiere las anotaciones del sitio de declaración (
keepanno,@VisibleForTesting), las reglas de biblioteca exportadas (export_proguard_flags_files: true) o la vinculación de pruebas estáticas por sobre los archivos.flagsseparados (consulta Cómo auditar y migrar 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 las líneas de base predeterminadas o si se puede migrar a
anotaciones de código.
Comprende las reglas de conservación predeterminadas 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 entorno de ejecución (RuntimeVisible*Annotations), los atributosExceptionsyAnnotationDefault, los métodosnative, los miembrosSerializable, los métodos@JavascriptInterface, los constructoresThrowable(String), los camposParcelable$CREATORy los campos protobufMessageLite.build/make/core/proguard/kotlin.flags: Silencia las advertencias inofensivas para meta-anotaciones específicas de Kotlin (kotlin.Metadataykotlin.annotation.{AnnotationRetention,AnnotationTarget,Retention,Target}) y quita las anotaciones de KotlinDebugMetadataen las compilaciones de lanzamiento.build/make/core/proguard/checknotnull.flags: Reemplaza las llamadas comunes de ayuda para la verificación de valores nulos (com.google.common.base.Preconditions.checkNotNullydagger.internal.Preconditions.checkNotNull*) por verificaciones de valores nulos de código de bytes concisas. Este archivo omiteObjects.requireNonNullde forma intencional para conservar los 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 la configuración de compilación los inhabilite.- 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 cadaActivity,Service,BroadcastReceiver,ContentProvider,BackupAgent,Application, subclaseViewpersonalizada, subclasePreferencey métodoandroid:onClickXML 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 inactivas
Borra las reglas que ya están cubiertas por las líneas de base globales, las reglas de manifiesto y diseño 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
reitera la configuración predeterminada, quita el bloque redundante y formatea el archivo de compilación
con bpfmt -w Android.bp. Cuando limpies un paquete, verifica si los módulos complementarios de los 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
Mueve las reglas de conservación basadas en pruebas fuera de los archivos .flags de producción personalizados con uno de los siguientes patrones:
Seguimiento de referencias de destinos de prueba o complementarios (
trace_references_fromoallow_traced_references): Cuando el destino y un módulo dependiente directo compilan su propio DEX (android_app,android_testo unjava_librarydexed) y están conectados a través deinstrumentation_forolibs, Soong puede ejecutar R8tracereferencesen el módulo dependiente y alimentar las reglas de conservación generadas en la compilación de R8 del destino sin crear un ciclo de dependencia ni requerir una división manual de la biblioteca.impl:Pruebas en el mismo repositorio (
trace_references_from): Enumera los dependientes directos deinstrumentation_forolibsen el módulo de destino (esto también se propaga automáticamente en las variantes deoverride_android_appyoverride_android_test, y sigue admitiendo bibliotecas de Java complementarias no dependientes cuando se vinculan con una división de.impl):android_app { name: "MyApp", srcs: ["src/**/*.java"], optimize: { obfuscate: true, shrink_resources: true, trace_references_from: ["MyAppTests"], }, } android_test { name: "MyAppTests", instrumentation_for: "MyApp", srcs: ["tests/**/*.java"], }Pruebas de varios repositorios o de proveedores (
allow_traced_referencesytrace_references): Cuando el módulo de destino no puede enumerar el dependiente directamente entrace_references_from(como las pruebas en otro repositorio o árbol de proveedores), configuraallow_traced_references: trueen el módulo de destino y habilita cada dependiente contrace_references: true:android_app { name: "MyApp", srcs: ["src/**/*.java"], optimize: { allow_traced_references: true, }, } android_test { name: "MyAppVendorTests", instrumentation_for: "MyApp", srcs: ["tests/**/*.java"], optimize: { trace_references: true, }, }Reflexión basada en cadenas en las pruebas:
tracereferencesinspecciona las referencias del grupo de constantes de código de bytes y no puede detectar la reflexión basada en cadenas en las pruebas (Class.forName,getDeclaredField,getDeclaredMethodo asistentes de inyección de campo reflexivos). Anota los campos y métodos de producción a los que se accede de forma reflexiva con@VisibleForTesting(o@UsedByReflection).
Vincula la biblioteca de implementación a las pruebas (
static_libs): Si quieres que no se retengan puntos de entrada exclusivos para pruebas en el APK de producción y no quieres que los cambios en las pruebas vuelvan a activar R8 en el destino de producción, coloca los archivos de origen de la app en unandroid_library(MyApp.impl) y vincula de forma estáticaMyApp.implaMyAppyMyAppTests: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 de autoinstrumentación con acceso completo a las clases internas, mientras que permite queMyAppse reduzca y se ofusque solo en los puntos de entrada de producción.Anota hooks de prueba con
@VisibleForTesting: Si un APK de prueba externo llama solo a unos pocos métodos o constructores internos en un destinoandroid_app, puedes anotar esas declaraciones con@VisibleForTesting. El modelo de referencia global debuild/make/core/proguard.flagsconserva automáticamente los elementos de@VisibleForTestingen los paquetesandroid.**,com.android.**ycom.google.android.**sin archivos.flagspersonalizados. (Para los módulos de proveedores fuera de estos espacios de nombres, usatrace_references_from,@UsedByReflectiono reglas de biblioteca exportadas). Cuando anotes manualmente con@VisibleForTestingen lugar de usartrace_references_fromotrace_references, también anota las clases que se pasaron a Mockitomock(...)ospy(...)(no solo los métodos con stub), ya quemockito-target(dexmaker-mockito) integrado en el dispositivo genera subclases de tiempo de ejecución y R8 puede marcar clases sin subclasesfinal(MockitoException: Can't mock/spy class ... - final class).Evita o vuelve a empaquetar las bibliotecas estáticas superpuestas (
jarjar_rules): Cuando unandroid_appy su APK de pruebainstrumentation_forvinculan de forma estática la misma biblioteca (directamente, comoguava, o de forma transitiva),tracereferencesve esas clases definidas en la prueba y no emite reglas de conservación para la app, y, en el tiempo de ejecución, el cargador de clases superior de la app oculta la copia de la prueba con sus clases reducidas de R8, lo que causaIllegalAccessErroroNoSuchMethodError. Si la prueba no necesita su propia copia empaquetada, quita el duplicado de lastatic_libsde la prueba (o vincúlalo a través delibs) para evitar la colisión; de lo contrario, vuelve a empaquetar los paquetes de biblioteca estática superpuestos de la app con un prefijo específico del módulo mediantejarjar_rules.
Paso 3: Exporta las 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 biblioteca y android_app downstream que vinculan de forma estática my-shared-library heredan esas reglas automáticamente, por lo que las apps downstream no necesitan duplicarlas. Cuando varios módulos de 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. Si deseas obtener orientación general sobre cómo crear reglas de conservación para consumidores de bibliotecas sin restringir la optimización de apps downstream, 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 la JNI, y AAPT2 o las líneas de base globales no los abarcan, reemplaza las reglas -keep separadas en los archivos .flags por anotaciones de origen de la Guía para conservar anotaciones (consulta la referencia de Javadoc de keepanno):
@UsesReflection: Se prefiere esta anotación cuando controlas el código que realiza la reflexión. Colócala en el sitio de llamada de reflexión para declarar a qué clases, métodos o campos de destino se accede de forma dinámica. Debido a que@UsesReflectioncodifica automáticamente una condición previa de que el sitio de llamada anotado es accesible, R8 elimina tanto el emisor como el destino reflexivo si no se usa el sitio de llamada.@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 clases cargadas conClass.forNamede clavesBundleoSettings, o 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 reflexivo exacto y pueda optimizar o quitar miembros de la clase que no se usen. No agregues@UsedByReflectiona los componentes registrados en el manifiesto, comoJobServiceoBroadcastReceiver, ya que AAPT2 ya los conserva.@UsedByNative: Se coloca en métodos o campos a los que se accede desde el código JNI de C o C++ conGetMethodID,GetStaticMethodIDoGetFieldID.@KeepForApi: Se coloca en clases o 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 resguardo cuando no se aplicakeepanno. Aplicar@Keepa una clase conserva 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 la clase que lo contiene, incluso si nunca se crea una instancia de la clase, mientras quekeepannoexpresa la accesibilidad condicional.
Para usar anotaciones de R8 keepanno 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 la declaración o el sitio de 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 en su modelo de regla de conservación interno y quita las anotaciones del resultado final de DEX, lo que agrega cero sobrecarga de código de bytes en el entorno de ejecución.
Evita errores comunes
En las siguientes secciones, se describen las dificultades frecuentes de proguard.flags y keep.xml en el código de la plataforma y cómo corregirlas.
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.xmlcombinado final, obliga a R8 a retener cada subclaseActivity,ServiceoBroadcastReceiverque se encuentre en cualquier parte de la ruta de clase, incluidos los componentes de biblioteca sin usar y las actividades de depuración inhabilitadas. - Corrección recomendada: Borra la regla. AAPT2 inspecciona el manifiesto combinado y genera reglas de
-keepexactas para los componentes registrados.
Reglas de conservación de controladores de clics de XML generales
# 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 intercale cualquier método que acepte un solo parámetroView. - Corrección recomendada: Borra la regla. AAPT2 analiza los archivos en formato XML de diseño y genera reglas de conservación orientadas para los métodos a los que hacen referencia los atributos
android:onClick.
Reglas de conservación de métodos get y set de View generales
# 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 retener cada método get y set en cada subclase
Viewde la app y todas las bibliotecas vinculadas (incluidas las bibliotecas de AndroidX y Material), lo que bloquea la eliminación de métodos inactivos y la inserción en el código de la IU. - Corrección recomendada: Borra la regla. AAPT2 ya conserva los constructores para las clases
Viewpersonalizadas que se aumentan a partir de archivos en formato XML de diseño. Si el código anima una propiedad de vista con nombres de cadena reflexivos, comoObjectAnimator.ofFloat(view, "translationZ", ...), reemplaza el nombre de la cadena por una referencia de propiedad con tipo (View.TRANSLATION_Zo una implementación personalizada deFloatPropertyoIntProperty) para evitar la reflexión por completo. Si no se puede evitar el acceso reflexivo a las propiedades, anota el método get o set específico con@UsedByReflection.
Inhabilitación de la optimización o la ofuscación generales
# 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, además, inhabilita los pases de optimización en todo el destino. Peor aún, si una biblioteca exporta un archivo.flagsque contiene-dontoptimize, inhabilita la optimización de R8 para cadaandroid_appdescendente que vincule la biblioteca. - Corrección recomendada: Quita
-dontoptimize,-dontshrinky-dontobfuscatede los archivos.flags. Controla el comportamiento de optimización de forma explícita enAndroid.bpcon el bloqueoptimize(shrink,optimize,obfuscate) en el destino de 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é perjudica 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 la 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 de
-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 configurartrace_references_from(oallow_traced_referencescontrace_references: true) o anotar métodos con@VisibleForTestingevita mantener reglas de-keepmanuales y conserva solo lo que las pruebas referencian, esos símbolos se siguen enviando en el objeto binario de producción. - Corrección recomendada: Elimina las reglas de
-keepde prueba personalizadas habilitandotrace_references_from(oallow_traced_referencesytrace_references: true), o bien estructura la app en una biblioteca de.implvinculada a un APK de prueba de instrumentación autónoma constatic_libspara mantener los puntos de entrada solo de prueba completamente fuera del APK de producción, 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 retienen 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 tamaño del APK y la tablaresources.arscasignada. - Corrección recomendada:
- Prefiere las referencias de recursos estáticos antes que
keep.xml: Evita usar el métodoResources.getIdentifierpara buscar un conjunto limitado de recursos de forma dinámica, como cadenas de experimentos numeradas o elementos de diseño temáticos. En su lugar, usa una sentenciaswitchde tiempo de compilación o asigna constantes estáticas deR.id,R.stringoR.drawable. Las referencias estáticas permiten que R8 y AAPT2 rastreen los recursos activos exactos, eliminen la sobrecarga de búsqueda de cadenas en el entorno 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
por 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 de la lista 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 de recursos estáticos antes que
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, apruebe las pruebas de unidades y de instrumentación, y conserve todos los puntos de entrada requeridos.
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 con privilegios o los módulos que dependen del hardware, ejecuta pruebas de instrumentación y de IU en los dispositivos físicos de destino o en el lab de pruebas de tu dispositivo para ejercitar las rutas de reflexión del entorno de ejecución, las vinculaciones de IPC y la inflación de recursos.
Inspecciona las diferencias de DEX y recursos
Compara el APK o JAR compilado antes y después de los cambios en las reglas para confirmar que R8 quite el código y los recursos inactivos sin quitar los puntos de entrada esperados:
Usa
apkanalyzerodexdumppara inspeccionar las clases, los métodos y los campos retenidos 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 sin usar y conserve los necesarios:aapt2 dump resources $OUT/system/priv-app/<APP_NAME>/<APP_NAME>.apkCuando ejecutes
prebuilts/r8/r8.jardirectamente en elclasses.jarprevio a R8 de un módulo para verificar los cambios de marcas sin conexión sin una compilación de plataforma completa, pasa los JAR de ruta de clase--libdel módulo (android.jar,framework.jarylibscompartido) y las reglas deproguard.optionsgeneradas por AAPT2 desdeAndroidManifest.xmly los XML de diseño. Sin esas entradas, R8 no puede resolver los supertipos del framework (Activity,Service,Phone,Handler) y retiene de forma insuficiente los métodos de jerarquía.
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 Keep Radius (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 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 las reglas 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: Son 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 o anotaciones de conservación ya retienen los mismos elementos. Si una regla personalizada está completamente incluida en una regla de AAPT2 o una regla de referencia global, puedes borrarla de forma segura.
- Reglas globales y para todo el paquete: Son reglas que usan comodines amplios para todo el paquete o que 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 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 de mayor radio en toda la compilación. Para obtener más información sobre cómo 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 variables de entorno adicionales de diagnóstico de R8 y de bucle interno en build/soong/java/dex.go:
R8_DUMP_INPUT=true: Escriber8inputs.zipen el directorio intermedio del módulo que contiene todos los JAR de entrada, los 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.SOONG_SKIP_TRACE_REFERENCES=true: Omite la ejecución de las reglastracereferencesconfiguradas portrace_references_fromotrace_referencespara acelerar las compilaciones de bucle interno locales cuando se itera en el código de prueba.
Valida las reglas de consumidor de la biblioteca
Cuando un java_library o android_library exporta reglas de conservación a consumidores downstream (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 archivos
y líneas si encuentra directivas que no están permitidas en las reglas del consumidor de la
biblioteca. Entre las directivas no permitidas, se incluyen la inhabilitación de la optimización global, la reducción o 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 asignación, y -keepattributes a nivel de la app.
Audita árboles de fuentes con pgaudit.py
Para auditar los directorios de código fuente no compilados 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 plataformas y OEM pueden combinar pgaudit.py para una clasificación rápida del árbol de fuentes, process-keep-rules para validar las reglas de bibliotecas exportadas y R8_DUMP_KEEP_RADIUS=true con KeepRadiusHtmlReportGenerator para medir el radio de retención exacto de clases, campos y métodos de cada regla en una compilación.