Escolha as bibliotecas com cuidado

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 ou java.lang.reflect.
  • Usa as funções Class.forName ou classLoader.getClass
  • Lê anotações no momento da execução, por exemplo, se ela armazena um valor de anotação usando val value = myClass.getAnnotation() ou val value = myMethod.getAnnotation() e faz algo com value
  • 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.