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 a otimização do Android, por exemplo, se ela usar reflexão sem agrupar as regras de retenção associadas, talvez não seja 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.
Dicas gerais ao escolher bibliotecas
Use estas dicas para garantir que suas bibliotecas sejam compatíveis com a otimização de apps.
Prefira a geração de código à reflexão
Escolha bibliotecas que usam a geração de código (codegen) em vez de reflexão. Com a codegen, o otimizador pode determinar qual código é realmente usado no tempo de execução e qual pode ser removido. Pode ser difícil saber se uma biblioteca usa codegen ou reflexão, mas há alguns sinais. Consulte as dicas para receber ajuda.
Para mais informações sobre codegen versus reflexão, consulte Otimização para autores de bibliotecas.
Verificar o uso da reflexão (avançado)
É possível saber se uma biblioteca usa reflexão inspecionando o código dela. Se a biblioteca usar reflexão, verifique se ela fornece regras de retenção associadas. Uma biblioteca provavelmente usa reflexão se fizer o seguinte:
- Usa classes ou métodos dos pacotes
kotlin.reflectoujava.lang.reflect. - Usa as funções
Class.forNameouclassLoader.getClass. - Lê anotações no tempo de execução, por exemplo, se 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, como no exemplo a seguir:
// Calls the private `processData` API with reflection myObject.javaClass.getMethod("processData", DataType::class.java) ?.invoke(myObject, data)
Verificar problemas de otimização
Ao considerar uma nova biblioteca, consulte o rastreador de problemas e as discussões on-line da biblioteca para verificar se há problemas relacionados à minificação ou à configuração da otimização de apps. Se houver, tente procurar alternativas para essa biblioteca. Lembre-se do seguinte:
- As bibliotecas do AndroidX e bibliotecas como o Hilt funcionam bem com a otimização de apps porque usam principalmente codegen em vez de reflexão. Quando usam reflexão, elas fornecem regras de retenção mínimas para manter apenas o código necessário.
- As bibliotecas de serialização geralmente usam reflexão para evitar código boilerplate ao instanciar ou serializar objetos. Em vez de abordagens baseadas em reflexão (como Gson para JSON), procure bibliotecas que usam codegen para evitar esses problemas, por exemplo, usando a serialização do Kotlin {:.external} ou o Moshi com codegen.
- Se possível, evite bibliotecas que incluem regras de retenção em todo o pacote. As regras de retenção em todo o pacote podem ajudar a resolver erros, mas as regras de retenção amplas precisam ser refinadas para manter apenas o código necessário. Para mais informações, consulte Adotar otimizações de forma incremental.
- Antes de publicar um app que usa uma biblioteca de terceiros, use o analisador de configuração do R8 para auditar as regras de retenção fornecidas. Ao analisar o relatório, você pode verificar se as regras de retenção da biblioteca são muito amplas, impedindo que o R8 execute otimizações críticas na sua base de código. Essa verificação garante que as bibliotecas selecionadas estejam alinhadas às metas de desempenho do app e não introduzam um aumento desnecessário na configuração.
- As bibliotecas não devem exigir que você copie e cole regras de retenção da documentação em um arquivo no seu projeto, especialmente regras de retenção em todo o pacote. Essas regras se tornam um fardo de manutenção para o desenvolvedor de apps a longo prazo e são difíceis de otimizar e mudar ao longo do tempo.
Ativar a otimização após adicionar uma nova biblioteca
Ao adicionar uma nova biblioteca, ative a otimização e verifique se há erros. Se houver erros, procure alternativas para essa biblioteca ou escreva regras de retenção. Se uma biblioteca não for compatível com a otimização, registre um bug com ela.
Filtrar regras de retenção ruins (avançado)
As regras de retenção podem ser somadas. Isso significa que algumas regras incluídas na dependência da biblioteca não podem ser removidas e podem afetar a compilação de outras partes do app. Por exemplo, se uma biblioteca inclui uma regra para desativar as otimizações de código, essa regra desativa as otimizações para todo o projeto.
Evite bibliotecas com regras de retenção que mantenham o código que realmente precisa ser removido. No entanto, se você precisar usá-las, poderá filtrar as regras conforme mostrado no código a seguir:
// 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 é interrompido com otimizações
Gson é uma biblioteca de serialização que geralmente causa problemas com a otimização de apps porque usa muito a reflexão. O snippet de código a seguir mostra como o Gson é usado normalmente, o que pode causar falhas no tempo de execução. Observe que, ao usar o Gson para receber uma lista de objetos de usuário, você não chama o construtor nem transmite uma fábrica para a função fromJson(). Construir ou consumir classes definidas pelo app sem um dos seguintes é um sinal de que uma biblioteca pode estar usando reflexão aberta:
- Classe de 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()
Para entender como o R8 funciona no Gson, consulte as regras de consumidor do Gson. Quando o R8
analisa esse código e não vê o UserList ou User instanciado
em qualquer 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 e, se fizerem, evite-as.
Para definir as classes de maneira compatível com as regras de consumidor do Gson, use o snippet a seguir como referência:
class User(@com.google.gson.annotations.SerializedName("name") val name: String)
class UserList(@com.google.gson.annotations.SerializedName("users") val users: List<User>)
Observe que Room, Hilt e Moshi com codegen constroem tipos definidos pelo app, mas usam codegen para evitar a necessidade de reflexão.