Ottimizzazione per gli autori delle librerie

In qualità di autore della libreria, devi assicurarti che gli sviluppatori di app possano incorporare facilmente la tua libreria nella loro app mantenendo un'esperienza utente finale di alta qualità. Devi assicurarti che la tua libreria sia compatibile con l'ottimizzazione di Android senza configurazione aggiuntiva o documentare che la libreria potrebbe essere inappropriata per l'utilizzo su Android.

Questa documentazione è rivolta agli sviluppatori di librerie pubblicate, ma potrebbe essere utile anche per gli sviluppatori di moduli di librerie interne in un'app di grandi dimensioni e modularizzata.

Se sei uno sviluppatore di app e vuoi scoprire come ottimizzare la 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 codegen anziché la riflessione

Se possibile, utilizza la generazione di codice (codegen) anziché la riflessione. La generazione di codice e la riflessione sono entrambi approcci comuni per evitare il codice boilerplate durante la programmazione, ma la generazione di codice è più compatibile con un ottimizzatore di app come R8:

  • Con codegen, il codice viene analizzato e modificato durante il processo di compilazione. Poiché non vengono apportate modifiche sostanziali dopo il tempo di compilazione, l'ottimizzatore sa quale codice è necessario e cosa può essere rimosso in sicurezza.
  • Con la riflessione, il codice viene analizzato e manipolato in fase di esecuzione. Poiché il codice non viene effettivamente finalizzato finché non viene eseguito, lo strumento di ottimizzazione non sa quale codice puoi rimuovere in sicurezza. È probabile che venga rimosso il codice utilizzato dinamicamente tramite la riflessione durante l'esecuzione, causando arresti anomali dell'app per gli utenti.

Molte librerie moderne utilizzano la compilazione anziché la riflessione. Consulta KSP per un punto di contatto comune, utilizzato da Room, Dagger2 e molti altri.

Quando la riflessione è accettabile

Se devi utilizzare la riflessione, devi farlo solo in uno dei seguenti modi:

  • Tipi di target specifici (implementatori o sottoclassi dell'interfaccia specifici)
  • Codice che utilizza un'annotazione di runtime specifica

L'utilizzo della riflessione in questo modo limita il costo di runtime e consente di scrivere regole di conservazione dei consumatori target.

Questa forma specifica e mirata di riflessione è uno schema che puoi vedere sia nel framework Android (ad esempio durante l'inflazione di attività, visualizzazioni e drawable) sia nelle librerie AndroidX (ad esempio durante la creazione di WorkManager ListenableWorker o RoomDatabase). Al contrario, la riflessione aperta di Gson non è appropriata per l'utilizzo nelle app per Android.

Scrivere regole di conservazione per i consumatori

Le librerie devono pacchettizzare le regole di conservazione "consumer", che utilizzano lo stesso formato delle regole di conservazione delle app. Queste regole vengono raggruppate negli elementi della libreria (AAR o JAR) e vengono utilizzate automaticamente durante l'ottimizzazione dell'app per Android quando la libreria viene utilizzata.

Librerie AAR

Per aggiungere regole di consumo per una libreria AAR, utilizza l'opzione consumerProguardFiles nello script di compilazione del modulo della libreria Android. Per ulteriori informazioni, consulta le nostre linee guida per la creazione di moduli della libreria.

Kotlin

android {
    defaultConfig {
        consumerProguardFiles("consumer-proguard-rules.pro")
    }
    ...
}

Groovy

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 un nome file qualsiasi. Ad esempio, se il codice è in <libraryroot>/src/main/kotlin, inserisci un file di regole consumer 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 il JAR finale combini correttamente le regole controllando che queste si trovino nella directory META-INF/proguard.

Ottimizzare la compilazione della libreria AAR (avanzato)

In genere, non dovresti ottimizzare direttamente la compilazione di una libreria perché le possibili ottimizzazioni al momento della compilazione della libreria sono molto limitate. Solo durante la compilazione di un'applicazione, quando una libreria è inclusa come parte di un'applicazione, R8 può sapere come vengono utilizzati tutti i metodi della libreria e quali parametri vengono passati. In qualità di sviluppatore di librerie, devi valutare più fasi di ottimizzazione e mantenere il comportamento sia durante la compilazione della libreria sia dell'app prima di ottimizzare la libreria.

Se vuoi comunque ottimizzare la libreria in fase di compilazione, questa operazione è supportata dal plug-in Android per 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"
        }
    }
}

Tieni presente che il comportamento di proguardFiles è molto diverso da quello di consumerProguardFiles:

  • proguardFiles vengono utilizzati in fase di compilazione, spesso insieme a getDefaultProguardFile("proguard-android-optimize.txt"), per definire quale parte della raccolta deve essere conservata durante la compilazione della raccolta. Come minimo, si tratta della tua API pubblica.
  • consumerProguardFiles, invece, vengono pacchettizzati nella libreria per influire sulle ottimizzazioni che verranno eseguite in un secondo momento, durante la compilazione di un'app che utilizza la tua libreria.

Ad esempio, se la tua libreria utilizza la riflessione per costruire classi interne, potresti dover definire le regole di mantenimento sia in proguardFiles che in consumerProguardFiles.

Se utilizzi -repackageclasses nella compilazione della libreria, impacchetta nuovamente le classi in un sottopacchetto all'interno del pacchetto della libreria. Ad esempio, utilizza -repackageclasses 'com.example.mylibrary.internal' anziché -repackageclasses 'internal'.

Supportare diverse versioni di R8 (avanzato)

Puoi personalizzare le regole in modo da scegliere come target versioni specifiche di R8. In questo modo, la biblioteca può funzionare in modo ottimale nei progetti che utilizzano le versioni più recenti di R8, consentendo al contempo di continuare a utilizzare le regole esistenti nei progetti con versioni precedenti di R8.

Per specificare le regole R8 target, devi includerle nella directory META-INF/com.android.tools all'interno di classes.jar di un file AAR o nella directory META-INF/com.android.tools di un file 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 nella forma r8-from-<X>-upto-<Y> per indicare per quali versioni di R8 sono scritte le regole. Ogni sottodirectory può avere uno o più file contenenti le regole R8, con nomi ed estensioni di file qualsiasi.

Tieni presente che le parti -from-<X> e -upto-<Y> sono facoltative, la versione <Y> è esclusiva e gli intervalli di versione 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 target. 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 per Gradle utilizza queste informazioni per selezionare tutte le regole che possono essere utilizzate dalla versione R8 corrente. Se una libreria non specifica le regole R8 selezionate, il plug-in Gradle per Android seleziona le regole dalle posizioni precedenti (proguard.txt per un file AAR o META-INF/proguard/<ProGuard-rule-files> per un file JAR).