Notícias sobre produtos
Configurar e resolver problemas com regras de permanência do R8
Leitura de 7 minutos
No desenvolvimento moderno para Android, entregar um aplicativo pequeno, rápido e seguro é uma expectativa fundamental do usuário. A principal ferramenta do sistema de build do Android para isso é o otimizador R8 , o compilador que processa a remoção de código morto e recursos para redução, renomeação ou minificação de código e otimização de apps.
Ativar o R8 é uma etapa essencial na preparação de um app para lançamento, mas exige que os desenvolvedores forneçam orientação na forma de "regras de manutenção".
Depois de ler este artigo, assista o vídeo da Semana de destaque da performance no YouTube sobre como ativar, depurar e resolver problemas do otimizador R8.
Por que as regras de retenção são necessárias
A necessidade de escrever regras de manutenção decorre de um conflito principal: o R8 é uma ferramenta de análise estática, mas os apps Android geralmente dependem de padrões de execução dinâmica, como reflexão ou chamadas dentro e fora do código nativo usando a JNI (Java Native Interface).
O R8 cria um gráfico de código usado analisando chamadas diretas. Quando o código é acessado de forma dinâmica, a análise estática do R8 não consegue prever isso. Ele identifica o código como não utilizado e o remove, causando falhas de execução.
Uma regra de manutenção é uma instrução explícita ao compilador R8, que diz: "Esta classe, método ou campo específico é um ponto de entrada que será acessado dinamicamente durante a execução. Você precisa manter essa informação, mesmo que não encontre uma referência direta a ela".
Consulte o guia oficial para mais detalhes sobre as regras de retenção.
Onde escrever regras do Keep
As regras de manutenção personalizadas de um aplicativo são gravadas em um arquivo de texto. Por convenção, esse arquivo é chamado de proguard-rules.pro e fica localizado na raiz do app ou módulo de biblioteca. Esse arquivo é especificado no tipo de build release do arquivo build.gradle.kts do módulo.
release {
isShrinkResources = true
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
)
}Use o arquivo padrão correto
O método getDefaultProguardFile importa um conjunto padrão de regras fornecidas pelo SDK do Android. Se você usar o arquivo errado, o app poderá não ser otimizado. Use proguard-android-optimize.txt. Esse arquivo fornece as regras keep padrão para componentes Android padrão e ativa as otimizações de código do R8. O proguard-android.txt desatualizado só fornece as regras de manutenção, mas não ativa as otimizações do R8.
Como esse é um problema grave de desempenho, vamos começar a alertar os desenvolvedores sobre o uso do arquivo errado a partir da atualização de recursos 3 do Android Studio Narwhal. E, a partir da versão 9.0 do Plug-in do Android para Gradle, não vamos mais oferecer suporte ao arquivo proguard-android.txt desatualizado. Por isso, faça upgrade para a versão otimizada.
Como escrever regras de retenção
Uma regra de retenção consiste em três partes principais:
- Uma opção como
-keepou-keepclassmembers - Modificadores opcionais, como
allowshrinking - Uma especificação de classe que define o código a ser correspondido
Para conferir a sintaxe completa e exemplos, consulte as orientações para adicionar regras de retenção.
Antipadrões de regras de retenção
É importante conhecer as práticas recomendadas e os antipadrões. Esses antipadrões geralmente surgem de mal-entendidos ou atalhos de solução de problemas e podem ser catastróficos para a performance de um build de produção.
Opções globais
Essas flags são alternâncias globais que nunca devem ser usadas em um build de lançamento. Eles são apenas para depuração temporária para isolar um problema.
Usar -dontotptimize desativa as otimizações de desempenho do R8, o que deixa o app mais lento.
Ao usar -dontobfuscate, você desativa toda a renomeação, e -dontshrink desativa a remoção de código morto. Ambas as regras globais aumentam o tamanho do app.
Evite usar essas flags globais em um ambiente de produção sempre que possível para uma experiência do usuário mais eficiente.
Regras de retenção muito amplas
A maneira mais fácil de anular os benefícios do R8 é escrever regras de manutenção muito abrangentes. As regras keep, como a abaixo, instruem o otimizador R8 a não reduzir, não ofuscar e não otimizar nenhuma classe neste pacote ou nenhum dos subpacotes dele. Isso remove completamente os benefícios do R8 para todo o pacote. Tente escrever regras mais restritas e específicas.
-keep class com.example.package.** { *;} // WIDE KEEP RULES CAUSE PROBLEMS
O operador de inversão (!)
O operador de inversão (!) parece ser uma maneira eficiente de excluir um pacote de uma regra. Mas não é tão simples assim. Por exemplo:
-keep class !com.example.my_package.** { *; } // USE WITH CAUTION
Você pode pensar que essa regra significa "não mantenha classes emcom.example.package", mas ela significa "mantenha todas as classes, métodos e propriedades em todo o aplicativo que não está em com.example.package". Se isso foi uma surpresa para você, verifique se há negações na sua configuração do R8.
Regras redundantes para componentes do Android
Outro erro comum é adicionar manualmente regras de preservação para Activities, Services ou BroadcastReceivers do app. Isso é desnecessário. O arquivo proguard-android-optimize.txt padrão já inclui as regras relevantes para que esses componentes padrão do Android funcionem sem precisar de configuração.
Além disso, muitas bibliotecas trazem as próprias regras de permanência. Portanto, não é necessário escrever suas próprias regras para isso. Se houver um problema com as regras de manutenção de uma biblioteca que você está usando, é melhor entrar em contato com o autor dela para saber qual é o problema.
Práticas recomendadas para regras de retenção
Agora que você sabe o que não fazer, vamos falar sobre práticas recomendadas.
Escrever regras de retenção restritas
As regras de retenção eficientes devem ser o mais restritas e específicas possível. Eles precisam preservar apenas o que é necessário, permitindo que o R8 otimize todo o resto.
| Regra | Qualidade |
|---|---|
| Baixa:mantém um pacote inteiro e os subpacotes dele. |
| Baixo:mantém uma classe inteira que provavelmente ainda é muito ampla. |
| Alto:apenas os métodos e propriedades relevantes de uma classe específica são mantidos. |
Usar ancestrais comuns
Em vez de escrever regras de retenção separadas para vários modelos de dados diferentes, escreva uma regra que tenha como destino uma classe de base ou interface comum. A regra abaixo informa ao R8 para manter todos os membros de classes que implementam essa interface e é altamente escalonável.
# Keep all fields of any class that implements SerializableModel -keepclassmembers class * implements com.example.models.SerializableModel { <fields>; }
Usar anotações para segmentar várias classes
Crie uma anotação personalizada (por exemplo, @Serialize) e use-a para "marcar" classes que precisam ter os campos preservados. Esse é outro padrão limpo, declarativo e altamente escalonável. Você também pode criar regras de permanência para anotações já existentes de frameworks que você está usando.
# Keep all fields of any class annotated with @Serialize -keepclassmembers class * { @com.example.annotations.Serialize <fields>; }
Escolher a opção certa do Keep
A opção "Manter" é a parte mais importante da regra. Escolher a opção errada pode desativar a otimização sem necessidade.
| Opção "Manter" | O que ele faz |
-keep | Impede que a classe e os membros mencionados na declaração sejam removidos ou renomeados. |
-keepclassmembers | Impede que os membros especificados sejam removidos ou renomeados, mas permite que a própria classe seja removida, apenas em classes que não são removidas de outra forma. |
-keepclasseswithmembers | Uma combinação: mantém a classe e os membros dela somente se todos os membros especificados estiverem presentes. |
Saiba mais sobre a opção de manter na documentação sobre opções de manutenção.
Permitir otimização com modificadores
Modificadores como allowshrinking e allowobfuscation flexibilizam uma regra -keep ampla, devolvendo o poder de otimização ao R8. Por exemplo, se uma biblioteca legada obrigar você a usar -keep em uma classe inteira, será possível recuperar alguma otimização permitindo a redução e a ofuscação:
# Keep this class, but allow R8 to remove it if it's unused and allow R8 to rename it. -keep,allowshrinking,allowobfuscation class com.example.LegacyClass
Adicionar opções globais para otimização adicional
Além das regras de manutenção, é possível adicionar flags globais ao arquivo de configuração do R8 para incentivar ainda mais a otimização.
-repackageclasses é uma opção eficiente que instrui o R8 a mover todas as classes ofuscadas para um único pacote. Isso economiza um espaço significativo no arquivo DEX ao remover strings de nomes de pacotes redundantes.
-allowaccessmodification permite que o R8 amplie o acesso (por exemplo, private para public) para ativar a inclusão inline mais agressiva. Agora, isso é ativado por padrão ao usar proguard-android-optimize.txt.
Aviso:os autores de bibliotecas nunca devem adicionar essas flags de otimização global às regras do consumidor, porque elas seriam aplicadas à força a todo o app.
Para deixar ainda mais claro, na versão 9.0 do Plug-in do Android para Gradle, vamos começar a ignorar completamente as flags de otimização global das bibliotecas.
Práticas recomendadas para bibliotecas
Todo app Android depende de bibliotecas de uma forma ou de outra. Então, vamos falar sobre as práticas recomendadas para bibliotecas.
Para desenvolvedores de bibliotecas
Se a biblioteca usa reflexão ou JNI, é sua responsabilidade fornecer as regras de retenção necessárias aos consumidores dela. Essas regras são colocadas em um arquivo consumer-rules.pro, que é automaticamente agrupado dentro do arquivo AAR da biblioteca.
android {
defaultConfig {
consumerProguardFiles("consumer-rules.pro")
}
...
}Para consumidores de bibliotecas
Filtrar regras do Keep problemáticas
Se você precisar usar uma biblioteca que inclua regras keep problemáticas, filtre-as no arquivo build.gradle.kts a partir do AGP 9.0. Isso informa ao R8 para ignorar as regras de uma dependência específica.
release {
optimization.keepRules {
// Ignore all consumer rules from this specific library
it.ignoreFrom("com.somelibrary:somelibrary")
}
}A melhor regra do Keep é não ter regra
A estratégia de configuração do R8 é remover a necessidade de escrever regras de manutenção. Para muitos apps, isso pode ser feito escolhendo bibliotecas modernas que favorecem a geração de código em vez da reflexão.Com a geração de código, o otimizador pode determinar mais facilmente qual código é usado no tempo de execução e qual pode ser removido. Além disso, não usar nenhuma reflexão dinâmica significa que não há pontos de entrada "ocultos" e, portanto, não são necessárias regras de manutenção. Ao escolher uma nova biblioteca, sempre prefira uma solução que use geração de código em vez de reflexão.
Para mais informações sobre como escolher bibliotecas, consulte Escolha bibliotecas com sabedoria.
Depuração e solução de problemas da configuração do R8
Quando o R8 remove um código que deveria ter sido mantido ou quando o APK fica maior do que o esperado, use essas ferramentas para diagnosticar o problema.
Encontrar regras Keep duplicadas e globais
Como o R8 mescla regras de dezenas de fontes, pode ser difícil saber qual é o conjunto de regras "final". Adicionar essa flag ao arquivo proguard-rules.pro gera um relatório completo:
# Outputs the final, merged set of rules to the specified file -printconfiguration build/outputs/logs/configuration.txt
Você pode pesquisar esse arquivo para encontrar regras redundantes ou rastrear uma regra problemática (como -dontoptimize) até a biblioteca específica que a incluiu.
Pergunte ao R8: por que você está mantendo isso?
Se uma classe que você esperava ser removida ainda estiver no app, o R8 pode informar o motivo. Basta adicionar esta regra:
# Asks R8 to explain why it's keeping a specific class class com.example.MyUnusedClass -whyareyoukeeping
Durante o build, o R8 vai imprimir a cadeia exata de referências que fez com que ele mantivesse essa classe. Assim, você pode rastrear a referência e ajustar as regras.
Para um guia completo, consulte a seção Resolver problemas do R8.
Próximas etapas
O R8 é uma ferramenta eficiente para melhorar o desempenho de apps Android. A eficácia dele depende de um entendimento correto da operação como um mecanismo de análise estática.
Ao escrever regras específicas no nível do membro, aproveitar ancestrais e anotações e escolher cuidadosamente as opções de permanência corretas, você pode preservar exatamente o que é necessário. A prática mais avançada é eliminar a necessidade de regras escolhendo bibliotecas modernas baseadas em codegen em vez de predecessoras baseadas em reflexão.
Enquanto acompanha a Semana em destaque de performance, confira o vídeo de hoje no YouTube e continue com nosso desafio da R8. Use #optimizationEnabled para tirar dúvidas sobre como ativar ou resolver problemas do R8. Estamos aqui para ajudar.
É hora de conferir os benefícios por conta própria.
Desafiamos você a ativar o modo completo do R8 no seu app hoje.
- Siga nossos guias para desenvolvedores e comece a ativar a otimização de apps.
- Verifique se você ainda usa
proguard-android.txte substitua porproguard-android-optimize.txt. - Em seguida, meça o impacto. Não apenas sinta a diferença, verifique. Para medir os ganhos de performance, adapte o código do app de exemplo da biblioteca Macrobenchmark no GitHub (em inglês) e meça os tempos de inicialização antes e depois.
Temos certeza de que você vai notar uma melhoria significativa no desempenho do app.
Aproveite e use a tag #AskAndroid nas redes sociais para enviar suas dúvidas. Durante toda a semana, nossos especialistas monitoram e respondem às suas perguntas.
Amanhã, vamos falar sobre a otimização guiada por perfil com perfis de referência e de inicialização, compartilhar como a performance de renderização do Compose melhorou nas versões anteriores e falar sobre considerações de performance para trabalho em segundo plano.
Continuar lendo
-
Notícias sobre produtos
O fluxo de trabalho e as necessidades de IA de cada desenvolvedor são únicos. Por isso, é importante poder escolher como a IA ajuda no desenvolvimento. Em janeiro, lançamos a opção de escolher qualquer modelo de IA local ou remoto para ativar a funcionalidade de IA no Android Studio.
Matthew Warner • Leitura de 2 minutos
-
Notícias sobre produtos
O Android Studio Panda 3 agora está estável e pronto para uso em produção. Com essa versão, você tem ainda mais controle e personalização sobre seus fluxos de trabalho com tecnologia de IA, o que facilita a criação de apps Android de alta qualidade.
Matt Dyor • 3 min de leitura
-
Notícias sobre produtos
No Google, nosso objetivo é levar os modelos de IA mais avançados diretamente para os dispositivos Android no seu bolso. Hoje, temos o prazer de anunciar o lançamento do nosso mais recente modelo aberto de última geração: o Gemma 4.
Caren Chang, David Chou • 3 min de leitura
Fique por dentro
Receba os insights mais recentes sobre desenvolvimento Android na sua caixa de entrada semanalmente.