In qualità di autore di una libreria, devi assicurarti che gli sviluppatori di app possano incorporare facilmente la tua libreria nella loro app, mantenendo al contempo un'esperienza utente finale di alta qualità. Devi assicurarti che la tua libreria sia compatibile con l'ottimizzazione di Android senza configurazione aggiuntiva oppure documentare che la libreria potrebbe non essere adatta all'utilizzo su Android.
Questa documentazione è rivolta agli sviluppatori di librerie pubblicate, ma potrebbe essere utile anche per gli sviluppatori di moduli di librerie interni in un'app di grandi dimensioni e modulare.
Se sei uno sviluppatore di app e vuoi scoprire di più sull'ottimizzazione della tua app per Android, consulta Attivare l'ottimizzazione delle app. Per scoprire quali librerie sono appropriate da utilizzare, consulta Scegliere le librerie con saggezza.
Utilizza la generazione di codice anziché la reflection
Se possibile, utilizza la generazione di codice (codegen) anziché la reflection. Codegen e reflection sono entrambi approcci comuni per evitare codice boilerplate durante la programmazione, ma codegen è più compatibile con un ottimizzatore di app come R8:
- Con la generazione di codice, il codice viene analizzato e modificato durante il processo di build. Poiché non vengono apportate modifiche importanti dopo la compilazione, l'ottimizzatore sa quale codice è necessario e quale può essere rimosso in sicurezza.
- Con la reflection, il codice viene analizzato e manipolato in fase di runtime. Poiché il codice non viene finalizzato fino all'esecuzione, l'ottimizzatore non sa quale codice può essere rimosso in sicurezza. Probabilmente rimuoverà il codice utilizzato dinamicamente tramite reflection durante l'esecuzione, causando arresti anomali dell'app per gli utenti.
Molte librerie moderne utilizzano la generazione di codice anziché la reflection. Consulta KSP per un punto di ingresso comune, utilizzato da Room, Dagger2 e molti altri.
Quando la riflessione è accettabile
Se devi utilizzare la reflection, devi riflettere solo in uno dei seguenti elementi:
- Tipi di target specifici (implementatori o sottoclassi di interfacce specifici)
- Codice che utilizza un'annotazione di runtime specifica
L'utilizzo della reflection in questo modo limita il costo di runtime e consente di scrivere regole di conservazione mirate per i consumatori.
Questa forma di reflection specifica e mirata è un pattern che puoi vedere sia nel framework Android (ad esempio quando vengono gonfiate attività, visualizzazioni e drawables) sia nelle librerie AndroidX (ad esempio quando vengono costruiti WorkManager
ListenableWorkers
o RoomDatabases
). Al contrario, la reflection aperta di Gson non è adatta all'utilizzo nelle app per Android.
Tipi di regole di conservazione nelle biblioteche
Esistono due tipi distinti di regole di conservazione che puoi avere nelle raccolte:
- Le regole di conservazione dei consumatori devono specificare le regole che conservano ciò che la biblioteca
riflette. Se una libreria utilizza la reflection o JNI per chiamare il proprio codice o
il codice definito da un'app client, queste regole devono descrivere il codice da
conservare. Le librerie devono includere le regole di conservazione dei consumatori, che utilizzano lo stesso formato delle regole di conservazione delle app. Queste regole sono raggruppate in artefatti della libreria
(AAR o JAR) e vengono utilizzate automaticamente durante l'ottimizzazione
dell'app per Android quando viene utilizzata la libreria. Queste regole vengono mantenute nel file specificato con la proprietà
consumerProguardFiles
nel filebuild.gradle.kts
(obuild.gradle
). Per saperne di più, vedi Scrivere regole di conservazione per i consumatori. - Le regole di conservazione della build della libreria vengono applicate durante la creazione della libreria. Sono necessari
solo se decidi di ottimizzare parzialmente la libreria in fase di compilazione.
Devono impedire la rimozione dell'API pubblica della libreria, altrimenti l'API pubblica non sarà presente nella distribuzione della libreria, il che significa che gli sviluppatori di app non potranno utilizzare la libreria. Queste regole vengono mantenute nel file
specificato con la proprietà
proguardFiles
nel filebuild.gradle.kts
(obuild.gradle
). Per saperne di più, consulta Ottimizzare la creazione della libreria AAR.
Scrivere regole di conservazione consumer
Oltre alle indicazioni generali sulle regole di conservazione, di seguito sono riportati consigli specifici per gli autori di librerie.
- Non utilizzare regole globali inappropriate. Evita di inserire impostazioni globali come
-dontobfuscate
o-allowaccessmodification
nel file delle regole di conservazione per i consumatori della tua libreria, poiché influiscono su tutte le app che la utilizzano. - Non utilizzare
-repackageclasses
nel file delle regole di conservazione per i consumatori della tua raccolta. Tuttavia, per ottimizzare la creazione della libreria, puoi utilizzare-repackageclasses
con un nome di pacchetto interno, ad esempio<your.library.package>.internal
, nel file delle regole di conservazione della build della libreria. In questo modo la libreria può diventare più efficiente anche se le app che la utilizzano non sono ottimizzate, ma in genere non è necessario, poiché anche le app dovrebbero essere ottimizzate. Per ulteriori dettagli sull'ottimizzazione delle librerie, consulta Ottimizzazione per gli autori di librerie. - Dichiara tutti gli attributi necessari per il funzionamento della tua libreria nei file delle regole di conservazione della libreria, anche se potrebbe esserci una sovrapposizione con gli attributi definiti in
proguard-android-optimize.txt
. - Se richiedi i seguenti attributi nella distribuzione della libreria,
mantienili nel file delle regole di conservazione della build della libreria e non nel file delle regole di conservazione dei consumatori della libreria:
AnnotationDefault
EnclosingMethod
Exceptions
InnerClasses
RuntimeInvisibleAnnotations
RuntimeInvisibleParameterAnnotations
RuntimeInvisibleTypeAnnotations
RuntimeVisibleAnnotations
RuntimeVisibleParameterAnnotations
RuntimeVisibleTypeAnnotations
Signature
- Gli autori delle librerie devono mantenere l'attributo
RuntimeVisibleAnnotations
nelle regole di conservazione dei consumatori se le annotazioni vengono utilizzate in fase di runtime. - Gli autori delle librerie non devono utilizzare le seguenti opzioni globali nelle regole di conservazione dei consumatori:
-include
-basedirectory
-injars
-outjars
-libraryjars
-repackageclasses
-flattenpackagehierarchy
-allowaccessmodification
-overloadaggressively
-renamesourcefileattribute
-ignorewarnings
-addconfigurationdebugging
-printconfiguration
-printmapping
-printusage
-printseeds
-applymapping
-obfuscationdictionary
-classobfuscationdictionary
-packageobfuscationdictionary
Librerie AAR
Per aggiungere regole per i consumatori per una libreria AAR, utilizza l'opzione consumerProguardFiles
nello script di build del modulo della libreria Android. Per saperne di più, consulta le nostre
indicazioni per la creazione di moduli della libreria.
Kotlin
android { defaultConfig { consumerProguardFiles("consumer-proguard-rules.pro") } ... }
Trendy
android { defaultConfig { consumerProguardFiles 'consumer-proguard-rules.pro' } ... }
Librerie JAR
Per raggruppare le regole con la libreria Kotlin/Java fornita come JAR, inserisci il file delle regole nella directory META-INF/proguard/
del JAR finale, con qualsiasi nome file.
Ad esempio, se il codice si trova in <libraryroot>/src/main/kotlin
, inserisci un file di regole per i consumatori in <libraryroot>/src/main/resources/META-INF/proguard/consumer-proguard-rules.pro
e le regole verranno raggruppate nella posizione corretta nel file JAR di output.
Verifica che le regole dei bundle JAR finali siano corrette controllando che si trovino nella directory META-INF/proguard
.
Ottimizzare la creazione della libreria AAR (avanzato)
In genere, non è necessario ottimizzare direttamente una build della libreria perché le possibili ottimizzazioni in fase di build della libreria sono molto limitate. È solo durante la compilazione di un'applicazione, quando una libreria è inclusa come parte di un'applicazione, che R8 può sapere come vengono utilizzati tutti i metodi della libreria e quali parametri vengono trasmessi. In qualità di sviluppatore di librerie, devi considerare più fasi di ottimizzazione e mantenere il comportamento, sia in fase di compilazione della libreria che dell'app, prima di ottimizzare la libreria.
Se vuoi comunque ottimizzare la libreria in fase di compilazione, questa operazione è supportata dal plug-in Android Gradle.
Kotlin
android { buildTypes { release { isMinifyEnabled = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) } configureEach { consumerProguardFiles("consumer-rules.pro") } } }
Trendy
android { buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } configureEach { consumerProguardFiles "consumer-rules.pro" } } }
Tieni presente che il comportamento di proguardFiles
è molto diverso da quello di
consumerProguardFiles
:
proguardFiles
vengono utilizzati in fase di compilazione, spesso insieme agetDefaultProguardFile("proguard-android-optimize.txt")
, per definire quale parte della libreria deve essere conservata durante la compilazione. Come minimo, si tratta della tua API pubblica.consumerProguardFiles
, al contrario, vengono inseriti nel pacchetto della libreria per influire sulle ottimizzazioni successive, durante la creazione di un'app che utilizza la tua libreria.
Ad esempio, se la tua libreria utilizza la reflection per costruire classi interne, potresti dover definire le regole di conservazione sia in proguardFiles
che in consumerProguardFiles
.
Se utilizzi -repackageclasses
nella build della tua libreria, ricompila le classi in un
sottopacchetto all'interno del pacchetto della libreria. Ad esempio, utilizza -repackageclasses
'com.example.mylibrary.internal'
invece di -repackageclasses 'internal'
.
Supportare versioni R8 diverse (avanzate)
Puoi personalizzare le regole in modo che abbiano come target versioni specifiche di R8. In questo modo, la tua libreria funziona in modo ottimale nei progetti che utilizzano versioni R8 più recenti, consentendo di continuare a utilizzare le regole esistenti nei progetti con versioni R8 precedenti.
Per specificare le regole R8 mirate, devi includerle nella directory META-INF/com.android.tools
all'interno di classes.jar
di un AAR o nella directory META-INF/com.android.tools
di un 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)
Nella directory META-INF/com.android.tools
possono essere presenti più sottodirectory con nomi nel formato r8-from-<X>-upto-<Y>
per indicare per quali versioni di R8 sono scritte le regole. Ogni sottodirectory può contenere uno o
più file contenenti le regole R8, con qualsiasi nome ed estensione.
Tieni presente che le parti -from-<X>
e -upto-<Y>
sono facoltative, la versione <Y>
è esclusiva e gli intervalli di versioni sono in genere continui, ma possono anche
sovrapporsi.
Ad esempio, r8
, r8-upto-8.0.0
, r8-from-8.0.0-upto-8.2.0
e
r8-from-8.2.0
sono nomi di directory che rappresentano un insieme di regole R8 mirate. Le regole
nella directory r8
possono essere utilizzate da qualsiasi versione di R8. Le regole nella directory
r8-from-8.0.0-upto-8.2.0
possono essere utilizzate da R8 dalla versione
8.0.0 fino alla versione 8.2.0, esclusa.
Il plug-in Android Gradle utilizza queste informazioni per selezionare tutte le regole che possono
essere utilizzate dalla versione attuale di R8. Se una libreria non specifica regole R8
mirate, il plug-in Android Gradle selezionerà le regole dalle posizioni legacy
(proguard.txt
per un AAR o META-INF/proguard/<ProGuard-rule-files>
per un
JAR).