Siga as práticas recomendadas

Ao trabalhar com regras de retenção, é importante acertar a especificidade para garantir que você se beneficie delas e mantenha o app funcionando corretamente. Consulte as seções a seguir para saber mais sobre bons padrões e o que evitar.

Padrões adequados nas regras de retenção

As regras de retenção devem ser o mais específicas possível:

  • Sempre estabeleça uma classe, classe base ou classe anotada específica, se possível, conforme mostrado nos exemplos a seguir:

    -keepclassmembers class com.example.MyClass {
      void someSpecificMethod();
    }
    
    -keepclassmembers ** extends com.example.MyBaseClass {
      void someSpecificMethod();
    }
    
    -keepclassmembers @com.example.MyAnnotation class ** {
      void someSpecificMethod();
    }
    
  • Sempre que possível, use anotações no código-fonte (como uma anotação @Keep) e segmente essas anotações diretamente nas regras de retenção. Isso cria um vínculo claro e explícito entre seu código e as regras que o preservam, tornando sua configuração mais robusta, fácil de entender e menos propensa a falhas quando o código muda.

    Por exemplo, bibliotecas como androidx.annotation usam este padrão:

    // In your source code
    @Keep
    class MyDataClass { /* ... */ }
    
    // In your R8 rules
    -keep @androidx.annotation.DisplayComponent class * {*;}
    

    Recomendamos nomear as anotações de forma que elas forneçam um contexto significativo sobre o motivo de partes do código serem preservadas. Por exemplo, use @DisplayComponent em um app em que os componentes de exibição exigem que algumas partes sejam mantidas.

  • Sempre que possível, a especificação do membro deve ser declarada, e apenas as partes da classe que precisarem ser mantidas para o funcionamento do app devem ser referenciadas. Recomendamos não aplicar uma regra a uma classe inteira definindo o escopo de membro opcional como { *; }, a menos que seja estritamente necessário.

    -keepclassmembers com.example.MyClass {
      void someSpecificMethod();
      void @com.example.MyAnnotation *;
    }
    
  • Se você usar a opção global repackageclasses, evite especificar o nome do pacote opcional. Isso resulta em arquivos DEX menores porque o prefixo do pacote é omitido nos nomes de classe reempacotados.

Se não for possível seguir essas diretrizes, isole temporariamente o código que precisa ser mantido em um pacote dedicado e aplique a regra de retenção a ele. No entanto, essa não é uma solução de longo prazo. Para saber mais, consulte Adotar otimizações de forma incremental. Para usar uma regra de retenção em um pacote, defina-a conforme mostrado no exemplo a seguir:

-keepclassmembers class com.example.pkg.** { *; }

O que deve ser evitado

A sintaxe da regra de retenção tem muitas opções, mas para ter benefícios de desempenho sustentáveis e mensuráveis, recomendamos não usar o seguinte:

  • Não use regras de retenção em todo o pacote, como -keep class com.example.pkg.** { *; }, a longo prazo. Elas podem ser usadas temporariamente para resolver problemas ao configurar o R8. Para mais informações, consulte Limitar o escopo da otimização. Em geral, tenha cuidado com os caracteres curinga. Mantenha apenas o código necessário.
  • Quando possível, evite bibliotecas que sugerem copiar e colar regras de retenção ao usá-las, especialmente as que se aplicam a todo o pacote. As bibliotecas projetadas para ter um bom desempenho no Android devem evitar a reflexão sempre que possível e incorporar regras de retenção para consumidores quando necessário.
  • Evite usar o operador de inversão ! em regras de retenção, porque você pode acabar aplicando uma regra a quase todas as classes no seu aplicativo sem querer.

Se não for possível seguir essas regras, talvez você esteja usando muitas reflexões abertas. Nesse caso, evite-as ou evite a biblioteca que usa reflexão. Consulte o estudo de caso do Gson.