Cómo configurar de forma segura las reglas de ofuscación ProGuard para el SDK de Android

opoinstall
2026-11-11
5 min read

¿Cómo excluir los SDKs móviles de la ofuscación de ProGuard en Android? Excluir un SDK de Android de la ofuscación requiere verificar si la librería incluye reglas ProGuard para el consumidor, añadir directivas -keep y -keepclassmembers de alcance preciso para puntos de entrada accesibles mediante reflexión, interfaces de callback, puentes JNI, y modelos de datos, además de auditar los símbolos preservados a través de informes de diagnóstico de R8 e inspección del código DEX crudo. Cuando los equipos de ingeniería de Android habilitan la reducción y optimización de código para las compilaciones de producción, el compilador R8 elimina el bytecode no referenciado y renombra clases, campos y métodos con identificadores cortos. Si un SDK de atribución móvil depende de reflexión dinámica o callbacks JNI que no están protegidos contra la eliminación agresiva de código (tree-shaking) y el renombrado, el deep linking diferido, la deserialización de modelos JSON y las compilaciones de paquetes de canal pueden fallar silenciosamente en producción, incluso después de haber superado todas las pruebas en una compilación de depuración (debug) sin minificar. OpoInstall, una plataforma de atribución móvil y deep linking, proporciona archivos de SDK para Android y flujos de trabajo de paquetes de canal nativos que los equipos de ingeniería pueden configurar y verificar dentro de los pipelines de compilación en release minificados por R8.

En las cadenas de herramientas modernas del Android Gradle Plugin (AGP), los archivos de reglas de preservación (proguard-rules.pro en el DSL heredado o src/<variant>/keepRules/*.keep en AGP 9.3+) sirven como contrato de configuración para el compilador R8 de Google. Excluir de forma segura un SDK de Android de terceros de la ofuscación significa preservar solo los puntos de entrada de la API pública específica, constructores reflectivos, interfaces de callback, llamadas JNI y campos de modelo requeridos en tiempo de ejecución, evitando reglas demasiado amplias a nivel de paquete o la supresión de advertencias no verificadas.

Término Definición Entidad Relacionada Rol en la intención de búsqueda
SDK de Android Librería compilada en Java o Kotlin integrada en una aplicación Android para proporcionar capacidades especializadas como la atribución de instalaciones. Bytecode classes.dex Navegacional / Transaccional
ProGuard / R8 Herramientas de compilación de Android que reducen el código no utilizado, optimizan el bytecode y ofuscan identificadores usando sintaxis de reglas ProGuard. proguard-rules.pro Técnico / Informativo
Reglas ProGuard de Consumidor Reglas de ofuscación integradas dentro de un .aar (/proguard.txt) o .jar (/META-INF/proguard/) que se fusionan automáticamente en R8. consumerProguardFiles Técnico / Resolución de problemas
Informes de Diagnóstico de R8 Archivos de salida (mapping.txt, seeds.txt, usage.txt, configuration.txt) que detallan el renombrado de símbolos, las reglas de preservación aplicadas y el código eliminado. Auditoría de compilación release Técnico / Resolución de problemas

¿Por qué la ofuscación de R8 y ProGuard interrumpe los callbacks de atribución del SDK de Android?

La optimización R8 de Android preserva símbolos esenciales del SDK durante la compilación en release.

La trampa entre Debug y Release: por qué la atribución funciona en pruebas pero falla en producción

Una de las regresiones de ingeniería de lanzamiento más comunes en la captación de usuarios móviles ocurre cuando el deep linking diferido (handleDeepLink) y la restauración de parámetros de instalación (getInstallParam) de una aplicación funcionan correctamente durante las pruebas de QA en compilaciones de depuración, pero devuelven payloads vacíos o dejan de activar callbacks una vez que la aplicación se compila como un APK firmado en versión release o un Android App Bundle (.aab).

La causa raíz reside en la disparidad entre tipos de compilación configurada en Gradle. Por defecto, el tipo de compilación debug deshabilita la reducción de código para acelerar las compilaciones incrementales. Cuando los ingenieros compilan la variante release con la optimización R8 habilitada, el Android Gradle Plugin invoca al Compilador R8 de Android para ejecutar tres transformaciones de bytecode tanto en la aplicación anfitriona como en las librerías de terceros vinculadas:

  • Reducción de código (Tree Shaking): R8 analiza el grafo de llamadas de la aplicación comenzando desde los puntos de entrada declarados (como los componentes en AndroidManifest.xml y las directivas explícitas -keep) y elimina cualquier clase, constructor, método o campo que parezca inalcanzable mediante análisis estático.
  • Optimización de Bytecode y Modificación de Acceso: R8 integra métodos, elimina parámetros no utilizados, fusiona clases y, cuando se combina con la directiva -allowaccessmodification en la configuración de optimización por defecto de Android, modifica las banderas de visibilidad de clases y miembros (por ejemplo, convirtiendo miembros private o package-private en public durante la integración) para reducir el tamaño del archivo Dalvik Executable (.dex) y mejorar la velocidad de ejecución.
  • Ofuscación de Identificadores (Renombrado y Reempaquetado): R8 renombra las clases, paquetes, campos y métodos restantes en identificadores compactos (como a.a.b()) y puede reempaquetar clases ofuscadas en jerarquías de paquetes aplanadas, reduciendo la sobrecarga de los pools de cadenas dentro de classes.dex.

El Modo Completo (Full Mode) de R8 ha estado habilitado por defecto desde el Android Gradle Plugin (AGP) 8.0. En comparación con el modo de compatibilidad ProGuard heredado, el modo completo de R8 aplica optimizaciones sustancialmente más estrictas: elimina constructores sin argumentos por defecto (<init>()), altera los modificadores de acceso de los miembros y elimina atributos de metadatos a menos que las clases y miembros asociados sean retenidos explícitamente mediante una regla de preservación coincidente. Para obtener contexto fundamental sobre las estructuras binarias .aar y los estándares de adquisición de SDK, consulte nuestras guías complementarias, Entendiendo el significado de los SDK móviles y los protocolos de crecimiento técnico y Cómo elegir un kit de desarrollo de software para una atribución precisa.

Seis modos de fallo distintos en SDKs de atribución ofuscados

Los modelos de callbacks por reflexión y JNI en SDKs de Android requieren reglas de preservación R8 específicas.

Debido a que el análisis estático del grafo de llamadas no puede predecir las búsquedas dinámicas en tiempo de ejecución, un SDK de Android sin protección puede experimentar seis modos de fallo distintos bajo el Modo Completo de R8. Los ingenieros deben distinguir estos modos de fallo cuidadosamente, ya que cada uno requiere un objetivo de protección ProGuard específico:

Modo de Fallo en Tiempo de Ejecución Causa del Bytecode R8 Objetivo de Protección Requerido
1. Fallo de búsqueda de clase por reflexión (ClassNotFoundException) R8 renombra, reempaqueta o elimina una clase cargada dinámicamente por nombre de cadena mediante Class.forName() Preservar la clase objetivo y su identificador original usando -keep class ...
2. Fallo de instanciación de objeto por reflexión (NoSuchMethodException: <init>) En modo completo, incluso si una clase se preserva, su constructor sin argumentos por defecto (<init>()) se elimina si no se llama directamente en el código Preservar explícitamente la firma del constructor (<init>();) dentro del bloque -keep
3. Fallos en Callback Dinámico y Modificación de Acceso R8 elimina o renombra los métodos de interfaz de callback, o altera las banderas de visibilidad de miembros esperadas por llamadas reflectivas getDeclaredMethod() / getDeclaredField() Preservar los métodos de la interfaz de callback (-keep interface ... { <methods>; }) y cualquier miembro accedido por reflexión
4. Pérdida de Metadatos de Firma Genérica en Listeners Los genéricos en Java sufren borrado de tipo en tiempo de compilación, mientras que los atributos Signature del bytecode preservan metadatos para reflexión (ResultCallBack<OpoData>). R8 Full Mode elimina estos metadatos si no se retienen explícitamente Retener -keepattributes Signature además de mantener tanto la interfaz de callback genérica como la clase de modelo objetivo
5. Fallo de Deserialización de Modelo de Datos JSON R8 elimina la clase del modelo (si solo se instancia por reflexión) o renombra sus campos a a, b, c, causando que los parsers JSON basados en reflexión devuelvan campos nulos o vacíos Usar -keep class ... si el modelo se instancia dinámicamente, o -keepclassmembers si la clase ya se retiene mediante referencias directas
6. Eliminación de JNI Downcall y Upcall Nativo-a-Java (UnsatisfiedLinkError / NoSuchMethodError) Las reglas por defecto de Android preservan los nombres en los native downcalls, pero no evitan que métodos RegisterNatives no usados sean eliminados, y no pueden ver el código C/C++ .so que llama de vuelta a Java mediante JNI (FindClass, GetMethodID) Usar explícitamente -keep para declaraciones native registradas dinámicamente y cualquier clase, constructor o método Java invocado vía upcalls JNI
[Compilación Android Release: Modo Completo R8 Habilitado]
                      |
                      v
     [R8 evalúa reglas de preservación propias y del host]
                      |
      +---------------+---------------+
      |                               |
      v (Reglas faltantes o no coincidentes)  v (Reglas de preservación activas + Atributos)
[Símbolos del SDK eliminados o renombrados] [Puntos de entrada y modelos del SDK preservados]
- () reflectivo eliminado           - Confirmado en -printseeds (seeds.txt)
- JNI Upcall / Campos de modelo perdidos   - Verificado en DEX crudo (apkanalyzer dex packages)
- Metadatos de Firma Genérica perdidos    - La atribución en release coincide con debug

Reglas de Consumidor en Bundles AAR vs. Importaciones JAR manuales

Cómo los archivos AAR y JAR incluyen reglas ProGuard de consumidor

Los archivos Android AAR y JAR contribuyen con reglas de consumidor a la configuración de R8.

Antes de añadir reglas de exclusión manual a su aplicación, inspeccione si el SDK de Android de terceros ya incluye reglas R8 integradas. Como se documenta en la Guía de desarrolladores de Android sobre optimización de librerías, AGP y R8 soportan el descubrimiento automático de reglas de consumidor tanto en distribuciones .aar como .jar, incluyendo reglas R8 segmentadas por versión:

Formato de Distribución del SDK Ubicación de Regla de Consumidor Integrada Comportamiento de Android Gradle Plugin / R8
Android Archive (.aar) /proguard.txt en la raíz del AAR (vía consumerProguardFiles), o reglas segmentadas por versión dentro de /classes.jar bajo /META-INF/com.android.tools/r8-from-<X>-upto-<Y>/ Extraído y fusionado automáticamente en la configuración R8 de la aplicación en tiempo de compilación
Java Archive (.jar) con reglas integradas /META-INF/proguard/*.pro o /META-INF/com.android.tools/r8-from-<X>-upto-<Y>/ (incluyendo variantes r8, r8-from-<X>, y r8-upto-<Y>) Descubierto y aplicado automáticamente por AGP y R8 según la versión activa del compilador
.jar o .aar autónomo sin reglas integradas Ninguna presente dentro del archivo No se proporciona protección integrada; añada reglas de preservación a nivel de host solo cuando la reflexión, JNI, serialización u otros símbolos accedidos dinámicamente requieran preservación explícita

(Nota sobre reglas de serialización Gson: Como se documenta en la Guía de solución de problemas de Gson para ProGuard y R8, Gson 2.11.0 y versiones posteriores incluyen reglas R8 para TypeToken y campos anotados con @SerializedName. Estas reglas no eliminan la necesidad de restringir el acceso a modelos reflectivos, proporcionar constructores sin argumentos compatibles o probar todos los caminos de serialización y deserialización en compilaciones minificadas.)

Inspeccionando el archivo SDK descargado antes de escribir reglas

La coincidencia de reglas de ProGuard y R8 es estrictamente sensible a mayúsculas y exacta en la ruta del paquete. En diferentes versiones del SDK o distribuciones regionales, los espacios de nombres de los paquetes pueden variar; si copia una regla de un tutorial antiguo cuyo paquete no coincide con los archivos .class reales en su archivo, R8 no encontrará ninguna coincidencia.

Antes de editar sus reglas de preservación, descargue su paquete SDK objetivo (como el SDK de Android v2.1.0 desde el centro de descargas de SDKs móviles) e inspeccione sus reglas integradas (tanto en /proguard.txt como dentro de classes.jar) junto con su jerarquía exacta de .class:

# 1. Inspeccionar un archivo .aar para reglas de consumidor raíz (proguard.txt) y extraer classes.jar para verificar reglas META-INF
unzip -l opoinstall-android-sdk.aar
unzip -p opoinstall-android-sdk.aar proguard.txt
unzip -p opoinstall-android-sdk.aar classes.jar > temp_classes.jar
jar tf temp_classes.jar | grep -E "META-INF/proguard/|META-INF/com\.android\.tools/|\.class$"

# 2. Si usa un archivo .jar autónomo, inspeccione las reglas META-INF integradas y las rutas .class exactas
jar tf libs/OpoInstall_SDK.jar | grep -E "META-INF/proguard/|META-INF/com\.android\.tools/|\.class$"

Configurando proguard-rules.pro para SDKs de atribución de Android

Evitando antipatrones de preservación a nivel de paquete y supresión general de advertencias

Al escribir reglas personalizadas, los ingenieros deben seguir las mejores prácticas de Google sobre Elegir qué conservar (reglas de preservación R8) para evitar deshabilitar la optimización innecesariamente:

  • Prefiera reglas precisas sobre los antipatrones -keep class package.** { *; }: Una regla general a nivel de paquete obliga a R8 a conservar y omitir la optimización en cada clase auxiliar interna en ese paquete. Limite sus reglas a las clases específicas, constructores (<init>();), interfaces de callback, objetivos JNI y campos de modelo accedidos dinámicamente en tiempo de ejecución (o utilice la anotación @UnconditionallyKeep de Jetpack en el código que usted controla).
  • Distinga -keep de -keepclassmembers en modelos de datos: -keepclassmembers solo preserva campos o métodos especificados si la clase contenedora se retiene en el grafo de llamadas. Si un modelo de datos (como OpoData) se instancia únicamente vía reflexión o es referenciado solo a través de tipos genéricos borrados, use -keep class ... para que R8 no elimine la clase misma.
  • Entienda las limitaciones de -keepclasseswithmembernames para JNI: Las reglas de optimización por defecto de Android incluyen -keepclasseswithmembernames,includedescriptorclasses class * { native <methods>; }. Como se define en la documentación de R8 de Google y el Manual de ProGuard sobre directivas de preservación, -keepclasseswithmembernames es equivalente a -keepclasseswithmembers,allowshrinking. Preserva los nombres de clases y miembros coincidentes que sobreviven a la fase de reducción, pero no evita que clases o métodos native no referenciados sean eliminados durante la reducción. Si los métodos nativos se registran dinámicamente mediante RegisterNatives sin sitios de llamada directos en Java, o si el código C/C++ invoca métodos Java vía upcalls JNI, debe usar reglas -keep o -keepclassmembers explícitas según cómo se instancia la clase objetivo.
  • Evite directivas blanket -dontwarn no verificadas: -dontwarn proporciona cero protección contra la reducción o el renombrado y suprime advertencias de referencias no resueltas que pueden ocultar fallos NoClassDefFoundError en tiempo de ejecución. Solo añada un -dontwarn de alcance limitado si su registro de compilación informa de una clase específica faltante (solo de compilación) que usted ha verificado que es opcional en tiempo de ejecución.
Directiva ProGuard / R8 Sintaxis de forma larga equivalente Reducción (Eliminación) Ofuscación (Renombrado) Alcance de ingeniería recomendado
-keep class ... -keep Prevenida (clase y miembros especificados conservados) Prevenida (nombres originales conservados) Puntos de entrada públicos, interfaces de callback y clases instanciadas dinámicamente (incluir <init>(...);)
-keepclassmembers class ... -keepclassmembers Miembros conservados solo si la clase es retenida Prevenida para miembros coincidentes Preservar <fields>; o métodos de upcall JNI en clases instanciadas directamente en Java/Kotlin
-keepclasseswithmembernames -keepclasseswithmembers,allowshrinking Permitida si no se referencia (allowshrinking) Prevenida si la clase/miembros sobreviven a la reducción Preservación de nombres de downcall JNI (native <methods>;) cuando el código Java invoca el método native
-keepnames class ... -keep,allowshrinking Permitida si no se referencia (allowshrinking) Prevenida si la clase es retenida Mantener nombres de clase legibles para seguimientos de pila (stack traces) sin forzar clases no usadas en classes.dex
-keepattributes ... -keepattributes Preserva metadatos en elementos retenidos N/A (Atributo de bytecode) Retener Signature (genéricos), InnerClasses, EnclosingMethod y *Annotation* en clases explícitamente preservadas
-dontwarn <clase_específica> -dontwarn Sin efecto en la reducción Sin efecto en el renombrado Suprimir advertencias en tiempo de compilación solo para una dependencia opcional verificada

Configurando Gradle en cadenas de herramientas AGP 8.x y AGP 9.3+

El Android Gradle Plugin 9.3 introdujo un bloque DSL de optimization simplificado y directorios keepRules específicos por variante (archivos .keep), mientras que AGP 8.x hasta 9.2 (y configuraciones heredadas aún soportadas en AGP 9.3+) utilizan isMinifyEnabled y proguardFiles. Los ingenieros deben mantener tres objetos de configuración distintos:

Objeto de Configuración Ubicación del Archivo o DSL Rol Arquitectónico en Compilaciones R8
Reglas de Preservación DSL Heredadas (AGP 8.x–9.2 y Heredado 9.3+) app/proguard-rules.pro (referenciado vía proguardFiles(...)) Declara reglas de preservación del host junto con getDefaultProguardFile("proguard-android-optimize.txt")
Reglas de Preservación DSL de Optimización (AGP 9.3+) app/src/main/keepRules/*.keep o app/src/<variant>/keepRules/*.keep Declara archivos .keep al usar optimization { enable = true }
Directivas de Informe de Diagnóstico R8 -printseeds, -printusage, -printconfiguration en un archivo de configuración R8 verificado (proguard-rules.pro) Instruye a R8 a emitir informes de auditoría en app/build/outputs/mapping/<buildVariant>/
// Opción A: AGP 8.x - 9.2 DSL Heredado (también soportado en AGP 9.3+) — app/build.gradle.kts
android {
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            isShrinkResources = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
}

// Opción B: AGP 9.3+ DSL de Optimización Simplificado — app/build.gradle.kts
// Coloque las reglas de preservación de clases/miembros en app/src/main/keepRules/sdk-attribution.keep
android {
    buildTypes {
        release {
            optimization {
                enable = true
            }
        }
    }
}

Reglas de preservación ilustrativas y directivas de diagnóstico

Debido a que la documentación pública de OpoInstall y las páginas de FAQ históricas hacen referencia a diferentes prefijos de paquete (com.opoinstall.api.** vs. com.fm.OpoInstall.** o io.OpoInstall.**), el conjunto de reglas a continuación es solo ilustrativo: no es un conjunto de reglas de producción verificado para el SDK de OpoInstall v2.1.0. Inspeccione las rutas de clase reales y las reglas de consumidor integradas dentro de su archivo descargado v2.1.0 primero, y consulte la documentación técnica de OpoInstall (vea Cómo los Kits de Desarrollo de Software potencian la atribución y el crecimiento móvil para el código de ciclo de vida de Java subyacente):

# Archivo: app/src/main/keepRules/sdk-attribution.keep (o app/proguard-rules.pro)
# IMPORTANTE: Solo ilustrativo — no es un conjunto de reglas de producción verificado para OpoInstall SDK v2.1.0.
# Verifique los nombres de paquete y clase sensibles a mayúsculas dentro de su archivo v2.1.0 descargado
# mediante `jar tf` o `apkanalyzer`, y verifique `configuration.txt` por reglas de consumidor existentes.

# ---------------------------------------------------------------------------
# 1. Preservar atributos de Bytecode en clases explícitamente preservadas
# Requerido para que las interfaces de callback genéricas (ej. ResultCallBack<OpoData>)
# retengan sus metadatos de tipo parametrizado en R8 Full Mode.
# ---------------------------------------------------------------------------
-keepattributes Signature, InnerClasses, EnclosingMethod, *Annotation*

# ---------------------------------------------------------------------------
# 2. Reglas de punto de entrada, interfaz de callback y modelos de datos del SDK
# ---------------------------------------------------------------------------

# Preservar el punto de entrada público del SDK y cualquier constructor/método invocado por reflexión:
-keep public class com.opoinstall.api.OpoInstall {
    <init>(...);
    public <methods>;
}

# Preservar interfaces de listener de callback y sus métodos:
-keep public interface com.opoinstall.api.ResultCallBack {
    public <methods>;
}

# Escenario A: Si los modelos OpoData / OpoError se instancian dinámicamente por reflexión
# o se referencian solo vía genéricos borrados, use -keep para preservar la clase y sus miembros:
-keep class com.opoinstall.api.OpoData {
    <init>(...);
    <fields>;
    public <methods>;
}
-keep class com.opoinstall.api.OpoError {
    <init>(...);
    <fields>;
    public <methods>;
}

# Escenario B: Si una clase modelo ya es retenida directamente en el grafo de llamadas
# y solo sus campos requieren protección contra el renombrado, use -keepclassmembers:
# -keepclassmembers class com.opoinstall.api.OpoData { <fields>; }

# ---------------------------------------------------------------------------
# 3. Protección de JNI Downcall y Native-to-Java Upcall (Patrones JNI ilustrativos)
# ---------------------------------------------------------------------------
# Caso JNI A: Si el código Java/Kotlin ya instancia directamente la clase puente JNI,
# use -keepclassmembers,includedescriptorclasses para métodos de upcall C/C++,
# y preserve las clases/constructores de parámetros instanciados por código nativo:
-keepclassmembers,includedescriptorclasses class com.example.sdk.internal.JniBridge {
    public void onNativeAttributionEvent(com.example.sdk.internal.NativeEventData);
}
-keep class com.example.sdk.internal.NativeEventData {
    <init>(...);
}

# Caso JNI B: Si el código nativo C/C++ busca o instancia dinámicamente la clase Java
# mediante FindClass / NewObject, o registra métodos nativos vía RegisterNatives
# sin sitios de llamada Java directos, use -keep para prevenir la eliminación de clases y métodos:
-keep,includedescriptorclasses class com.example.sdk.internal.DynamicNativeBridge {
    <init>(...);
    public native <methods>;
    public void onNativeCallback(java.lang.String);
}
# Archivo: app/proguard-rules.pro (Directivas de salida de diagnóstico)
# Coloque las banderas de informe de diagnóstico R8 en su archivo de configuración verificado.
# Las rutas se resuelven relativas al directorio de trabajo del módulo (`app/`).
# Ajuste el nombre del directorio para variantes de producto (ej. build/outputs/mapping/prodRelease/).
-printseeds build/outputs/mapping/release/seeds.txt
-printusage build/outputs/mapping/release/usage.txt
-printconfiguration build/outputs/mapping/release/configuration.txt

Resolución de conflictos de ofuscación y fortalecimiento en paquetes de canal de Android

Separación de compilación R8, alineación APK, firma y paquetes de canal

Para distribuciones de Android fuera de Google Play, OpoInstall soporta la exportación de Paquetes de Canal de Android dedicados, tomando un APK base subido por el desarrollador y generando APKs específicos para cada canal que llevan metadatos de rastreo channelCode distintos.

Al solucionar problemas de estadísticas de canal faltantes o fallos de instalación en paquetes de canal exportados, los ingenieros deben separar dos criterios de verificación de lanzamiento independientes: validez de la firma criptográfica del APK e integridad de los metadatos del canal del proveedor:

  1. Reducción de código R8 en tiempo de compilación: R8 se ejecuta dentro del pipeline de compilación de Gradle al convertir bytecode Java/Kotlin en classes.dex antes de que el APK base sea empaquetado y firmado. R8 nunca se ejecuta en un APK ya exportado.
  2. Alineación de APK (zipalign) y Firma Criptográfica (apksigner): Los APKs estándar de lanzamiento construidos por AGP se alinean automáticamente. Sin embargo, si su pipeline de lanzamiento aplica post-procesamiento personalizado o refuerzo (hardening) de terceros antes de firmar, debe volver a alinear el APK bajo las especificaciones oficiales de zipalign de Android y las especificaciones de apksigner de Android. zipalign alinea datos sin comprimir en límites de 4 bytes; si el APK contiene librerías nativas .so sin comprimir, los equipos deben usar zipalign -P 16 -f -v 4 infile.apk outfile.apk para cumplir con los requisitos de alineación de página de 16 KB y verificar con zipalign -c -P 16 -v 4. zipalign debe ejecutarse antes de firmar con apksigner, ya que modificar un APK después de firmarlo invalida su firma criptográfica V2/V3.
  3. Verificación de Firma y Metadatos de Canal Específicos del Proveedor: Ejecutar herramientas de refuerzo (hardening) o reempaquetado de terceros después de exportar un APK de canal desde la consola de OpoInstall puede sobrescribir los metadatos del canal integrados o invalidar la firma del APK. Si un parser de paquetes de canal de terceros requiere un esquema de firma APK específico (como habilitar la firma JAR V1 junto con la firma V2/V3), verifique ese requisito directamente con su documentación actual de OpoInstall o soporte del proveedor antes de modificar las configuraciones de firma de producción, y no trate la firma V1 como un requisito universal del SO Android.

Orden recomendado del pipeline de lanzamiento para paquetes de canal de Android

Para mantener firmas APK válidas, optimización R8 intacta y metadatos de canal de proveedor, ejecute su pipeline de lanzamiento en esta secuencia estricta:

  1. Compilar y minificar el APK base con R8: Ejecute ./gradlew :app:assembleRelease con sus reglas de preservación verificadas para que R8 optimice classes.dex en tiempo de compilación.
  2. Completar refuerzo pre-subida, alineación y firma: Si su equipo de seguridad utiliza herramientas de refuerzo de terceros, aplíquelas al APK base, ejecute zipalign -P 16 -f -v 4 (si es necesaria la realineación antes de firmar), verifique con zipalign -c -P 16 -v 4 y firme el APK base.
  3. Subir el APK base y verificar independientemente los APKs de canal exportados: Suba el APK base firmado y ofuscado a la consola de OpoInstall, exporte los APKs de canal y verifique independientemente tanto la validez de la firma criptográfica (apksigner verify --verbose) como la atribución de canal en el dispositivo físico en el APK exportado antes de la distribución pública.
[Pipeline recomendado para paquetes de canal]:
Código Fuente -> Minificación Gradle R8 -> (Refuerzo previo opcional + zipalign -P 16 + Firma) -> Subir APK base a consola -> Exportar APK de canal -> Verificar firma (apksigner) y Callback de canal

Auditoría de símbolos del SDK preservados usando informes de R8 y APK Analyzer

Uso del Analizador de configuración R8 autónomo en AGP 9.3+

En el Android Gradle Plugin 9.3 y posteriores, no necesita esperar a una compilación completa de APK o App Bundle para auditar cómo se fusionan y coinciden sus reglas de preservación. AGP 9.3+ proporciona una tarea de Gradle dedicada analyze<Variant>R8Config (por ejemplo, ./gradlew :app:analyzeReleaseR8Config), que evalúa rápidamente las reglas del consumidor y del host y emite diagnósticos de configuración para que los ingenieros puedan iterar sobre las reglas .keep antes de ejecutar una compilación completa.

Verificación cruzada de configuration.txt, seeds.txt, usage.txt y mapping.txt

Los informes R8 de Android y la inspección DEX cruda verifican la integridad de los lanzamientos de paquetes de canal.

Nunca asuma que una regla -keep funcionó simplemente porque ./gradlew :app:assembleRelease finalizó sin errores. Para verificar su configuración determinísticamente, inspeccione los cuatro archivos de diagnóstico de R8 generados en app/build/outputs/mapping/<buildVariant>/ (use -whyareyoukeeping class <ClassName> cuando depure por qué un símbolo específico fue retenido):

  • configuration.txt: Contiene el conjunto de reglas completo y fusionado aplicado por R8, combinando sus reglas de preservación, reglas de plataforma Android por defecto, reglas AAPT2 y cualquier regla /proguard.txt o /META-INF/ integrada dentro de librerías .aar y .jar vinculadas.
  • seeds.txt: Lista cada clase, constructor (<init>()), método y campo que coincidió con sus reglas activas -keep. Note que la coincidencia en seeds.txt confirma la sintaxis de la regla, pero debe ser emparejada con inspección DEX y pruebas en tiempo de ejecución.
  • usage.txt: Lista el código muerto que R8 eliminó del APK durante la reducción. Confirme que ninguna de sus clases de SDK requeridas, constructores reflectivos o métodos de upcall JNI aparecen en usage.txt.
  • mapping.txt: Registra el mapa de traducción de identificadores antes y después para clases y miembros retenidos. Note que la ausencia en mapping.txt no prueba que una clase se preservó bajo su nombre original: puede haber sido eliminada completamente durante la reducción (y listada en usage.txt). Realice siempre referencia cruzada de mapping.txt contra seeds.txt, usage.txt y el archivo DEX compilado.

Verificación de identificadores DEX crudos vs. estados de informes deobfuscados con apkanalyzer

Al utilizar el CLI de APK Analyzer de Google Android Developers oficial para inspeccionar su APK de lanzamiento, distinga entre identificadores DEX compilados crudos y nombres desofuscados por mapeo:

  • Inspeccionando nombres DEX compilados crudos (Sin --proguard-mappings): Cuando pasa --proguard-mappings mapping.txt a apkanalyzer, la herramienta traduce los nombres DEX ofuscados (como a.b.c) de vuelta a sus nombres originales. Por lo tanto, ver un nombre de clase original cuando --proguard-mappings está habilitado no prueba que el símbolo escapó del renombrado en classes.dex. Ejecute apkanalyzer dex packages sin --proguard-mappings primero. Debido a que el reempaquetado de clases de R8 puede mover clases a rutas de paquete aplanadas, no confíe solo en un grep de paquete; inspeccione el árbol de clases DEX crudo, verifique los nombres de las clases de puntos de entrada de reflexión, revise los nombres de métodos JNI y firmas de descriptores, y verifique mapping.txt para confirmar que los métodos y campos requeridos no fueron renombrados.
  • Inspeccionando estados de retención de R8 (Con --proguard-mappings, --proguard-seeds y --proguard-usages): En una segunda pasada, proporcione los tres archivos de informe de R8 para reconstruir las banderas de retención (k por retenido por una regla seed, x por eliminado durante la reducción, r por referenciado y d por definido) a través de cada paquete (P), clase (C), método (M) y campo (F):
# Ejecutar desde el directorio raíz del proyecto Android (ajuste la ruta de la variante para sabores de producto, ej. prodRelease)

# 1. (AGP 9.3+) Ejecute el analizador de configuración R8 autónomo para verificación rápida, luego compile el APK de lanzamiento
./gradlew :app:analyzeReleaseR8Config
./gradlew :app:assembleRelease

# 2. Inspeccione la configuración R8 fusionada, los seeds coincidentes y los informes de uso eliminados
grep -i -C 3 "opoinstall" app/build/outputs/mapping/release/configuration.txt
grep -i "opoinstall" app/build/outputs/mapping/release/seeds.txt
grep -i "opoinstall" app/build/outputs/mapping/release/usage.txt

# 3A. Inspeccione identificadores DEX compilados CRUDOS (SIN --proguard-mappings)
# Inspeccione también el árbol DEX crudo completo y verifique mapping.txt en caso de reempaquetado
apkanalyzer dex packages app/build/outputs/apk/release/app-release.apk | grep -i "opoinstall"

# 3B. Inspeccione banderas de estado de retención R8 (k=retenido, x=eliminado, r=ref, d=def) CON informes R8
apkanalyzer dex packages \
  --proguard-mappings app/build/outputs/mapping/release/mapping.txt \
  --proguard-seeds app/build/outputs/mapping/release/seeds.txt \
  --proguard-usages app/build/outputs/mapping/release/usage.txt \
  app/build/outputs/apk/release/app-release.apk | grep -i "opoinstall"

# 4. Verifique la alineación de página de 16 KB (si hay archivos .so nativos) y firmas APK criptográficas
zipalign -c -P 16 -v 4 app/build/outputs/apk/release/app-release.apk
apksigner verify --verbose app/build/outputs/apk/release/app-release.apk

Matriz de solución de problemas de atribución en lanzamientos

Diagnóstico de fallos silenciosos de atribución y deep-link en compilaciones Android minificadas

Al solucionar discrepancias de atribución que solo ocurren en compilaciones release minificadas, use la matriz de diagnóstico a continuación para aislar la causa raíz:

Síntoma observado en Release Causa raíz probable en R8 / Build Paso de verificación diagnóstica Acción correctiva
El callback getInstallParam o handleDeepLink no se dispara Clases de punto de entrada del SDK, constructores reflectivos <init>(), o métodos ResultCallBack fueron eliminados o renombrados Verificar seeds.txt y usage.txt; inspeccionar salida apkanalyzer dex packages (sin mapeo) y mapping.txt Inspeccionar el .jar / .aar con jar tf para hallar rutas de clase precisas y añadir reglas -keep incluyendo <init>(...);
Callback se dispara, pero parámetros personalizados de OpoData salen vacíos/nulos OpoData fue protegido solo con -keepclassmembers (permitiendo que la clase sea eliminada) o se perdieron metadatos Signature Revisar usage.txt y mapping.txt para OpoData y verificar configuration.txt para -keepattributes Signature Usar -keep class ... con <init>(...); <fields>; y retener -keepattributes Signature
UnsatisfiedLinkError o callback nativo faltante al cargar .so Downcalls RegisterNatives fueron reducidos o métodos de upcall C/C++-a-Java renombrados Inspeccionar logcat para el símbolo JNI faltante y revisar apkanalyzer y mapping.txt Usar -keep (o -keepclassmembers) con includedescriptorclasses en los objetivos de upcall JNI y RegisterNatives
El paquete de canal de Android falla al instalar o no registra estadísticas Modificación del APK post-exportación invalidó la firma o sobrescribió metadatos Ejecutar zipalign -c -P 16 -v 4 y apksigner verify en el APK exportado y probar en un dispositivo físico Completar toda la minificación R8, refuerzo, alineación y firma en el APK base antes de subirlo a la consola; verificar tras la exportación

Preguntas Frecuentes (FAQ)

¿Cómo excluyo un SDK móvil de la ofuscación ProGuard y R8 en Android?
Primero, inspeccione su archivo `.aar` (`/proguard.txt` y `classes.jar/META-INF/com.android.tools/`) o `.jar` (`/META-INF/proguard/` y `/META-INF/com.android.tools/`) y revise `configuration.txt` para ver si las reglas ProGuard de consumidor ya están incluidas. Si se requieren reglas manuales, añada directivas `-keep` y `-keepclassmembers` de alcance limitado, apuntando exactamente a las clases de entrada del SDK, constructores reflectivos (`<init>(...);`), interfaces de callback, objetivos JNI y `<fields>;`, junto con `-keepattributes Signature, InnerClasses, EnclosingMethod, *Annotation*`.
¿Cómo verifico que mis clases de SDK de Android fueron excluidas de la ofuscación R8?
Habilite `-printseeds`, `-printusage` y `-printconfiguration` en su configuración R8 (o ejecute `./gradlew :app:analyzeReleaseR8Config` en AGP 9.3+) y construya su APK de lanzamiento. Verifique que las clases y constructores del SDK aparezcan en `seeds.txt` y no aparezcan en `usage.txt`. Luego, ejecute `apkanalyzer dex packages app-release.apk` **sin** `--proguard-mappings` y verifique `mapping.txt` para confirmar que los símbolos DEX crudos retienen sus nombres originales. Finalmente, ejecute `apkanalyzer` con `--proguard-mappings`, `--proguard-seeds` y `--proguard-usages` para auditar las banderas de retención (`k`, `x`, `r`, `d`).
¿Por qué las estadísticas de los paquetes de canal de Android dejan de funcionar después del hardening o post-procesamiento del APK?
Cuando exporta un paquete de canal de Android desde la consola de atribución, los metadatos de identificación de canal se incrustan en el APK. Si posteriormente ejecuta herramientas de refuerzo (hardening), empaquetado o re-firma en ese APK, el paso de post-procesamiento puede invalidar la firma criptográfica o sobrescribir los metadatos del canal. Complete siempre la minificación R8, el refuerzo, `zipalign` y la firma en su APK base *antes* de subirlo a la consola, y verifique los APKs de canal exportados con `apksigner verify --verbose`.

Resumen y lista de verificación de compilación para lanzamientos

Entender cómo excluir SDKs móviles de la ofuscación de ProGuard y R8 es fundamental para prevenir regresiones de atribución silenciosas al realizar la transición de pruebas de depuración a compilaciones de producción. Al verificar las reglas de consumidor, escribir directivas -keep de alcance preciso que coincidan con las rutas exactas dentro del archivo de su SDK, respetar el orden de compilación para paquetes de canal de Android y verificar la salida diagnóstica de R8 contra el bytecode DEX, los ingenieros pueden mantener una reducción de código completa sin romper el deep linking diferido o la restauración de parámetros.

Antes de publicar una compilación Android minificada en Google Play o exportar paquetes de canal, ejecute esta lista de verificación de cuatro pasos:

  • Inspeccione el archivo SDK y la configuración fusionada: Revise su archivo SDK descargado (como v2.1.0) con jar tf o unzip -l para inspeccionar las rutas de clase exactas y las reglas de consumidor, y ejecute ./gradlew :app:analyzeReleaseR8Config (en AGP 9.3+) o revise configuration.txt.

  • Verifique informes R8 e identificadores DEX crudos: Confirme que los puntos de entrada del SDK, constructores <init>(), campos del modelo OpoData, métodos ResultCallBack y firmas JNI aparezcan en seeds.txt, estén ausentes de usage.txt y retengan sus nombres originales en la salida de apkanalyzer dex packages (sin --proguard-mappings) y mapping.txt.

  • Aplique minificación pre-subida y verifique firmas de paquetes de canal: Complete toda la minificación R8, refuerzo de terceros, zipalign (zipalign -c -P 16 -v 4) y la firma del APK base antes de subirlo para exportar paquetes de canal, y ejecute apksigner verify --verbose en los paquetes exportados.

  • Ejecute una prueba de humo de lanzamiento en dispositivo físico: Instale el APK firmado y minificado en un dispositivo Android real y verifique tanto la instalación de parámetros H5 (getInstallParam) como la activación con un clic (handleDeepLink).

Los equipos de ingeniería pueden registrar una aplicación y subir su APK base ofuscado en la consola de desarrolladores de OpoInstall para probar la exportación de paquetes de canal y verificar la restauración de parámetros en compilaciones release.

Materiales relacionados

Share this article