Como configurar regras de ofuscação ProGuard para SDK Android com segurança

opoinstall
2026-11-11
5 min read

Como excluir SDKs móveis da ofuscação do ProGuard no Android? Excluir um SDK Android da ofuscação exige verificar se a biblioteca inclui regras ProGuard do consumidor, adicionar diretrizes -keep e -keepclassmembers com escopo restrito para pontos de entrada acessados via reflexão, interfaces de callback, pontes JNI e modelos de dados, além de auditar os símbolos preservados por meio de relatórios de diagnóstico do R8 e inspeção do DEX bruto. Quando as equipes de engenharia Android habilitam a redução e otimização de código para builds de lançamento em produção, o compilador R8 remove bytecode não referenciado e renomeia classes, campos e métodos para identificadores curtos. Se um SDK de atribuição móvel depender de reflexão dinâmica ou callbacks JNI que não estejam protegidos contra a remoção agressiva de código e renomeação, o deep linking diferido, a desserialização de modelos JSON e as builds de pacotes de canais podem falhar silenciosamente em produção, mesmo após passarem por todos os testes em uma build de depuração sem minificação. O OpoInstall, uma plataforma de atribuição e deep linking móvel, fornece arquivos SDK Android nativos e fluxos de trabalho de pacotes de canais que as equipes de engenharia podem configurar e verificar em pipelines de lançamento minificados pelo R8.

Nos toolchains modernos do Android Gradle Plugin (AGP), arquivos de regras de keep (proguard-rules.pro no DSL legado ou src/<variant>/keepRules/*.keep no AGP 9.3+) servem como contrato de configuração de regras para o compilador R8 do Google. Excluir com segurança um SDK Android de terceiros da ofuscação significa preservar apenas os pontos de entrada de API pública específicos, construtores reflexivos, interfaces de callback, chamadas JNI e campos de modelo necessários em tempo de execução—evitando regras de keep amplas ou supressões de avisos não verificadas.

Termo Definição Entidade Relacionada Intenção de Busca
SDK Android Uma biblioteca Java ou Kotlin compilada integrada a um aplicativo Android para fornecer recursos especializados, como atribuição de instalação. Bytecode classes.dex Navegacional / Transacional
ProGuard / R8 Ferramentas de build Android que reduzem código não utilizado, otimizam bytecode e ofuscam identificadores usando sintaxe de regras ProGuard. proguard-rules.pro Técnico / Informativo
Regras ProGuard do Consumidor Regras de ofuscação agrupadas em um .aar (/proguard.txt) ou .jar (/META-INF/proguard/) que se fundem automaticamente no R8. consumerProguardFiles Técnico / Solução de problemas
Relatórios de Diagnóstico R8 Arquivos de saída (mapping.txt, seeds.txt, usage.txt, configuration.txt) detalhando renomeação de símbolos, seeds de keep correspondentes e código removido. Auditoria de Build de Lançamento Técnico / Solução de problemas

Por que a ofuscação do R8 e ProGuard quebra callbacks de atribuição de SDK Android

A otimização R8 do Android preserva símbolos essenciais do SDK durante a compilação de lançamento.

A Armadilha entre Debug e Release: Por que a atribuição funciona no teste, mas falha na produção

Uma das regressões de engenharia de lançamento mais comuns na aquisição de usuários móveis ocorre quando o deep linking diferido (handleDeepLink) e a restauração de parâmetros de instalação (getInstallParam) de um aplicativo funcionam de forma confiável durante testes de QA em builds de debug, mas retornam payloads vazios ou param de disparar callbacks quando o aplicativo é compilado como um APK de lançamento assinado ou Android App Bundle (.aab).

A causa raiz reside na disparidade de tipos de build configurada no Gradle. Por padrão, o tipo de build debug desativa a redução de código para acelerar builds incrementais. Quando os engenheiros compilam a variante release com a otimização R8 ativada, o Android Gradle Plugin invoca o Compilador R8 do Android para executar três transformações de bytecode tanto no aplicativo host quanto nas bibliotecas de terceiros vinculadas:

  • Redução de Código (Tree Shaking): O R8 analisa o grafo de chamadas do aplicativo começando a partir de pontos de entrada declarados (como componentes do AndroidManifest.xml e seeds -keep explícitos) e remove qualquer classe, construtor, método ou campo que pareça inalcançável por meio de análise estática.
  • Otimização de Bytecode e Modificação de Acesso: O R8 inlinha métodos, remove parâmetros não utilizados, funde classes e—quando pareado com a diretriz -allowaccessmodification na configuração de otimização padrão do Android—modifica flags de visibilidade de membros e classes (como ampliar membros private ou package-private para public durante o inlining) para reduzir o tamanho do Dalvik Executable (.dex) e melhorar a velocidade de execução em tempo de execução.
  • Ofuscação de Identificadores (Renomeação e Repacotamento): O R8 renomeia classes, pacotes, campos e métodos restantes para identificadores compactos (como a.a.b()) e pode repacotar classes ofuscadas em hierarquias de pacotes achatadas, reduzindo o overhead do pool de strings dentro do classes.dex.

O Modo Completo (Full Mode) do R8 é ativado por padrão desde o Android Gradle Plugin (AGP) 8.0. Comparado ao modo de compatibilidade legado do ProGuard, o Modo Completo do R8 aplica otimizações substancialmente mais rigorosas—removendo construtores padrão sem argumentos (<init>()), alterando modificadores de acesso de membros e removendo atributos de metadados, a menos que as classes e membros associados sejam mantidos explicitamente por uma regra de keep correspondente. Para um contexto fundamental sobre estruturas binárias .aar e padrões de aquisição de SDK, consulte nossos guias complementares: Compreendendo os Significados de SDK Móvel e Protocolos de Crescimento e Como Escolher um Kit de Desenvolvimento de Software para Atribuição Precisa.

Seis Modos de Falha Distintos em SDKs de Atribuição Ofuscados

Callbacks de reflexão, modelos e JNI do SDK Android exigem regras de preservação R8 direcionadas.

Como a análise estática do grafo de chamadas não consegue prever pesquisas dinâmicas em tempo de execução, um SDK Android desprotegido pode sofrer seis modos de falha distintos sob o Modo Completo do R8. Os engenheiros devem distinguir esses modos de falha cuidadosamente, pois cada um requer um alvo de proteção ProGuard específico:

Modo de Falha em Tempo de Execução Causa Subjacente do Bytecode R8 Alvo de Proteção Específico Necessário
1. Falha na Busca de Classe Reflexiva (ClassNotFoundException) O R8 renomeia, repacota ou remove uma classe carregada dinamicamente por nome de string via Class.forName() Preserve a classe alvo e seu identificador original usando -keep class ...
2. Falha na Instanciação de Objeto Reflexivo (NoSuchMethodException: <init>) No Modo Completo do R8, mesmo que uma classe seja mantida, seu construtor padrão sem argumentos (<init>()) é removido se não for chamado diretamente no código Preserve explicitamente a assinatura do construtor (<init>();) dentro do bloco -keep da classe
3. Falhas de Callback Dinâmico & Modificação de Acesso O R8 remove ou renomeia métodos de interface de callback, ou altera flags de visibilidade de membros esperados por chamadas reflexivas getDeclaredMethod() / getDeclaredField() Preserve os métodos da interface de callback (-keep interface ... { <methods>; }) e quaisquer membros acessados reflexivamente
4. Perda de Metadados de Assinatura Genérica em Listeners de Callback Genéricos Java sofrem apagamento de tipo em tempo de compilação, enquanto atributos de Signature do bytecode preservam metadados genéricos para reflexão em tempo de execução (ResultCallBack<OpoData>). O Modo Completo do R8 remove metadados de Signature a menos que sejam mantidos explicitamente Mantenha -keepattributes Signature além de manter tanto a interface de callback genérica quanto a classe de modelo alvo
5. Falha na Desserialização de Modelo de Dados JSON O R8 remove a classe de modelo (se instanciada apenas reflexivamente) ou renomeia seus campos para a, b, c, fazendo com que parsers JSON baseados em reflexão retornem campos nulos ou vazios Use -keep class ... se o modelo for instanciado dinamicamente, ou -keepclassmembers se a classe já for mantida por meio de referências diretas
6. Remoção de Chamada JNI Downcall & Upcall Nativo-para-Java (UnsatisfiedLinkError / NoSuchMethodError) Regras padrão do Android preservam nomes em native downcalls Java sobreviventes, mas não impedem que métodos RegisterNatives não utilizados sejam reduzidos, e não podem ver códigos C/C++ .so chamando de volta para Java via JNI (FindClass, GetMethodID) -keep explicitamente declarações native registradas dinamicamente e quaisquer classes, construtores e métodos Java invocados via JNI upcalls de C/C++
[Android Release Build: Modo Completo R8 Ativado]
                      |
                      v
     [R8 Avalia Regras de Keep Agrupadas e do Host]
                      |
      +---------------+---------------+
      |                               |
      v (Regras Faltantes / Desajustadas)  v (Regras de Keep com Escopo + Atributos Ativos)
[Símbolos do SDK Removidos ou Renomeados] [Pontos de Entrada & Modelos de SDK Necessários Preservados]
- () Reflexivo Removido     - Confirmado em -printseeds (seeds.txt)
- JNI Upcall / Campos de Modelo Perdidos  - Verificado em DEX Bruto (apkanalyzer dex packages)
- Metadados de Assinatura Genérica Perdidos - Atribuição de Lançamento corresponde ao Comportamento de Debug

Regras de Consumidor em Pacotes AAR vs. Importações Manuais JAR

Como bibliotecas AAR e JAR agrupam regras ProGuard de consumidor

Arquivos AAR e JAR do Android contribuem com regras de consumidor para a configuração R8.

Antes de adicionar regras de exclusão manuais ao seu aplicativo host, inspecione se o SDK Android de terceiros já inclui regras R8 integradas. Conforme documentado no Guia de Desenvolvedores Android do Google sobre Otimização de Biblioteca, o AGP e o R8 suportam a descoberta automática de regras de consumidor em distribuições .aar e .jar, incluindo regras R8 direcionadas por versão:

Formato de Distribuição SDK Local da Regra de Consumidor Embutida Comportamento de Build AGP / R8
Android Archive (.aar) /proguard.txt na raiz do AAR (via consumerProguardFiles), ou regras direcionadas por versão dentro de /classes.jar sob /META-INF/com.android.tools/r8-from-<X>-upto-<Y>/ Extraído e mesclado automaticamente na configuração R8 do aplicativo host no momento da compilação
Java Archive (.jar) com Regras Integradas /META-INF/proguard/*.pro ou /META-INF/com.android.tools/r8-from-<X>-upto-<Y>/ (incluindo variantes r8, r8-from-<X> e r8-upto-<Y>) Descoberto e aplicado automaticamente pelo AGP e R8 de acordo com a versão ativa do compilador R8
.jar ou .aar Independente Sem Regras Integradas Nenhum presente dentro do arquivo Nenhuma proteção de consumidor integrada é fornecida; adicione regras de keep em nível de host apenas quando reflexão em tempo de execução, JNI, serialização ou outros símbolos acessados dinamicamente exigirem preservação explícita

(Nota sobre Regras de Serialização Gson: Conforme documentado no Guia de Solução de Problemas do Gson para ProGuard e R8, o Gson 2.11.0 e versões posteriores incluem regras R8 para TypeToken e campos anotados com @SerializedName. Essas regras não eliminam a necessidade de restringir o acesso reflexivo ao modelo, fornecer construtores sem argumentos compatíveis ou testar todos os caminhos de serialização e desserialização em builds de lançamento minificadas.)

Inspecionando seu arquivo SDK baixado antes de escrever regras

A correspondência de regras ProGuard e R8 é estritamente sensível a maiúsculas e minúsculas e exata quanto ao caminho do pacote. Entre diferentes versões de SDK ou distribuições regionais, os namespaces de pacotes podem variar—e se você copiar uma regra de um tutorial antigo cujo uso de maiúsculas no pacote não corresponda aos arquivos .class reais em seu arquivo, o R8 corresponderá silenciosamente a zero classes.

Antes de editar suas regras de keep, baixe seu pacote SDK alvo (como o SDK Android v2.1.0 do centro de download de SDK móvel) e inspecione suas regras embutidas (tanto em /proguard.txt quanto dentro de classes.jar) ao lado de sua hierarquia exata de .class:

# 1. Inspecione um arquivo .aar em busca de regras de consumidor raiz (proguard.txt) e extraia classes.jar para verificar regras 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. Se estiver usando um arquivo .jar independente, inspecione regras META-INF embutidas e caminhos .class sensíveis a maiúsculas
jar tf libs/OpoInstall_SDK.jar | grep -E "META-INF/proguard/|META-INF/com\.android\.tools/|\.class$"

Configurando proguard-rules.pro para SDKs de Atribuição Android

Evitando antipadrões de regras de keep para todo o pacote e supressões de avisos genéricas

Ao escrever regras personalizadas, os engenheiros devem seguir as melhores práticas do Google em Escolha o que Manter (Regras de Keep do R8) para evitar desativar a otimização desnecessariamente:

  • Prefira Regras Restritas a Antipadrões de todo o pacote -keep class package.** { *; }: Uma regra abrangente para todo o pacote força o R8 a reter e pular a otimização em cada classe auxiliar interna naquele pacote. Limite suas regras às classes específicas, construtores (<init>();), interfaces de callback, alvos JNI e campos de modelo acessados dinamicamente em tempo de execução (ou use a anotação @UnconditionallyKeep do Jetpack no código que você controla).
  • Distingua -keep de -keepclassmembers em Modelos de Dados: -keepclassmembers preserva apenas os campos ou métodos especificados se a classe que os contém for mantida no grafo de chamadas. Se um modelo de dados (como OpoData) for instanciado apenas via reflexão ou referenciado apenas através de tipos genéricos apagados, use -keep class ... para que o R8 não remova a própria classe.
  • Entenda as Limitações do -keepclasseswithmembernames para JNI: As regras de otimização padrão do Android incluem -keepclasseswithmembernames,includedescriptorclasses class * { native <methods>; }. Conforme definido na documentação do R8 Keep Rules do Google e no Manual do ProGuard sobre Diretrizes de Keep, -keepclasseswithmembernames é equivalente a -keepclasseswithmembers,allowshrinking. Ele preserva os nomes das classes e membros correspondentes que sobrevivem à fase de redução, mas não impede que classes não referenciadas ou métodos native sejam removidos durante a redução. Se métodos nativos forem registrados dinamicamente via RegisterNatives sem locais de chamada Java diretos, ou se o código C/C++ invocar métodos Java via JNI upcalls, você deve usar regras explícitas -keep ou -keepclassmembers de acordo com a forma como a classe alvo é instanciada.
  • Evite Diretrizes -dontwarn Abrangentes Não Verificadas: -dontwarn fornece proteção zero contra redução ou renomeação e suprime avisos de referência não resolvida que podem ocultar falhas de NoClassDefFoundError em tempo de execução. Adicione um -dontwarn com escopo restrito apenas se o seu log de build relatar uma classe específica ausente apenas para compilação que você verificou ser opcional em tempo de execução.
Diretriz ProGuard / R8 Sintaxe Equivalente de Longa Forma Redução (Remoção) Ofuscação (Renomeação) Escopo de Engenharia Recomendado
-keep class ... -keep Impedido (Classe & membros especificados mantidos) Impedido (Nomes originais mantidos) Pontos de entrada públicos de SDK, interfaces de callback e classes instanciadas dinamicamente via reflexão ou JNI (incluir <init>(...);)
-keepclassmembers class ... -keepclassmembers Membros mantidos apenas se a classe for retida Impedido para membros correspondentes Preservação de <fields>; ou métodos de JNI upcall em classes que já são diretamente instanciadas em Java/Kotlin
-keepclasseswithmembernames -keepclasseswithmembers,allowshrinking Permitido se não referenciado (allowshrinking) Impedido se classe & membros sobreviverem à redução Preservação de nome de JNI downcall padrão (native <methods>;) quando o código Java invoca diretamente o método native
-keepnames class ... -keep,allowshrinking Permitido se não referenciado (allowshrinking) Impedido se a classe for retida Manter nomes de classe legíveis para stack traces sem forçar classes não utilizadas para dentro do classes.dex
-keepattributes ... -keepattributes Preserva metadados nos itens mantidos N/A (Atributo de Bytecode) Manter Signature (genéricos), InnerClasses, EnclosingMethod e *Annotation* nas classes explicitamente mantidas
-dontwarn <specific_class> -dontwarn Sem efeito na redução Sem efeito na renomeação Suprimir avisos em tempo de compilação apenas para uma dependência opcional de compilação verificada

Configurando o Gradle entre AGP 8.x e Toolchains AGP 9.3+

O Android Gradle Plugin 9.3 introduziu um bloco DSL de optimization simplificado e diretórios keepRules específicos para variantes (arquivos .keep), enquanto o AGP 8.x até 9.2 (e configurações legadas ainda suportadas no AGP 9.3+) usam isMinifyEnabled e proguardFiles. Os engenheiros devem manter três objetos de configuração distintos:

Objeto de Configuração Local do Arquivo ou DSL Papel Arquitetural em Builds R8
Regras de Keep DSL Legado (AGP 8.x–9.2 & Legado 9.3+) app/proguard-rules.pro (referenciado via proguardFiles(...)) Declara regras de keep do host ao lado de getDefaultProguardFile("proguard-android-optimize.txt")
Regras de Keep DSL de Otimização Novo (AGP 9.3+) app/src/main/keepRules/*.keep ou app/src/<variant>/keepRules/*.keep Declara arquivos .keep ao usar optimization { enable = true } (regras padrão da plataforma Android são aplicadas automaticamente; DSL Legado também suporta conjuntos de origem .keep)
Diretrizes de Relatório de Diagnóstico R8 -printseeds, -printusage, -printconfiguration em um arquivo de configuração R8 verificado (proguard-rules.pro) Instrui o R8 a emitir relatórios de auditoria para app/build/outputs/mapping/<buildVariant>/
// Opção A: AGP 8.x - 9.2 DSL Legado (também suportado no AGP 9.3+) — app/build.gradle.kts
android {
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            isShrinkResources = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
}

// Opção B: AGP 9.3+ DSL de Otimização Simplificado — app/build.gradle.kts
// Coloque regras de keep de classe/membro com escopo em app/src/main/keepRules/sdk-attribution.keep
android {
    buildTypes {
        release {
            optimization {
                enable = true
            }
        }
    }
}

Regras de Keep com Escopo Ilustrativas e Diretrizes de Diagnóstico

Como a documentação pública e as páginas de FAQ históricas do OpoInstall referenciam diferentes prefixos de pacote (com.opoinstall.api.** vs. com.fm.OpoInstall.** ou io.OpoInstall.**), o conjunto de regras abaixo é apenas ilustrativo—não um conjunto de regras de produção verificado para o SDK OpoInstall v2.1.0. Inspecione os caminhos de classe reais e regras de consumidor embutidas dentro do seu arquivo v2.1.0 baixado primeiro, e consulte a documentação técnica do OpoInstall (veja Como Kits de Desenvolvimento de Software Impulsionam a Atribuição e o Crescimento Móvel para o código de ciclo de vida Java subjacente):

# Arquivo: app/src/main/keepRules/sdk-attribution.keep (ou app/proguard-rules.pro)
# IMPORTANTE: Apenas ilustrativo — não um conjunto de regras de produção verificado para o SDK OpoInstall v2.1.0.
# Verifique os nomes exatos de pacotes e classes (sensíveis a maiúsculas) dentro do seu arquivo v2.1.0 baixado
# via `jar tf` ou `apkanalyzer`, e verifique `configuration.txt` para regras de consumidor existentes.

# ---------------------------------------------------------------------------
# 1. Preservar Atributos de Bytecode em Classes Explicitamente Mantidas
# Necessário para que interfaces de callback genéricas (ex: ResultCallBack<OpoData>)
# mantenham seus metadados de tipo parametrizado no Modo Completo do R8.
# ---------------------------------------------------------------------------
-keepattributes Signature, InnerClasses, EnclosingMethod, *Annotation*

# ---------------------------------------------------------------------------
# 2. Regras de Ponto de Entrada, Interface de Callback e Modelo de Dados do SDK com Escopo
# ---------------------------------------------------------------------------

# Preservar o ponto de entrada público do SDK e quaisquer construtores/métodos invocados reflexivamente:
-keep public class com.opoinstall.api.OpoInstall {
    <init>(...);
    public <methods>;
}

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

# Cenário de Modelo A: Se os modelos OpoData / OpoError forem instanciados dinamicamente via reflexão
# ou referenciados apenas via genéricos apagados, use -keep para preservar tanto a classe quanto seus membros:
-keep class com.opoinstall.api.OpoData {
    <init>(...);
    <fields>;
    public <methods>;
}
-keep class com.opoinstall.api.OpoError {
    <init>(...);
    <fields>;
    public <methods>;
}

# Cenário de Modelo B: Se uma classe de modelo já for mantida diretamente no grafo de chamadas do seu host
# e apenas seus campos exigirem proteção contra renomeação, use -keepclassmembers:
# -keepclassmembers class com.opoinstall.api.OpoData { <fields>; }

# ---------------------------------------------------------------------------
# 3. Proteção de JNI Downcall e Upcall Nativo-para-Java (Padrões JNI Ilustrativos)
# ---------------------------------------------------------------------------
# Caso JNI A: Se o código Java/Kotlin já instancia diretamente a classe de ponte JNI,
# use -keepclassmembers,includedescriptorclasses para métodos de upcall C/C++, e mantenha
# quaisquer classes de modelo de parâmetro/construtores 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: Se o código nativo C/C++ procura ou instancia dinamicamente a classe Java
# por si só via FindClass / NewObject, ou registra métodos nativos via RegisterNatives
# sem locais de chamada Java diretos, use -keep para evitar a remoção de classe e método:
-keep,includedescriptorclasses class com.example.sdk.internal.DynamicNativeBridge {
    <init>(...);
    public native <methods>;
    public void onNativeCallback(java.lang.String);
}
# Arquivo: app/proguard-rules.pro (Diretrizes de Saída de Diagnóstico)
# Coloque flags de relatório de diagnóstico R8 em seu arquivo de configuração R8 verificado.
# Caminhos resolvem relativo ao diretório de trabalho do módulo (`app/`).
# Ajuste o nome do diretório para sabores de produto (ex: 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

Resolvendo Conflitos de Ofuscação e Proteção de Pacotes de Canal Android

Separando Compilação R8 em tempo de build, Alinhamento de APK, Assinatura e Pacotes de Canal

Para distribuições Android fora do Google Play, o OpoInstall suporta a exportação de Android Channel Packages dedicados—pegando um APK base enviado pelo desenvolvedor e gerando APKs específicos de canal carregando metadados de rastreamento channelCode distintos.

Ao solucionar problemas de estatísticas de canal ausentes ou falhas de instalação em pacotes de canal exportados, os engenheiros devem separar dois critérios de verificação de lançamento independentes—validade da assinatura criptográfica do APK e integridade dos metadados de canal do fornecedor:

  1. Redução de Código R8 em tempo de Build: O R8 é executado dentro do pipeline de build do Gradle ao compilar bytecode Java/Kotlin em classes.dex antes que o APK base seja empacotado e assinado. O R8 nunca é executado em um APK já exportado.
  2. Alinhamento (zipalign) e Assinatura Criptográfica (apksigner) do APK Android: APKs de lançamento padrão construídos pelo AGP são alinhados automaticamente. No entanto, se o seu pipeline de lançamento aplicar pós-processamento personalizado ou reforço de APK de terceiros antes da assinatura, você deve realinhar o APK sob as Especificações oficiais de zipalign do Android e as Especificações de apksigner do Android. O zipalign alinha dados não compactados em limites de 4 bytes—e se o APK contiver bibliotecas nativas .so não compactadas, as equipes devem usar zipalign -P 16 -f -v 4 infile.apk outfile.apk para satisfazer os requisitos de alinhamento de página de 16 KB e verificar o alinhamento com zipalign -c -P 16 -v 4. O zipalign deve ser executado antes da assinatura com apksigner, porque modificar um APK após a assinatura invalida sua assinatura criptográfica V2/V3 de APK completo.
  3. Verificação de Assinatura e Metadados de Canal Específicos do Fornecedor: Executar ferramentas de reforço (empacotamento) ou reempacotamento de APK de terceiros após exportar um APK de canal do console OpoInstall pode sobrescrever metadados de canal embutidos ou invalidar a assinatura do APK. Se um parser de pacote de canal de terceiros exigir um esquema de assinatura de APK específico (como habilitar a assinatura JAR V1 ao lado da assinatura V2/V3 de APK completo), verifique esse requisito diretamente em sua documentação atual do console OpoInstall ou suporte ao fornecedor antes de modificar as configurações de assinatura de produção, e não trate a assinatura V1 como um requisito universal do sistema operacional Android.

A Ordem Recomendada de Pipeline de Lançamento para Pacotes de Canal Android

Para manter assinaturas APK válidas, otimização de bytecode R8 e metadados de canal de fornecedor intactos, execute seu pipeline de lançamento nesta sequência estrita:

  1. Compile e Minifique o APK Base com R8: Execute ./gradlew :app:assembleRelease com suas regras de keep verificadas para que o R8 otimize o classes.dex no momento da build.
  2. Conclua Qualquer Proteção Pré-Upload, Alinhamento e Assinatura: Se sua equipe de segurança usa ferramentas de reforço de APK de terceiros, aplique-as ao APK base, execute zipalign -P 16 -f -v 4 (se o realinhamento for necessário antes da assinatura), verifique o alinhamento com zipalign -c -P 16 -v 4 e assine o APK base.
  3. Envie o APK Base e Verifique Independentemente os APKs de Canal Exportados: Envie o APK base assinado e ofuscado para o console OpoInstall, exporte os APKs de canal e verifique independentemente tanto a validade da assinatura criptográfica (apksigner verify --verbose) quanto a atribuição de canal em dispositivo físico no APK exportado antes da distribuição pública.
[Pipeline Recomendado de Pacote de Canal]:
Código Fonte -> Minificação R8 do Gradle -> (Opcional: Proteção Pré-Upload + zipalign -P 16 + Assinatura) -> Enviar APK Base para o Console -> Exportar APK de Canal -> Verificar Assinatura (apksigner) & Callback de Canal

Auditando Símbolos de SDK Preservados Usando Relatórios R8 e APK Analyzer

Usando o Analisador de Configuração R8 Independente no AGP 9.3+

No Android Gradle Plugin 9.3 e posterior, você não precisa aguardar uma compilação completa de APK de lançamento ou App Bundle para auditar como suas regras de keep se mesclam e correspondem. O AGP 9.3+ fornece uma tarefa dedicada analyze<Variant>R8Config do Gradle (por exemplo, ./gradlew :app:analyzeReleaseR8Config), que avalia rapidamente as regras de keep do consumidor e do host e emite diagnósticos de configuração para que os engenheiros possam iterar nas regras .keep antes de executar uma build de lançamento completa.

Cruzando configuration.txt, seeds.txt, usage.txt e mapping.txt

Relatórios R8 do Android e inspeção DEX bruta verificam a integridade de lançamento do APK de canal.

Nunca assuma que uma regra -keep funcionou simplesmente porque ./gradlew :app:assembleRelease terminou sem erros de build. Para verificar sua configuração deterministicamente, inspecione os quatro arquivos de diagnóstico R8 gerados em app/build/outputs/mapping/<buildVariant>/ (use -whyareyoukeeping class <ClassName> ao depurar por que um símbolo específico foi retido):

  • configuration.txt: Contém o conjunto completo de regras mesclado aplicado pelo R8—combinando suas regras de keep do host, regras padrão da plataforma Android, regras AAPT2 e quaisquer regras /proguard.txt ou /META-INF/ incluídas em bibliotecas .aar e .jar vinculadas.
  • seeds.txt: Lista cada classe, construtor (<init>()), método e campo que correspondeu às suas regras -keep ativas. Observe que a correspondência em seeds.txt confirma a sintaxe da regra, mas deve ser pareada com inspeção DEX e testes em tempo de execução.
  • usage.txt: Lista código morto que o R8 removeu do APK durante a redução (tree-shaking). Confirme que nenhuma das suas classes necessárias do SDK, construtores reflexivos ou métodos de JNI upcall aparecem em usage.txt.
  • mapping.txt: Registra o mapa de tradução de identificadores antes e depois para classes e membros retidos. Observe que a ausência no mapping.txt não prova que uma classe foi preservada sob seu nome original—ela pode ter sido completamente removida durante a redução (e listada em usage.txt). Sempre cruze o mapping.txt com seeds.txt, usage.txt e o arquivo DEX compilado.

Verificando Identificadores DEX Brutos vs. Estados de Relatório Desofuscados com apkanalyzer

Ao usar o Google Android Developers APK Analyzer CLI oficial para inspecionar seu APK de lançamento, distinga entre identificadores DEX compilados brutos e nomes desofuscados por mapeamento:

  • Inspecionando Nomes DEX Compilados Brutos (Sem --proguard-mappings): Quando você passa --proguard-mappings mapping.txt para o apkanalyzer, a ferramenta traduz nomes DEX ofuscados (como a.b.c) de volta para seus nomes de origem originais para exibição. Portanto, ver um nome de classe original quando --proguard-mappings está habilitado não prova que o símbolo escapou da renomeação em classes.dex. Execute apkanalyzer dex packages sem --proguard-mappings primeiro. Como o repacotamento de classes pelo R8 também pode mover classes para caminhos de pacote achatados, não confie apenas em um grep de pacote; inspecione a árvore de classes DEX bruta, verifique nomes de classes de pontos de entrada de reflexão, verifique nomes de métodos JNI e assinaturas de descritores, e cruze o mapping.txt para confirmar que métodos e campos necessários não foram renomeados.
  • Inspecionando Estados de Retenção R8 (Com --proguard-mappings, --proguard-seeds e --proguard-usages): Em uma segunda passagem, forneça todos os três arquivos de relatório R8 para reconstruir as flags de retenção (k para mantido por uma regra de seed, x para removido durante a redução, r para referenciado e d para definido) em cada pacote (P), classe (C), método (M) e campo (F):
# Execute a partir do diretório raiz do projeto Android (ajuste o caminho da variante para sabores de produto, ex: prodRelease)

# 1. (AGP 9.3+) Execute o analisador de configuração R8 independente para verificação rápida de regras, então construa o APK de lançamento
./gradlew :app:analyzeReleaseR8Config
./gradlew :app:assembleRelease

# 2. Inspecione a configuração R8 mesclada, seeds correspondentes e relatórios de uso removido
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. Inspecione identificadores DEX compilados BRUTOS (SEM --proguard-mappings)
# Também inspecione a árvore DEX bruta completa e cruze o mapping.txt caso o R8 tenha repacotado classes
apkanalyzer dex packages app/build/outputs/apk/release/app-release.apk | grep -i "opoinstall"

# 3B. Inspecione flags de estado de retenção R8 (k = mantido, x = removido, r = referenciado, d = definido) COM arquivos de relatório 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 alinhamento de página de 16 KB (se arquivos .so nativos estiverem presentes) e assinaturas criptográficas de APK
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 Solução de Problemas de Atribuição em Build de Lançamento

Diagnosticando Falhas de Atribuição Silenciosas e de Deep-Link em Builds Android Minificadas

Ao solucionar discrepâncias de atribuição que ocorrem apenas em builds de lançamento minificadas, use a matriz de diagnóstico abaixo para isolar a causa raiz:

Sintoma de Lançamento Observado Causa Raiz de Build / R8 Provável Passo de Verificação de Diagnóstico Ação Corretiva
Callback getInstallParam ou handleDeepLink nunca dispara em build de lançamento Classes de ponto de entrada do SDK, construtores reflexivos <init>() ou métodos ResultCallBack foram removidos ou renomeados devido a uma incompatibilidade de pacote Verifique seeds.txt e usage.txt; inspecione a saída bruta de apkanalyzer dex packages (sem --proguard-mappings) e mapping.txt Inspecione o .jar / .aar com jar tf para encontrar os caminhos de classe exatos e sensíveis a maiúsculas e adicione regras -keep com escopo, incluindo <init>(...);
Callback dispara, mas parâmetros personalizados OpoData são analisados como vazios ou nulos OpoData foi protegido apenas com -keepclassmembers (permitindo que a própria classe fosse removida) ou metadados de Signature genéricos foram perdidos Verifique usage.txt e mapping.txt para OpoData e verifique configuration.txt para -keepattributes Signature Use -keep class ... (se OpoData for instanciado reflexivamente) com <init>(...); <fields>; e mantenha -keepattributes Signature
UnsatisfiedLinkError ou callback nativo ausente quando o SDK carrega a biblioteca .so Downcalls RegisterNatives registrados dinamicamente foram reduzidos ou métodos de alvo de upcall C/C++ para Java foram renomeados pelo R8 Inspecione o logcat para o símbolo JNI ausente exato e verifique apkanalyzer dex packages bruto e mapping.txt para a classe Java alvo Use -keep (ou -keepclassmembers se a classe de ponte for instanciada diretamente em Java) com includedescriptorclasses em JNI upcall e alvos RegisterNatives
Pacote de canal Android exportado falha na instalação ou descarta estatísticas de canal A modificação do APK pós-exportação invalidou a assinatura do APK ou sobrescreveu metadados de canal embutidos Execute zipalign -c -P 16 -v 4 e apksigner verify --verbose no APK de canal exportado e teste em um dispositivo físico Conclua toda a minificação R8, proteção, alinhamento e assinatura no APK base antes de enviar para o console; verifique assinatura e atribuição após a exportação

Perguntas Frequentes (FAQ)

Como excluo um SDK móvel da ofuscação do ProGuard e R8 no Android?
Primeiro, inspecione seu arquivo `.aar` (`/proguard.txt` e `classes.jar/META-INF/com.android.tools/`) ou `.jar` (`/META-INF/proguard/` e `/META-INF/com.android.tools/`) e verifique o `configuration.txt` para ver se as regras do ProGuard do consumidor já estão incluídas. Se forem necessárias regras manuais, adicione diretivas `-keep` e `-keepclassmembers` de escopo restrito direcionadas às classes de entrada exatas do SDK (sensíveis a maiúsculas), construtores reflexivos (`<init>(...);`), interfaces de callback, alvos JNI e `<fields>;` do modelo, pareados com `-keepattributes Signature, InnerClasses, EnclosingMethod, *Annotation*`.
Como verifico se minhas classes de SDK Android foram excluídas da ofuscação do R8?
Ative `-printseeds`, `-printusage` e `-printconfiguration` em sua configuração R8 (ou execute `./gradlew :app:analyzeReleaseR8Config` no AGP 9.3+) e construa seu APK de lançamento. Verifique se suas classes e construtores de SDK alvo aparecem em `seeds.txt` e não aparecem em `usage.txt`. Em seguida, execute `apkanalyzer dex packages app-release.apk` **sem** `--proguard-mappings` e cruze com o `mapping.txt` para confirmar que os símbolos DEX compilados brutos retêm seus nomes originais não ofuscados e assinaturas de método, então execute o `apkanalyzer` uma segunda vez com `--proguard-mappings`, `--proguard-seeds` e `--proguard-usages` para auditar as flags de estado de retenção (`k`, `x`, `r`, `d`).
Por que as estatísticas do pacote de canal Android param de funcionar após a proteção ou pós-processamento do APK?
Quando você exporta um pacote de canal Android do console de atribuição, os metadados de identificação de canal são embutidos no APK exportado. Se você executar posteriormente ferramentas de reforço de APK, empacotamento ou reassinatura de terceiros nesse APK de canal exportado, a etapa de pós-processamento pode invalidar a assinatura criptográfica do APK ou sobrescrever os metadados de canal do fornecedor. Sempre conclua a minificação R8, proteção, `zipalign` e assinatura em seu APK base *antes* de enviá-lo para o console, e verifique os APKs de canal exportados com `apksigner verify --verbose`.

Resumo e Checklist de Verificação de Build de Lançamento

Compreender como excluir SDKs móveis da ofuscação do ProGuard e R8 do Android é essencial para evitar regressões silenciosas de atribuição ao transicionar de testes de debug para builds de lançamento de produção. Ao verificar regras de consumidor incluídas, escrever diretivas -keep de escopo restrito que correspondem aos caminhos de classe exatos (sensíveis a maiúsculas) dentro do seu arquivo SDK, respeitando a ordem de build pré-upload para pacotes de canal Android e cruzando os arquivos de saída de diagnóstico do R8 com o bytecode DEX bruto, os engenheiros Android podem manter a redução de código completa sem quebrar o deep linking diferido ou a restauração de parâmetros.

Antes de publicar uma build de lançamento Android minificada no Google Play ou exportar pacotes de canal, execute este checklist de verificação de quatro etapas:

  • Inspecione o Arquivo SDK e Configuração Mesclada: Verifique seu arquivo SDK baixado (como a v2.1.0) com jar tf ou unzip -l para inspecionar caminhos de classe exatos e regras de consumidor incluídas, e execute ./gradlew :app:analyzeReleaseR8Config (no AGP 9.3+) ou revise configuration.txt.

  • Cruzamento de Relatórios R8 e Identificadores DEX Brutos: Confirme que os pontos de entrada do SDK necessários, construtores reflexivos <init>(), campos de modelo OpoData, métodos ResultCallBack e assinaturas JNI aparecem em seeds.txt, estão ausentes de usage.txt e retêm seus nomes originais na saída bruta de apkanalyzer dex packages (executado sem --proguard-mappings) e mapping.txt.

  • Aplique Minificação Pré-Upload e Verifique Assinaturas de APK de Canal: Conclua toda a minificação R8, qualquer proteção de terceiros, zipalign (zipalign -c -P 16 -v 4) e assinatura de APK no APK base antes de enviá-lo para exportar pacotes de canal Android, e execute apksigner verify --verbose nos pacotes exportados.

  • Execute um Teste de Fumaça de Lançamento em Dispositivo Físico: Instale o APK de lançamento assinado e minificado em um dispositivo Android real e verifique tanto a instalação de parâmetros H5 (getInstallParam) quanto o despertar com um clique (handleDeepLink).

As equipes de engenharia podem registrar um aplicativo e enviar seu APK base ofuscado no console de desenvolvedor OpoInstall para testar a exportação de pacotes de canal e verificar a restauração de parâmetros de build de lançamento.

Materiais Relacionados

Share this article