Como autor de uma biblioteca, você precisa garantir que os desenvolvedores de apps possam incorporar facilmente sua biblioteca ao app deles, mantendo uma experiência de alta qualidade para o usuário final. Verifique se a biblioteca é compatível com a otimização do Android sem configuração adicional ou documente que ela pode ser inadequada para uso no Android.
Esta documentação é destinada a desenvolvedores de bibliotecas publicadas, mas também pode ser útil para desenvolvedores de módulos de biblioteca internos em um app grande e modularizado.
Se você é desenvolvedor de apps e quer saber como otimizar seu app Android, consulte Ativar a otimização de apps. Para saber quais bibliotecas são adequadas para uso, consulte Escolha bibliotecas com sabedoria.
Usar codegen em vez de reflexão
Sempre que possível, use a geração de código (codegen) em vez de reflexão. A geração de código e a reflexão são abordagens comuns para evitar códigos clichê na programação, mas a geração de código é mais compatível com um otimizador de apps como o R8:
- Com a geração de código, o código é analisado e modificado durante o processo de build. Como não há modificações importantes após o tempo de compilação, o otimizador sabe qual código é necessário e o que pode ser removido com segurança.
- Com a reflexão, o código é analisado e manipulado em tempo de execução. Como o código não é realmente finalizado até ser executado, o otimizador não sabe qual código pode ser removido com segurança. É provável que ele remova o código usado dinamicamente por reflexão durante o tempo de execução, o que causa falhas no app para os usuários.
Muitas bibliotecas modernas usam codegen em vez de reflexão. Consulte KSP para um ponto de entrada comum, usado pelo Room, Dagger2 e muitos outros.
Quando a reflexão é adequada
Se você precisar usar a reflexão, faça isso apenas em uma das seguintes opções:
- Tipos de destino específicos (implementadores ou subclasses de interface específicos)
- Codificar usando uma anotação de ambiente de execução específica
Usar a reflexão dessa forma limita o custo de tempo de execução e permite escrever regras de retenção de consumidores segmentadas.
Essa forma específica e direcionada de reflexão é um padrão que pode ser visto na
estrutura do Android (por exemplo, ao inflar atividades, visualizações e
elementos gráficos) e nas bibliotecas do AndroidX (por exemplo, ao construir WorkManager
ListenableWorkers
ou RoomDatabases
). Por outro lado, a reflexão aberta do
Gson não é adequada para uso em apps Android.
Tipos de regras de manutenção em bibliotecas
Há dois tipos distintos de regras de manutenção que podem ser usadas em bibliotecas:
- As regras de manutenção do consumidor precisam especificar regras que mantenham o que a biblioteca
reflete. Se uma biblioteca usa reflexão ou JNI para chamar o próprio código ou
código definido por um app cliente, essas regras precisam descrever qual código precisa
ser mantido. As bibliotecas precisam empacotar regras de manutenção do consumidor, que usam o mesmo
formato das regras de manutenção do app. Essas regras são agrupadas em artefatos de biblioteca
(AARs ou JARs) e são consumidas automaticamente durante a otimização
de apps Android quando a biblioteca é usada. Essas regras são mantidas no
arquivo especificado com a propriedade
consumerProguardFiles
no seu arquivobuild.gradle.kts
(oubuild.gradle
). Para saber mais, consulte Escrever regras de retenção do consumidor. - As regras de manutenção de build da biblioteca são aplicadas quando ela é criada. Elas só são necessárias se você decidir otimizar parcialmente sua biblioteca no momento da build.
Eles precisam impedir que a API pública da biblioteca seja removida. Caso contrário, a
API pública não estará presente na distribuição da biblioteca, o que significa que os
desenvolvedores de apps não poderão usar a biblioteca. Essas regras são mantidas no arquivo
especificado com a propriedade
proguardFiles
no arquivobuild.gradle.kts
(oubuild.gradle
). Para saber mais, consulte Otimizar o build da biblioteca AAR.
Escrever regras de retenção de consumidores
Além das orientações gerais sobre regras de retenção, confira as recomendações específicas para autores de bibliotecas.
- Não use regras globais inadequadas. Evite colocar configurações globais como
-dontobfuscate
ou-allowaccessmodification
no arquivo de regras de preservação do consumidor da biblioteca, já que elas afetam todos os apps que usam sua biblioteca. - Não use
-repackageclasses
no arquivo de regras de manutenção do consumidor da biblioteca. No entanto, para otimizar o build da biblioteca, use-repackageclasses
com um nome de pacote interno, como<your.library.package>.internal
, no arquivo de regras de manutenção do build da biblioteca. Isso pode tornar sua biblioteca mais eficiente, mesmo que os apps que a consomem não estejam otimizados, mas geralmente não é necessário, já que os apps também precisam ser otimizados. Para mais detalhes sobre a otimização de bibliotecas, consulte Otimização para autores de bibliotecas. - Declare todos os atributos necessários para que a biblioteca funcione nos arquivos de regras keep dela, mesmo que haja uma sobreposição com os atributos definidos em
proguard-android-optimize.txt
. - Se você precisar dos seguintes atributos na distribuição da biblioteca, mantenha-os no arquivo de regras de manutenção da biblioteca, não no arquivo de regras de manutenção do consumidor da biblioteca:
AnnotationDefault
EnclosingMethod
Exceptions
InnerClasses
RuntimeInvisibleAnnotations
RuntimeInvisibleParameterAnnotations
RuntimeInvisibleTypeAnnotations
RuntimeVisibleAnnotations
RuntimeVisibleParameterAnnotations
RuntimeVisibleTypeAnnotations
Signature
- Os autores de bibliotecas precisam manter o atributo
RuntimeVisibleAnnotations
nas regras de manutenção do consumidor se as anotações forem usadas no tempo de execução. - Os autores de bibliotecas não devem usar as seguintes opções globais nas regras de
preservação do consumidor:
-include
-basedirectory
-injars
-outjars
-libraryjars
-repackageclasses
-flattenpackagehierarchy
-allowaccessmodification
-overloadaggressively
-renamesourcefileattribute
-ignorewarnings
-addconfigurationdebugging
-printconfiguration
-printmapping
-printusage
-printseeds
-applymapping
-obfuscationdictionary
-classobfuscationdictionary
-packageobfuscationdictionary
Bibliotecas AAR
Para adicionar regras de consumidor a uma biblioteca AAR, use a opção consumerProguardFiles
no script de build do módulo da biblioteca Android. Para mais informações, consulte nossas
orientações sobre como criar módulos de biblioteca.
Kotlin
android { defaultConfig { consumerProguardFiles("consumer-proguard-rules.pro") } ... }
Groovy
android { defaultConfig { consumerProguardFiles 'consumer-proguard-rules.pro' } ... }
Bibliotecas JAR
Para agrupar regras com sua biblioteca Kotlin/Java que é enviada como um JAR, coloque o arquivo de regras no diretório META-INF/proguard/
do JAR final, com qualquer nome de arquivo.
Por exemplo, se o código estiver em <libraryroot>/src/main/kotlin
, coloque um arquivo de regras do consumidor em <libraryroot>/src/main/resources/META-INF/proguard/consumer-proguard-rules.pro
, e as regras serão agrupadas no local correto do JAR de saída.
Verifique se o JAR final agrupa as regras corretamente. Para isso, confira se elas estão no diretório META-INF/proguard
.
Otimizar a build da biblioteca AAR (avançado)
Em geral, não é necessário otimizar uma build de biblioteca diretamente, porque as possíveis otimizações no momento da build são muito limitadas. Somente durante um build de aplicativo, quando uma biblioteca é incluída como parte de um aplicativo, o R8 pode saber como todos os métodos da biblioteca são usados e quais parâmetros são transmitidos. Como desenvolvedor de bibliotecas, você precisa considerar várias etapas de otimização e manter o comportamento, tanto no momento da criação da biblioteca quanto do app, antes de otimizar essa biblioteca.
Se você ainda quiser otimizar sua biblioteca no momento da build, isso será compatível com o Plug-in do Android para Gradle.
Kotlin
android { buildTypes { release { isMinifyEnabled = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) } configureEach { consumerProguardFiles("consumer-rules.pro") } } }
Groovy
android { buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } configureEach { consumerProguardFiles "consumer-rules.pro" } } }
O comportamento de proguardFiles
é muito diferente de consumerProguardFiles
:
- Os
proguardFiles
são usados no momento da build, geralmente comgetDefaultProguardFile("proguard-android-optimize.txt")
, para definir qual parte da biblioteca deve ser mantida durante a build. No mínimo, essa é sua API pública. consumerProguardFiles
, por outro lado, são empacotados na biblioteca para afetar quais otimizações acontecem mais tarde, durante o build de um app que consome sua biblioteca.
Por exemplo, se a biblioteca usar reflexão para construir classes internas, talvez seja necessário definir as regras de manutenção em proguardFiles
e consumerProguardFiles
.
Se você usar -repackageclasses
no build da biblioteca, reembale as classes em um
subpacote dentro do pacote da biblioteca. Por exemplo, use -repackageclasses
'com.example.mylibrary.internal'
em vez de -repackageclasses 'internal'
.
Suporte a diferentes versões do R8 (avançado)
É possível personalizar regras para segmentar versões específicas do R8. Isso permite que sua biblioteca funcione de maneira ideal em projetos que usam versões mais recentes do R8, além de permitir que regras atuais continuem sendo usadas em projetos com versões mais antigas do R8.
Para especificar regras segmentadas do R8, inclua-as no diretório
META-INF/com.android.tools
dentro de classes.jar
de um AAR ou no diretório
META-INF/com.android.tools
de um JAR.
In an AAR library:
proguard.txt (legacy location, the file name must be "proguard.txt")
classes.jar
└── META-INF
└── com.android.tools (location of targeted R8 rules)
├── r8-from-<X>-upto-<Y>/<R8-rule-files>
└── ... (more directories with the same name format)
In a JAR library:
META-INF
├── proguard/<ProGuard-rule-files> (legacy location)
└── com.android.tools (location of targeted R8 rules)
├── r8-from-<X>-upto-<Y>/<R8-rule-files>
└── ... (more directories with the same name format)
No diretório META-INF/com.android.tools
, pode haver vários
subdiretórios com nomes no formato r8-from-<X>-upto-<Y>
para indicar
para quais versões do R8 as regras foram escritas. Cada subdiretório pode ter um ou mais arquivos com as regras do R8, com qualquer nome de arquivo e extensão.
As partes -from-<X>
e -upto-<Y>
são opcionais, a versão <Y>
é exclusiva, e os intervalos de versão geralmente são contínuos, mas também podem se sobrepor.
Por exemplo, r8
, r8-upto-8.0.0
, r8-from-8.0.0-upto-8.2.0
e
r8-from-8.2.0
são nomes de diretórios que representam um conjunto de regras do R8 segmentadas. As regras no diretório r8
podem ser usadas por qualquer versão do R8. As regras no diretório
r8-from-8.0.0-upto-8.2.0
podem ser usadas pelo R8 da versão
8.0.0 até a versão 8.2.0, mas não incluindo essa última.
O plug-in do Android para Gradle usa essas informações para selecionar todas as regras que podem ser usadas pela versão atual do R8. Se uma biblioteca não especificar regras de R8
segmentadas, o plug-in do Android Gradle vai selecionar as regras dos locais legados
(proguard.txt
para um AAR ou META-INF/proguard/<ProGuard-rule-files>
para um
JAR).