Por padrão, o R8 faz muitas otimizações para melhorar a performance e o tamanho, mas elas podem não funcionar imediatamente no seu app. Se você estiver ativando R8 (ou ativando o modo completo) em um app grande pela primeira vez, tente adotar otimizações de forma incremental: desative temporariamente a ofuscação e ative o R8 para partes do código por vez, em vez de todo o código do seu app. Recomendamos essa abordagem incremental durante o desenvolvimento local, mas você também pode usá-la durante os testes de QA internos ou até mesmo na produção como um lançamento gradual. As etapas exatas que você realiza dependem da linha do tempo desejada e da confiança na cobertura de testes de pré-lançamento.
Limitar as otimizações
O R8 faz muitos tipos de otimizações, incluindo a remoção e a reescrita de código e a remoção de recursos. Confira algumas descrições gerais dos tipos de otimização:
- Redução de código (ou tree shaking): remove o código não referenciado
- Ofuscação (ou minimização de identificadores): encurta os nomes de classes e métodos
- Otimização: reescreve o código, por exemplo, inlining
Para reduzir a chance de erros, comece ativando apenas algumas dessas otimizações.
Ativar apenas o tree shaking
A redução de código, também conhecida como tree shaking, remove o código que parece não referenciado. Recomendamos começar apenas com o tree shaking, já que ele é o mais simples.
Para ativar apenas o tree shaking, adicione o seguinte ao arquivo proguard-rules.pro para desativar os outros tipos de otimizações. Desativar a ofuscação é fundamental porque facilita muito a leitura de stack traces.
-dontobfuscate // Use temporarily to turn off identifier minification
-dontoptimize // Use temporarily to turn off optimization
No final, você não vai querer enviar essa configuração, já que ela limita drasticamente a capacidade do R8 de otimizar o código, mas é um ótimo ponto de partida ao adotar o R8 pela primeira vez em uma base de código grande com problemas a serem corrigidos.
Usar o modo de compatibilidade
Por padrão, o R8 é executado no modo completo. O modo completo oferece performance e economia de tamanho significativamente melhores, mas você pode desativá-lo temporariamente e usar o modo de compatibilidade ao ativar a minimização pela primeira vez.
Para usar o modo de compatibilidade, use a seguinte configuração no arquivo gradle.properties:
android.enableR8.fullMode = false // Use temporarily to disable full mode
Ativar o restante das otimizações
Quando você confirmar que o tree shaking funciona para seu app, remova as configurações anteriores para reativar a ofuscação, a otimização e o modo completo do R8. A ofuscação pode dificultar a depuração. Por isso, recomendamos resolver os problemas de tree shaking primeiro.
Para mais informações sobre como desofuscar stack traces, consulte Recuperar o stack trace original.
Limitar o escopo de otimização
Um build totalmente otimizado otimiza todo o código em todas as bibliotecas e pacotes. Por isso, é comum encontrar problemas com o R8 quando você o ativa pela primeira vez. Se você encontrar um problema com a otimização em uma parte do app, não desative o R8 completamente, ou você vai perder os benefícios em todos os outros lugares. Em vez disso, desative temporariamente o R8 apenas nas partes do app que estão causando problemas.
Usar regras de retenção em todo o pacote
Recomendamos usar regras de retenção em todo o pacote como uma maneira de desativar temporariamente o R8 em partes do app. Você sempre deve voltar para corrigir esses problemas de otimização mais tarde. Essa é geralmente uma solução provisória para contornar áreas problemáticas.
Por exemplo, se parte do seu app usa o Gson com frequência e está causando problemas de
otimização, a correção ideal é adicionar regras de retenção mais
direcionadas ou mudar para uma solução de geração de código. No entanto, para desbloquear a otimização do restante do app, você pode colocar o código que define os tipos Gson em um subpacote dedicado e adicionar uma regra como esta ao arquivo proguard-rules.pro:
-keep class com.myapp.json.** { *; }
Se alguma biblioteca que você está usando tiver reflexão em componentes internos, você poderá adicionar uma regra de retenção para toda a biblioteca. Será necessário inspecionar o código ou o JAR/AAR da biblioteca para encontrar o pacote apropriado a ser mantido. Novamente, isso não é recomendado para manter a longo prazo, mas pode desbloquear a otimização do restante do app:
-keep class com.somelibrary.** { *; }
Remover regras de retenção em todo o pacote
Depois que o app funcionar corretamente com regras de retenção em todo o pacote, volte e adicione regras de retenção direcionadas ou remova o uso de reflexão ou a biblioteca que exige a regra de retenção.
Por exemplo, manter todas as regras que estendem uma determinada classe é extremamente comum no AndroidX para manter apenas as classes relevantes. Geralmente, a reflexão só deve segmentar classes ou métodos que estendem determinadas classes abstratas, implementam determinadas interfaces ou em classes com uma anotação de ambiente de execução específica. Cada uma delas é uma maneira compatível de definir regras de retenção para que você não precise de regras de retenção em todo o pacote no app final e totalmente otimizado.
Se a reflexão estiver presente na sua própria base de código, você poderá substituir as regras de retenção em todo o pacote pelo Jetpack Keep. As regras geradas são condicionais: o R8 mantém o destino de uma chamada reflexiva somente se também mantiver o código que faz a chamada. Para migrar um pacote, use as verificações de lint do Jetpack Keep para encontrar e anotar as chamadas reflexivas e, em seguida, remova a regra em todo o pacote.