O sistema de build da plataforma Android (Soong) executa o compilador R8 em
destinos que compilam bytecode DEX, incluindo apps Android (android_app), testes
(android_test) e bibliotecas Java instaláveis com compile_dex: true (como
services.jar), para reduzir, otimizar e remover código e recursos não utilizados. Em
bibliotecas estáticas (android_library e java_library estático), o Soong não
executa o R8 diretamente, mas usa o bloco de propriedades optimize para anexar e propagar
regras de manutenção do consumidor (export_proguard_flags_files: true) para destinos DEX
que os vinculam de forma estática.
Para engenheiros de plataforma que criam imagens de sistema ou pacotes de fornecedores, eliminar regras de retenção muito amplas oferece benefícios diretos à integridade do sistema:
- Pegada menor da partição do sistema: o R8 remove classes, métodos e recursos inativos antes de empacotar APKs e JARs em
/system,/system_ext,/producte/vendor. - Artefatos compilados menores: menos métodos DEX significam arquivos
.odexe.vdexmenores gerados pelodex2oatdurante a compilação no momento do build ou no dispositivo. - Menor pressão na memória de execução: como o Android mapeia
.odex,.vdexe tabelas de recursos de APK na memória do processo usando chamadasmmappaginadas por demanda, binários menores reduzem falhas de página importantes durante a inicialização do app e diminuem o código residente e a memória de recursos em todos os processos. Para mais informações sobre como o código do app compilado afeta a memória do dispositivo, consulte O código do app é memória. - Otimização mais eficaz de todo o programa: restrições de manutenção restritas permitem que o R8 faça inlining de métodos, desvirtualize interfaces e chamadas virtuais, remova campos não usados e propague constantes entre classes.
Configurar a otimização do R8 no Soong
Em arquivos Android.bp, configure a otimização do R8 usando o bloco de propriedades optimize
em destinos android_app e java_library instaláveis (ou em
módulos android_library e java_library estáticos para exportar regras de
preservação do consumidor):
android_app {
name: "MySystemApp",
srcs: ["src/**/*.java"],
optimize: {
obfuscate: true,
shrink_resources: true,
},
}
A tabela a seguir resume as propriedades optimize mais comuns em Soong (definidas em build/soong/java/dex.go):
| Propriedade | Descrição |
|---|---|
enabled |
Controla se o R8 é executado no destino. O padrão é true
para todas as metas de android_app.
|
shrink |
Controla o tree shaking para remover classes, campos e métodos inacessíveis. O padrão é true para destinos android_app (false para java_library independentes e módulos de teste, que transmitem -dontshrink, a menos que sejam definidos explicitamente).
|
optimize |
Controla otimizações de bytecode, como inlining de métodos, fusão de classes, propagação de constantes e remoção de ramificações mortas. O padrão é
true para destinos android_app (controlados pela
flag de build de lançamento RELEASE_R8_OPTIMIZE_BY_DEFAULT).
|
obfuscate |
Controla a minimização e a renomeação de identificadores. O padrão é
false para compatibilidade histórica, mas defina como
true sempre que possível para reduzir o tamanho do DEX e desbloquear
otimizações mais profundas (consulte
Ativar a ofuscação sempre que possível).
|
shrink_resources |
Remove recursos não utilizados (entradas res/) do APK
empacotado após a redução de código. O valor padrão é false.
|
optimized_shrink_resources |
Executa o pipeline integrado de redução de código e recursos do R8 para que o R8 rastreie
referências de código e recursos em conjunto em uma única transmissão. Quando shrink_resources: true é definido, o padrão é RELEASE_USE_OPTIMIZED_RESOURCE_SHRINKING_BY_DEFAULT (true em builds de plataforma padrão).
|
proguard_flags_files |
Lista arquivos .flags ou .pro específicos do módulo
que contêm regras de manutenção personalizadas. Evite adicionar arquivos personalizados quando anotações ou padrões forem suficientes.
|
export_proguard_flags_files |
Propaga o proguard_flags_files desta biblioteca para
módulos downstream que dependem dela de forma estática.
|
trace_references_from |
Lista destinos complementares da biblioteca Java cujas referências de bytecode a esse destino o R8 rastreia e retém automaticamente. |
Ative a ofuscação sempre que possível
No Soong, obfuscate usa false por padrão para compatibilidade histórica, mas você
deve definir explicitamente obfuscate: true em android_app e destinos DEX
independente sempre que possível:
- Pegada menor do DEX e do
.vdex: renomear pacotes, classes, campos e métodos para identificadores curtos (a,b) reduz o pool de strings do DEX (string_idsestring_data_item) e os descritores de tipo. Como o Android mapeia arquivos.vdexe.odexna memória do processo, tabelas de símbolos menores reduzem diretamente o tamanho da partição do sistema e o consumo de memória do ambiente de execução. - Otimizações mais profundas de todo o programa: permitir que o R8 renomeie identificadores desbloqueia a fusão de classes, o nivelamento de pacotes e a remoção de duplicação de classes sintéticas em pacotes que o R8 precisaria pular para evitar conflitos de nomes.
- Simbolização completa do stack trace: ativar a ofuscação no Soong não
reduz a capacidade de depuração. Para cada destino compilado pelo R8, o Soong emite um
arquivo de mapeamento
proguard_dictionaryno diretório intermediário do módulo, empacota todos os dicionários de módulos no artefatoproguard-dict.zipdo build, incorpora um hash de mapeamento (--map-id-template) no cabeçalho DEX e reescreve os atributos da classeSourceFile(--source-file-template) para queretracepossa simbolizar rastreamentos de pilha automaticamente.
Mantenha obfuscate: false (ou preserve explicitamente os nomes de APIs públicas) apenas para:
- Bibliotecas compartilhadas no bootclasspath ou no classpath
system_server: módulos (java_libraryoujava_sdk_library) que expõem uma superfície de API externa vinculada dinamicamente por outros módulos durante a execução (como destinosframework.jar,services.jarou<uses-library>) precisam manterobfuscate: falseou preservar explicitamente a superfície da API usando regras@KeepForApiou-keep(junto comprotect_api_surface: truepara destinos de bootclasspath) para que os chamadores compilados em relação aos stubs possam resolver nomes de classes e membros durante a execução.
Antes de ativar o obfuscate: true em um aplicativo, verifique os seguintes pré-requisitos de migração:
- Dependências externas do APK de teste: se um APK
android_testexterno definirinstrumentation_for: "MyApp"e invocar diretamente classes ou métodos internos deMyApp, renomear esses símbolos vai causarNoSuchMethodErrorouNoClassDefFoundErrorno tempo de execução do teste. Prefira estruturar o teste como um APK de autoinstrumentação que vinculaMyApp.implde forma estática, anote hooks de teste com@VisibleForTestingou configuretrace_references_from(consulte Etapa 2: migrar regras de manutenção orientadas por teste) para que símbolos internos necessários para testes sejam preservados ao ofuscar o restante do app. - Reflexão baseada em strings e JNI: se um módulo pesquisar classes, métodos ou campos por nomes de strings literais (
Class.forName,getDeclaredMethodou JNIFindClasseGetMethodID) sem regras ou anotações de preservação, a ofuscação vai renomear esses destinos e interromper as pesquisas de tempo de execução. (Observe que a reflexão não anotada também falha emshrink: trueeoptimize: true.) Anote esses pontos de entrada com o Guia para manter anotações (@UsesReflection,@UsedByReflectionou@UsedByNative) para que o R8 preserve os nomes e ofusque o restante do módulo.
Siga o princípio de zero flags personalizadas
O módulo de plataforma Android ideal não tem arquivos proguard.flags ou keep.xml personalizados. No build da plataforma Android, a grande maioria das regras de manutenção personalizadas é
redundante:
- Linhas de base da plataforma e AAPT2: a maioria dos pontos de entrada já é retida automaticamente pelas linhas de base globais da plataforma (como
@Keep, métodos JNInativee@VisibleForTesting) ou gerada pelo AAPT2 deAndroidManifest.xmle recursos de layout. Consulte Entender as regras de manutenção padrão da plataforma. - Alternativas segmentadas: quando os pontos de entrada precisam ser preservados, prefira
anotações no local da declaração (
keepanno,@VisibleForTesting), regras de biblioteca exportadas (export_proguard_flags_files: true) ou vinculação de teste estática em vez de arquivos.flagsseparados (consulte Auditar e migrar regras de retenção atuais).
Antes de adicionar ou reter um arquivo proguard.flags ou keep.xml personalizado, verifique se a regra já é processada por comparativos de mercado padrão ou se pode ser migrada para anotações de código.
Entender as regras de retenção padrão da plataforma
O Soong transmite automaticamente as seguintes regras de base globais para cada invocação do R8
em destinos de compilação DEX (android_app, android_test e
módulos java_library instaláveis), configurados em build/soong/java/dex.go:
build/make/core/proguard.flags: preserva classes e membros anotados com@com.android.internal.annotations.VisibleForTestingem todos os pacotes e com@VisibleForTesting(androidx.annotation.VisibleForTestingoucom.google.common.annotations.VisibleForTesting) nos pacotesandroid.**,com.android.**ecom.google.android.**. Também preserva@TestApi,@Keep(androidx.annotation,android.support.annotation,com.android.internal.annotations),@KeepForWeakReference,@WeaklyReferencedCallbacke@dalvik.annotation.optimization.**.build/make/core/proguard_basic_keeps.flags: preserva atributosSourceFilepara rastreamentos de pilha, anotações de visibilidade de tempo de execução (RuntimeVisible*Annotations), atributosExceptionseAnnotationDefault, métodosnative, membrosSerializable, métodos@JavascriptInterface, construtoresThrowable(String), camposParcelable$CREATORe camposMessageLitedo protobuf.build/make/core/proguard/kotlin.flags: silencia avisos inofensivos para meta-anotações específicas do Kotlin (kotlin.Metadataekotlin.annotation.{AnnotationRetention,AnnotationTarget,Retention,Target}) e remove as anotaçõesDebugMetadatado Kotlin em builds de lançamento.build/make/core/proguard/checknotnull.flags: substitui chamadas comuns de auxiliar de verificação de nulo (com.google.common.base.Preconditions.checkNotNulledagger.internal.Preconditions.checkNotNull*) por verificações de nulo de bytecode concisas. Esse arquivo omite intencionalmenteObjects.requireNonNullpara preservar mensagens de exceção explícitas em limites de API de framework.build/make/core/proguard/enumvalues.flags: retém os métodosvaluesevalueOfem tiposenum, a menos que sejam desativados pela configuração de build.- Regras geradas automaticamente pelo AAPT2: o AAPT2 inspeciona arquivos
AndroidManifest.xmlmesclados, XML de layout e XML de preferências para gerar regras de manutenção exatas para cadaActivity,Service,BroadcastReceiver,ContentProvider,BackupAgent,Application, subclasseViewpersonalizada, subclassePreferencee métodoandroid:onClickXML registrados.
Auditar e migrar regras de retenção atuais
Ao auditar arquivos proguard.flags ou keep.xml em um repositório
de plataforma, avalie cada regra em ordem de acordo com a seguinte hierarquia
de quatro etapas:
Etapa 1: excluir regras redundantes ou obsoletas
Exclua regras que já são cobertas por valores de referência globais, manifesto do AAPT2 e regras de layout ou padrões do R8, bem como regras que fazem referência a classes ou pacotes que não existem mais.
Se todas as regras em um arquivo .flags forem redundantes, exclua o arquivo e remova proguard_flags_files de Android.bp. Se o bloco optimize restante apenas
reafirmar as configurações padrão, remova o bloco redundante e formate o arquivo de build
com bpfmt -w Android.bp. Ao limpar um pacote, verifique se os módulos
complementares em subdiretórios (como variantes de destino do Kotlin) referenciam o mesmo
arquivo de flags e atualize-os juntos.
Etapa 2: migrar regras de manutenção orientadas por testes
Mova as regras de manutenção orientadas por testes para fora dos arquivos de produção personalizados .flags usando um dos seguintes padrões:
Vincule a biblioteca de implementação aos testes (
static_libs): quando umandroid_testexercita detalhes de implementação internos ou privados do pacote de um app, a arquitetura de plataforma recomendada é colocar os arquivos de origem do app em umandroid_library(MyApp.impl) e vincular estaticamenteMyApp.implaMyAppeMyAppTests: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"], }Esse padrão de três módulos permite que o
MyAppTestsseja executado como um teste de auto-instrumentação com acesso total a classes internas, permite que oMyAppative oobfuscate: truelivremente sem interromper os testes, evita o envio de pontos de entrada somente de teste no APK de produção e elimina a recriação doMyAppquando o código de teste muda.Anotar hooks de teste com
@VisibleForTesting: se um APK de teste externo chamar um pequeno número de métodos ou construtores internos em um destinoandroid_appe a reestruturação em uma biblioteca.implfor inviável, anote essas declarações com@VisibleForTesting. A referência globalbuild/make/core/proguard.flagsretém itens@VisibleForTestingnos pacotesandroid.**,com.android.**ecom.google.android.**automaticamente sem arquivos.flagspersonalizados. Para módulos de fornecedores fora desses namespaces, use@UsedByReflectionou regras de biblioteca exportadas.Use
trace_references_fromem limites de biblioteca de plataforma: quando os testes não podem vincular estaticamente a implementação de destino (por exemplo, testes que exercem serviços do servidor do sistema ou JARs da plataforma, comoframework-connectivity), configuretrace_references_fromno módulo de destino apontando para um módulo complementar da biblioteca Java que contenha as origens de teste. O R8 rastreia e retém todas as classes e membros referenciados pelo bytecode complementar.
Etapa 3: exportar regras da biblioteca proprietária
Se um java_library ou android_library compartilhado exigir regras de permanência para a própria
reflexão interna ou callbacks JNI, defina as regras no destino da biblioteca e
defina 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 os android_app e destinos de biblioteca downstream que vinculam estaticamente
my-shared-library herdam essas regras automaticamente. Assim, os apps downstream não
precisam duplicá-las. Quando vários módulos em diretórios diferentes precisam
compartilhar um conjunto de regras que não está vinculado a uma única biblioteca de código, publique o arquivo .flags
usando um filegroup explícito em Android.bp para que os módulos possam referenciar
:my-shared-flags de maneira limpa em todos os limites de pacote. Para orientações gerais sobre
como criar regras de manutenção de consumidores de biblioteca sem restringir a otimização
de apps downstream, consulte Otimização para autores de bibliotecas.
Etapa 4: adicionar anotações às declarações no código-fonte
Quando uma classe, um método, um construtor ou um campo é acessado por reflexão ou
JNI e não é coberto pelo AAPT2 ou por valores de referência globais, substitua as regras -keep
desvinculadas em arquivos .flags por anotações de origem do
Guia para manter anotações (consulte a
referência do Javadoc keepanno):
@UsesReflection: prefira essa anotação quando você controla o código que realiza a reflexão. Coloque-o no site de chamada de reflexão para declarar quais classes, métodos ou campos de destino são acessados dinamicamente. Como@UsesReflectioncodifica automaticamente uma condição prévia de que o próprio site de chamada anotado é acessível, o R8 remove o autor da chamada e o destino reflexivo se o site de chamada não for usado.@UsedByReflection: coloque em classes, métodos, campos ou construtores que são instanciados ou invocados de forma reflexiva por código ou bibliotecas externas, como classes carregadas comClass.forNamede chavesBundleouSettings, ou classes de plug-in carregadas em carregadores de classes dinâmicos. Especifiquekind(comoKeepItemKind.CLASS_AND_METHODS),preconditions(@KeepCondition) e restrições de parâmetro para que o R8 mantenha apenas o contrato reflexivo exato e ainda possa otimizar ou remover membros não utilizados da classe. Não adicione@UsedByReflectiona componentes registrados no manifesto, comoJobServiceouBroadcastReceiver, porque o AAPT2 já os mantém.@UsedByNative: coloque em métodos ou campos acessados do código JNI C ou C++ usandoGetMethodID,GetStaticMethodIDouGetFieldID.@KeepForApi: coloque em classes ou membros da API de biblioteca que precisam permanecer intactos quando uma biblioteca é reduzida antes da distribuição.@Keep(androidx.annotation.Keep): use como substituto quandokeepannonão for aplicável. Aplicar@Keepa uma classe retém a classe e todos os membros dela incondicionalmente. Aplicar@Keepa um método ou campo funciona como um ponto de entrada incondicional que retém o membro e a classe que o contém, mesmo que a classe nunca seja instanciada, enquantokeepannoexpressa acessibilidade condicional.
Para usar anotações keepanno do R8 em um módulo do Soong:
Adicione
"keepanno-annotations"alibsemAndroid.bp:libs: [ "keepanno-annotations", ],Importe
com.android.tools.r8.keepanno.annotations.*e anote a declaração ou o site de chamada no código-fonte Java ou 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) { // ... } }O R8 traduz as anotações
keepannodiretamente para o modelo interno de regra de manutenção e remove as anotações da saída DEX final, sem adicionar sobrecarga de bytecode de tempo de execução.
Evitar erros comuns
As seções a seguir descrevem erros comuns de proguard.flags e keep.xml
no código da plataforma e como corrigi-los.
Regras de retenção de componentes gerais
# Don't do this:
-keep class * extends android.app.Activity
-keep class * extends android.app.Service
-keep class * extends android.content.BroadcastReceiver
- Por que isso prejudica a performance: essa regra é completamente redundante com o AAPT2.
Como ele usa um caractere curinga sem verificar se o componente está
registrado no
AndroidManifest.xmlmesclado final, ele força o R8 a reter todas as subclassesActivity,ServiceouBroadcastReceiverencontradas em qualquer lugar no classpath, incluindo componentes de biblioteca não utilizados e atividades de depuração desativadas. - Correção recomendada: exclua a regra. O AAPT2 inspeciona o manifesto integrado e
gera regras
-keepexatas para componentes registrados.
Regras de manutenção de manipuladores de cliques XML amplas
# Don't do this:
-keepclassmembers class * {
public void *(android.view.View);
}
- Por que isso prejudica o desempenho: manter todos os métodos
public void *(View)em todas as classes do módulo impede que o R8 remova ou faça inlining de qualquer método que aceite um único parâmetroView. - Correção recomendada: exclua a regra. O AAPT2 verifica arquivos XML de layout e
gera regras de manutenção direcionadas para métodos referenciados por atributos
android:onClick.
Regras de getter e setter de visualização ampla
# 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 que isso prejudica a performance: essa regra força o R8 a reter todos os getters e
setters em todas as subclasses
Viewno app e em todas as bibliotecas vinculadas (incluindo as bibliotecas AndroidX e Material), bloqueando a remoção de métodos inativos e a incorporação no código da interface. - Correção recomendada: exclua a regra. O AAPT2 já preserva construtores
para classes
Viewpersonalizadas extraídas de arquivos XML de layout. Se o código animar uma propriedade de visualização usando nomes de strings reflexivas, comoObjectAnimator.ofFloat(view, "translationZ", ...), substitua o nome da string por uma referência de propriedade tipada (View.TRANSLATION_Zou uma implementação personalizada deFloatPropertyouIntProperty) para evitar a reflexão por completo. Se não for possível evitar o acesso reflexivo à propriedade, anote o getter ou setter específico com@UsedByReflection.
Desativações de otimização ou ofuscação abrangentes
# Don't do this in proguard.flags:
-dontoptimize
-dontshrink
-dontobfuscate
- Por que isso prejudica a performance: colocar
-dontoptimizeou-dontshrinkem um arquivo.flagssubstitui silenciosamente as configuraçõesAndroid.bpdo módulo e desativa as transmissões de otimização em todo o destino. Pior ainda, se uma biblioteca exportar um arquivo.flagsque contenha-dontoptimize, ela vai desativar a otimização do R8 para todos osandroid_appdownstream que vinculam a biblioteca. - Correção recomendada: remova
-dontoptimize,-dontshrinke-dontobfuscatedos arquivos.flags. Controle o comportamento de otimização explicitamente emAndroid.bpusando o blocooptimize(shrink,optimize,obfuscate) na meta folha.
Flags de diagnóstico em regras confirmadas
# Don't do this in proguard.flags:
-verbose
-printmapping proguard.map
-printusage usage.txt
-printconfiguration config.txt
- Por que isso prejudica o build: flags de diagnóstico inundam os registros de build ou tentam gravar arquivos de saída em caminhos locais durante builds do Soong em sandbox.
- Correção recomendada: exclua essas flags dos arquivos
.flagsconfirmados. O Soong grava automaticamente saídas de mapeamento e uso do R8, comoproguard_dictionaryeproguard_usage.zip, no diretório intermediário do módulo emout/soong/.intermediates/.
Regras de retenção de produção para código de teste
- Por que isso prejudica a performance: adicionar regras
-keeppersonalizadas apenas para acesso de teste força esse código a permanecer não ofuscado e retido em builds de produção. Embora a anotação de métodos com@VisibleForTestingou a configuração detrace_references_fromevite a manutenção manual de regras-keep, esses símbolos ainda são enviados no binário de produção. - Correção recomendada: para manter os pontos de entrada somente de teste completamente fora do
APK de produção, estruture o aplicativo em uma biblioteca
.imple vincule-o a um APK de teste com autoinstrumentação usandostatic_libs, conforme descrito em Etapa 2: migrar regras de manutenção orientadas por testes.
Curingas genéricos de recursos em 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 que isso prejudica o desempenho: os curingas gerais retêm todos os recursos no
tipo correspondente em todo o módulo e suas dependências, prejudicando a redução de recursos (
shrink_resources: true) e aumentando o APK e a tabelaresources.arscmapeada. - Correção recomendada:
- Prefira referências de recursos estáticos em vez de
keep.xml: evite usar o métodoResources.getIdentifierpara pesquisar dinamicamente um conjunto limitado de recursos, como strings de experimentos numeradas ou elementos gráficos temáticos. Em vez disso, use uma instruçãoswitchde tempo de compilação ou mapeie constantes estáticasR.id,R.stringouR.drawable. As referências estáticas permitem que o R8 e o AAPT2 rastreiem os recursos ativos exatos, eliminem a sobrecarga de pesquisa de strings em tempo de execução e removam a necessidade dekeep.xml. - Listar nomes de recursos específicos: se for necessário fazer uma pesquisa dinâmica, como
por um leitor de licenças de terceiros, liste os identificadores de recursos exatos em
tools:keep(por exemplo,tools:keep="@raw/third_party_licenses"). - Exclua arquivos
keep.xmlredundantes: se os recursos listados já forem referenciados de forma estática no código (R.raw.foo) ou XML (@raw/foo), o redutor os manterá automaticamente. Assim, você poderá excluirres/raw/keep.xml.
- Prefira referências de recursos estáticos em vez de
Verificar e auditar mudanças nas regras
Sempre que você remover ou restringir as regras de retenção em proguard.flags ou keep.xml,
verifique se o módulo é criado corretamente, passa nos testes de unidade e instrumentação
e retém todos os pontos de entrada necessários.
Criar e testar módulos
Crie o módulo de maneira limpa para verificar se o R8 e o AAPT2 são concluídos sem avisos de referência ausente:
m <MODULE_NAME>Execute testes de unidade e instrumentação para o módulo usando
atest:atest <TEST_MODULE_NAME>Para apps do sistema, serviços privilegiados ou módulos dependentes de hardware, execute testes de instrumentação e de UI em dispositivos físicos de destino ou no laboratório de testes do dispositivo para exercitar caminhos de reflexão de tempo de execução, vinculações de IPC e inflação de recursos.
Inspecionar diferenças de DEX e recursos
Compare o APK ou JAR compilado antes e depois das mudanças nas regras para confirmar que o R8 remove código e recursos inativos sem remover os pontos de entrada esperados:
Use
apkanalyzeroudexdumppara inspecionar classes, métodos e campos retidos nos arquivos APK ou DEX de saída:apkanalyzer dex packages $OUT/system/priv-app/<APP_NAME>/<APP_NAME>.apkAo modificar
keep.xmlou ativarshrink_resources, useaapt2 dump resourcespara verificar se o build remove recursos não utilizados e mantém os necessários:aapt2 dump resources $OUT/system/priv-app/<APP_NAME>/<APP_NAME>.apk
Analisar o raio de manutenção e a subsunção de regras com o R8
O compilador R8 de código aberto inclui um analisador de raio de preservação (também exposto no Android Studio e no Gradle como o R8 Configuration Analyzer) que mede o impacto exato de cada regra de preservação durante a compilação. O Soong integra esse analisador
diretamente ao build da plataforma Android (configurado em
build/soong/java/dex.go).
Para analisar regras de manutenção em um módulo de plataforma ou em todo um build:
Execute o build com a variável de ambiente
R8_DUMP_KEEP_RADIUS=true:R8_DUMP_KEEP_RADIUS=true m <MODULE_NAME>Quando
R8_DUMP_KEEP_RADIUS=trueé definido, o Soong instrui o R8 a registrar métricas de regras de preservação em um arquivor8keepradius.pbintermediário para cada módulo compilado emout/soong/.intermediates/. Para cada regra de manutenção e anotaçãokeepanno, o R8 registra:- Raio de retenção imediata: as classes, os campos e os métodos exatos retidos pela regra, além das restrições específicas que ela impõe contra redução, otimização ou ofuscação.
- Subsunção de regras: quais outras regras de retenção ou anotações já retêm os mesmos itens. Se uma regra personalizada for completamente subsumida por uma regra do AAPT2 ou de base global, ela poderá ser excluída com segurança.
- Regras globais e em todo o pacote: regras que usam caracteres curinga amplos em todo o pacote ou aplicam diretivas de configuração global.
Converta a saída
r8keepradius.pbem um relatório HTML interativo usandoKeepRadiusHtmlReportGenerator(agrupado emprebuilts/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_reportsQuando um diretório é fornecido, o
KeepRadiusHtmlReportGeneratorpercorre todos os arquivos*keepradius*.pb, gera um relatório HTML para cada módulo e criaout/keep_radius_reports/keepradius.htmlresumindo as contagens de itens ativos, mantidos e as regras de manutenção de maior raio em todo o build. Para mais informações sobre como interpretar as pontuações de redução, otimização e ofuscação no relatório gerado, consulte Usar o analisador de configuração do R8.
O Soong também é compatível com duas variáveis de ambiente de diagnóstico do R8 adicionais em
build/soong/java/dex.go:
R8_DUMP_INPUT=true: gravar8inputs.zipno diretório intermediário do módulo que contém todos os JARs de entrada e de biblioteca, além das configurações mescladas do ProGuard para reprodução independente do R8.R8_DUMP_PERFETTO_TRACE=true: gravar8trace.ptraceno diretório intermediário do módulo para inspecionar transmissões de compilação do R8 no Perfetto.
Validar regras de consumidor de biblioteca
Quando uma exportação java_library ou android_library mantém regras para consumidores
downstream (export_proguard_flags_files: true), essas regras não podem incluir
flags globais que alteram ou desativam a otimização para o app consumidor.
O R8 oferece um analisador de regras de manutenção de código aberto
(ProcessKeepRules), exposto no build da plataforma Android
como a ferramenta host process-keep-rules (definida em
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>
A ferramenta process-keep-rules analisa cada arquivo de configuração e falha com diagnósticos de arquivo e linha se encontrar diretivas não permitidas nas regras do consumidor da biblioteca. As diretivas proibidas incluem otimização global, redução,
ou desativações de ofuscação (como -dontoptimize ou -dontshrink), reempacotamento
de pacotes e flags de modificação de acesso, flags de diagnóstico ou mapeamento e
-keepattributes no nível do app.
Auditar árvores de origem com pgaudit.py
Para auditar diretórios de origem não criados junto com os analisadores de tempo de build do R8, a
árvore da plataforma Android inclui o script pgaudit.py em
build/make/core/proguard/tools/. Essa ferramenta de análise estática verifica arquivos Android.bp, proguard.flags e keep.xml em um repositório para sinalizar regras já cobertas por AAPT2 ou bases de referência de plataforma global, caracteres curinga amplos, substituições de -dontoptimize e regras de manutenção somente para teste:
# 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
As equipes de plataforma e OEM podem combinar pgaudit.py para triagem rápida da árvore de origem, process-keep-rules para validar regras de biblioteca exportadas e R8_DUMP_KEEP_RADIUS=true com KeepRadiusHtmlReportGenerator para medir o raio de retenção exato de classe, campo e método de cada regra em um build.