Optimiza el código y los recursos de la plataforma con R8

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, /product y /vendor.
  • Artefactos compilados más pequeños: Menos métodos DEX significan archivos .odex y .vdex más pequeños generados por dex2oat durante 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, .vdex y APK a la memoria del proceso con llamadas de mmap paginadas 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_ids y string_data_item) y los descriptores de tipo. Dado que Android asigna archivos .vdex y .odex a 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_dictionary en el directorio intermedio del módulo, agrupa todos los diccionarios de módulos en el artefacto proguard-dict.zip de la compilación, incorpora un hash de asignación (--map-id-template) en el encabezado DEX y reescribe los atributos de clase SourceFile (--source-file-template) para que retrace pueda 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_library o java_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 destinos framework.jar, services.jar o <uses-library>) deben mantener obfuscate: false o conservar de forma explícita su superficie de API con las reglas @KeepForApi o -keep (junto con protect_api_surface: true para 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_test externo establece instrumentation_for: "MyApp" y, luego, invoca directamente clases o métodos internos de MyApp, cambiar el nombre de esos símbolos provoca NoSuchMethodError o NoClassDefFoundError en el tiempo de ejecución de la prueba. Es preferible estructurar la prueba como un APK autoinstrumentado que vincule MyApp.impl de forma estática, anotar los hooks de prueba con @VisibleForTesting o configurar trace_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, getDeclaredMethod o JNI FindClass y GetMethodID) 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 en shrink: true y optimize: true). Anota esos puntos de entrada con la Guía para conservar anotaciones (@UsesReflection, @UsedByReflection o @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 JNI native y @VisibleForTesting) o AAPT2 los genera a partir de los recursos de AndroidManifest.xml y 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 .flags separados (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.VisibleForTesting en todos los paquetes y con @VisibleForTesting (androidx.annotation.VisibleForTesting o com.google.common.annotations.VisibleForTesting) dentro de los paquetes android.**, com.android.** y com.google.android.**. También conserva @TestApi, @Keep (androidx.annotation, android.support.annotation, com.android.internal.annotations), @KeepForWeakReference, @WeaklyReferencedCallback y @dalvik.annotation.optimization.**.
  • build/make/core/proguard_basic_keeps.flags: Conserva los atributos SourceFile para los seguimientos de pila, las anotaciones de visibilidad del tiempo de ejecución (RuntimeVisible*Annotations), los atributos Exceptions y AnnotationDefault, los métodos native, los miembros Serializable, los métodos @JavascriptInterface, los constructores Throwable(String), los campos Parcelable$CREATOR y los campos MessageLite de Protobuf.
  • build/make/core/proguard/kotlin.flags: Silencia las advertencias inofensivas para las metaanotaciones específicas de Kotlin (kotlin.Metadata y kotlin.annotation.{AnnotationRetention,AnnotationTarget,Retention,Target}) y quita las anotaciones DebugMetadata de 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.checkNotNull y dagger.internal.Preconditions.checkNotNull*) por verificaciones de nulos concisas de código de bytes. Este archivo omite intencionalmente Objects.requireNonNull para 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étodos values y valueOf en los tipos enum, 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étodo Activity, Service, BroadcastReceiver, ContentProvider, BackupAgent, Application, subclase View personalizada, subclase Preference y android:onClick de 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 un android_test ejercita 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 un android_library (MyApp.impl) y vincular estáticamente MyApp.impl en MyApp y MyAppTests:

    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 MyAppTests se ejecute como una prueba auto-instrumentada con acceso completo a las clases internas, permite que MyApp habilite obfuscate: true libremente 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 de MyApp cuando 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 destino android_app y la reestructuración en una biblioteca .impl no es práctica, anota esas declaraciones con @VisibleForTesting. El modelo de referencia global build/make/core/proguard.flags conserva automáticamente los elementos@VisibleForTesting de los paquetes android.**, com.android.** y com.google.android.** sin archivos .flags personalizados. (Para los módulos del proveedor fuera de estos espacios de nombres, usa @UsedByReflection o reglas de biblioteca exportadas).

  • Usa trace_references_from en 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, como framework-connectivity), configura trace_references_from en 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 @UsesReflection codifica 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 con Class.forName desde claves Bundle o Settings, o las clases de complementos cargadas en cargadores de clases dinámicos. Especifica kind (como KeepItemKind.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 @UsedByReflection a los componentes registrados en el manifiesto, como JobService o BroadcastReceiver, 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++ con GetMethodID, GetStaticMethodID o GetFieldID.
  • @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 cuando keepanno no es aplicable. Si aplicas @Keep a una clase, se conservará la clase y todos sus miembros de forma incondicional. Aplicar @Keep a 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 que keepanno expresa la accesibilidad condicional.

Para usar las anotaciones keepanno de R8 en un módulo de Soong, haz lo siguiente:

  1. Agrega "keepanno-annotations" a libs en Android.bp:

    libs: [
        "keepanno-annotations",
    ],
    
  2. 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 keepanno directamente 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.xml final combinado, obliga a R8 a conservar cada subclase Activity, Service o BroadcastReceiver que 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 -keep exactas 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ámetro View.
  • 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 View de 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 View personalizadas infladas desde archivos XML de diseño. Si el código anima una propiedad de vista con nombres de cadenas reflectivas, como ObjectAnimator.ofFloat(view, "translationZ", ...), reemplaza el nombre de la cadena por una referencia de propiedad escrita (View.TRANSLATION_Z o una implementación personalizada de FloatProperty o IntProperty) 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 -dontoptimize o -dontshrink dentro de un archivo .flags anula de forma silenciosa la configuración de Android.bp del módulo y deshabilita los pases de optimización en todo el destino. Peor aún, si una biblioteca exporta un archivo .flags que contiene -dontoptimize, se inhabilita la optimización de R8 para cada android_app de nivel inferior que vincula la biblioteca.
  • Corrección recomendada: Quita -dontoptimize, -dontshrink y -dontobfuscate de los archivos .flags. Controla el comportamiento de la optimización de forma explícita en Android.bp con el bloque optimize (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 .flags confirmados. Soong escribe automáticamente las asignaciones de R8 y los resultados de uso, como proguard_dictionary y proguard_usage.zip, en el directorio intermedio del módulo en out/soong/.intermediates/.

Reglas de conservación de producción para el código de prueba

  • Por qué afecta el rendimiento: Agregar reglas -keep personalizadas 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 @VisibleForTesting o configurar trace_references_from evita mantener reglas -keep manuales, 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 .impl y vincúlala a un APK de prueba de instrumentación autónoma con static_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 de resources.arsc asignada.
  • Solución recomendada:
    • Prefiere las referencias a recursos estáticos en lugar de keep.xml: Evita usar el método Resources.getIdentifier para 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ón switch en tiempo de compilación o asigna constantes estáticas R.id, R.string o R.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 de keep.xml por 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.xml redundantes: 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 borrar res/raw/keep.xml.

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

  1. Compila el módulo de forma limpia para verificar que R8 y AAPT2 se completen sin advertencias de referencias faltantes:

    m <MODULE_NAME>
    
  2. Ejecuta pruebas de unidades y de instrumentación para el módulo con atest:

    atest <TEST_MODULE_NAME>
    
  3. 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 apkanalyzer o dexdump para 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>.apk
    
  • Cuando modifiques keep.xml o habilites shrink_resources, usa aapt2 dump resources para 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:

  1. 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 archivo r8keepradius.pb intermedio para cada módulo compilado en out/soong/.intermediates/. Para cada regla de conservación y anotación keepanno, 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.
  2. Convierte el resultado de r8keepradius.pb en un informe HTML interactivo con KeepRadiusHtmlReportGenerator (incluido en prebuilts/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_reports
    

    Cuando se le proporciona un directorio, KeepRadiusHtmlReportGenerator recorre todos los archivos *keepradius*.pb, genera un informe HTML para cada módulo y crea out/keep_radius_reports/keepradius.html que 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: Escribe r8inputs.zip en 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: Escribe r8trace.ptrace en 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.