Implementar otimizações de forma incremental

Por padrão, o R8 faz muitas otimizações para melhorar o desempenho e o tamanho, mas elas podem não funcionar imediatamente no seu app. Se você estiver ativando o R8 (ou o modo completo) em um app grande pela primeira vez, tente adotar otimizações incrementais: desative temporariamente a ofuscação e ative o R8 para partes do código por vez, em vez de todo o código no app. Recomendamos essa abordagem incremental durante o desenvolvimento local, mas você também pode usá-la durante testes de controle de qualidade interno ou até mesmo na produção como um lançamento gradual. As etapas exatas que você toma dependem do cronograma desejado 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 de código, 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 sem referência
  • Ofuscação (ou minimização de identificador): encurta os nomes de classes e métodos.
  • Otimização: reescreve o código, por exemplo, inline

Para reduzir a chance de erros, comece ativando apenas algumas dessas otimizações.

Ativar somente a agitação de árvore

A redução de código, também conhecida como tree shaking, remove o código que parece não ter referências. Recomendamos começar com a agitação de árvores, porque é a mais simples.

Para ativar apenas a agitação de árvore, adicione o seguinte ao arquivo proguard-rules.pro para desativar os outros tipos de otimizações. Desabilitar a ofuscação é fundamental porque facilita a leitura dos rastros de pilha.

-dontobfuscate // Use temporarily to turn off identifier minification
-dontoptimize // Use temporarily to turn off optimization

No final, não é recomendável 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 compatível

Por padrão, o R8 é executado no modo completo. O modo completo melhora significativamente o desempenho e a economia de tamanho, mas você pode desativá-lo temporariamente e usar o modo compatível ao ativar a minificaçã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

Depois de confirmar que a agitação de árvore funciona no 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 agitação de árvore primeiro.

Para mais informações sobre como desofuscar stack traces, consulte Recuperar o stack trace original.

Limitar o escopo da 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 de otimização em uma parte do app, não desative o R8 completamente ou 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 o uso de regras de manutenção em todo o pacote como uma maneira de desativar temporariamente o R8 em partes do app. Sempre volte para corrigir esses problemas de otimização mais tarde. Essa é geralmente uma solução temporária para contornar áreas problemáticas.

Por exemplo, se parte do app usa muito o Gson e está causando problemas com otimização, a correção ideal é adicionar mais regras de keep 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 seus 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 manutenção para toda a biblioteca. Você precisa inspecionar o código da biblioteca ou o JAR/AAR para encontrar o pacote adequado para manter. Novamente, não é recomendável manter isso 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 keep em todo o pacote, volte e adicione regras de keep específicas ou remova o uso de reflexão ou a biblioteca que exige a regra de keep.

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 precisa ser direcionada apenas a classes ou métodos que estendem determinadas classes abstratas, implementam determinadas interfaces ou a classes com uma anotação de tempo de execução específica. Cada uma delas é uma forma com suporte para definir regras de manutenção para que você não precise de regras de manutenção em todo o pacote no app final totalmente otimizado.