Para ativar a otimização de apps, use bibliotecas compatíveis com a otimização do Android. Se uma biblioteca não estiver configurada para otimização do Android, por exemplo, se ela usar reflexão sem agrupar as regras de keep associadas, ela pode não ser adequada para um app Android. Esta página explica por que algumas bibliotecas são mais adequadas para a otimização de apps e oferece dicas gerais para ajudar você a escolher.
Preferir a geração de código em vez da reflexão
Em geral, escolha bibliotecas que usam geração de código (codegen) em vez de reflexão. Com a geração de código, o otimizador pode determinar com mais facilidade qual código é realmente usado no tempo de execução e qual pode ser removido. Pode ser difícil saber se uma biblioteca usa geração de código ou reflexão, mas há alguns sinais. Confira as dicas para receber ajuda.
Para mais informações sobre a geração de código em comparação com a reflexão, consulte Otimização para autores de bibliotecas.
Dicas gerais para escolher bibliotecas
Use estas dicas para garantir que suas bibliotecas sejam compatíveis com a otimização do app.
Verificar problemas de otimização
Ao considerar uma nova biblioteca, consulte o rastreador de problemas e as discussões on-line dela para verificar se há problemas relacionados à minificação ou à configuração da otimização do app. Se houver, tente encontrar alternativas para essa biblioteca. Lembre-se do seguinte:
- As bibliotecas do AndroidX e bibliotecas como Hilt funcionam bem com a otimização do app porque usam a geração de código em vez da reflexão. Quando usam a reflexão, eles fornecem regras mínimas de retenção para manter apenas o código necessário.
- As bibliotecas de serialização geralmente usam a reflexão para evitar o código boilerplate ao instanciar ou serializar objetos. Em vez de abordagens baseadas em reflexão (como o Gson para JSON), procure bibliotecas que usem o codegen para evitar esses problemas, por exemplo, usando a serialização do Kotlin (link em inglês).
- Se possível, evite bibliotecas que incluam regras de manutenção em todo o pacote. As regras de manutenção em todo o pacote podem ajudar a resolver erros, mas as regras de manutenção amplas precisam ser refinadas para manter apenas o código necessário. Para mais informações, consulte Adotar otimizações de forma incremental.
Ativar a otimização depois de adicionar uma nova biblioteca
Quando você adicionar uma nova biblioteca, ative a otimização depois e verifique se há erros. Se houver erros, procure alternativas para essa biblioteca ou escreva regras de manutenção. Se uma biblioteca não for compatível com a otimização, registre um bug com ela.
As regras são cumulativas
As regras de manutenção são cumulativas. Isso significa que algumas regras que uma dependência de biblioteca inclui não podem ser removidas e podem afetar a compilação de outras partes do app. Por exemplo, se uma biblioteca incluir uma regra para desativar otimizações de código, essa regra desativará as otimizações do projeto inteiro.
Verificar o uso de reflexão (avançado)
É possível saber se uma biblioteca usa a reflexão ao inspecionar o código dela. Se a biblioteca usar reflexão, verifique se ela oferece regras de manutenção associadas. Uma biblioteca provavelmente está usando a reflexão se fizer o seguinte:
- Usa classes ou métodos dos pacotes
kotlin.reflect
oujava.lang.reflect
. - Usa as funções
Class.forName
ouclassLoader.getClass
- Lê anotações no momento da execução, por exemplo, se ela armazena um valor de anotação
usando
val value = myClass.getAnnotation()
ouval value = myMethod.getAnnotation()
e faz algo comvalue
Chama métodos usando o nome do método como uma string, por exemplo:
// Calls the private `processData` API with reflection myObject.javaClass.getMethod("processData", DataType::class.java) ?.invoke(myObject, data)
Filtrar regras de manutenção incorretas (avançado)
Evite bibliotecas com regras de manutenção que retêm o código que precisa ser removido. No entanto, se você precisar usá-las, filtre as regras, conforme mostrado no código abaixo:
// If you're using AGP 8.4 and higher
buildTypes {
release {
optimization.keepRules {
it.ignoreFrom("com.somelibrary:somelibrary")
}
}
}
// If you're using AGP 7.3-8.3
buildTypes {
release {
optimization.keepRules {
it.ignoreExternalDependencies("com.somelibrary:somelibrary")
}
}
}
Estudo de caso: por que o Gson falha com otimizações
O Gson é uma biblioteca de serialização que geralmente causa problemas com a otimização do app
porque usa muito a reflexão. O snippet de código abaixo mostra como o Gson é
usado normalmente, o que pode causar facilmente falhas no momento da execução. Quando você
usa o Gson para receber uma lista de objetos de usuário, não chama o construtor nem transmite uma
fábrica para a função fromJson()
. Criar ou consumir classes
definidas pelo app sem nenhum dos itens a seguir é um sinal de que uma biblioteca pode estar usando
reflexão aberta:
- Classe do app que implementa uma biblioteca ou interface ou classe padrão
- Plug-in de geração de código, como o KSP
class User(val name: String)
class UserList(val users: List<User>)
// This code runs in debug mode, but crashes when optimizations are enabled
Gson().fromJson("""[{"name":"myname"}]""", User::class.java).toString()
Quando o R8 analisa esse código e não encontra a UserList
ou a User
instanciada
em nenhum lugar, ele pode renomear campos ou remover construtores que não parecem ser
usados, causando falhas no app. Se você estiver usando outras bibliotecas de maneiras
semelhantes, verifique se elas não vão interferir na otimização do app. Se
interferirem, evite usá-las.
O Room e o Hilt criam tipos definidos pelo app, mas usam o código de geração para evitar a necessidade de reflexão.