Scegli le librerie con cura

Per abilitare l'ottimizzazione delle app, devi utilizzare librerie compatibili con l'ottimizzazione di Android. Se una libreria non è configurata per l'ottimizzazione di Android, ad esempio se utilizza la reflection senza raggruppare le regole di conservazione associate, potrebbe non essere adatta a un'app Android. Questa pagina spiega perché alcune librerie sono più adatte all'ottimizzazione delle app e fornisce suggerimenti generali per aiutarti a scegliere.

Suggerimenti generali per la scelta delle librerie

Utilizza questi suggerimenti per assicurarti che le tue librerie siano compatibili con l'ottimizzazione delle app.

Preferisci la generazione di codice alla reflection

Scegli librerie che utilizzano la generazione di codice (codegen) anziché la reflection. Con la generazione di codice, l'ottimizzatore può determinare quale codice viene effettivamente utilizzato in fase di runtime e quale codice può essere rimosso. Può essere difficile capire se una libreria utilizza la generazione di codice o la reflection, ma ci sono alcuni segnali. Consulta i suggerimenti per ricevere assistenza.

Per ulteriori informazioni sulla generazione di codice rispetto alla reflection, consulta la sezione Ottimizzazione per gli autori di librerie.

Verifica l'utilizzo della reflection (avanzato)

Puoi capire se una libreria utilizza la reflection ispezionando il suo codice. Se la libreria utilizza la reflection, verifica che fornisca le regole di conservazione associate. È probabile che una libreria utilizzi la reflection se:

  • Utilizza classi o metodi dei pacchetti kotlin.reflect o java.lang.reflect.
  • Utilizza le funzioni Class.forName o classLoader.getClass.
  • Legge le annotazioni in fase di runtime, ad esempio se memorizza un valore di annotazione utilizzando val value = myClass.getAnnotation() o val value = myMethod.getAnnotation() e poi esegue un'operazione con value.
  • Chiama i metodi utilizzando il nome del metodo come stringa, come nell'esempio seguente:

    // Calls the private `processData` API with reflection
    myObject.javaClass.getMethod("processData", DataType::class.java)
    ?.invoke(myObject, data)
    

Verifica la presenza di problemi di ottimizzazione

Quando prendi in considerazione una nuova libreria, esamina il tracker dei problemi della libreria e le discussioni online per verificare se sono presenti problemi relativi alla riduzione o alla configurazione dell'ottimizzazione delle app. In caso affermativo, dovresti provare a cercare alternative a quella libreria. Tieni presente quanto segue:

  • Le librerie AndroidX e librerie come Hilt funzionano bene con l' ottimizzazione delle app perché utilizzano principalmente la generazione di codice anziché la reflection. Quando utilizzano la reflection, forniscono regole di conservazione minime per conservare solo il codice necessario.
  • Le librerie di serializzazione utilizzano spesso la reflection per evitare il codice boilerplate durante l'istanza o la serializzazione degli oggetti. Anziché approcci basati sulla reflection (come Gson per JSON), cerca librerie che utilizzano la generazione di codice per evitare questi problemi, ad esempio utilizzando Kotlin Serialization {:.external} o Moshi con la generazione di codice.
  • Se possibile, evita le librerie che includono regole di conservazione a livello di pacchetto. Le regole di conservazione a livello di pacchetto possono aiutarti a risolvere gli errori, ma le regole di conservazione generali devono essere perfezionate per conservare solo il codice necessario. Per ulteriori informazioni, consulta la sezione Adottare le ottimizzazioni in modo incrementale.
  • Prima di pubblicare un'app che utilizza una libreria di terze parti, utilizza R8 Configuration Analyzer per controllare le regole di conservazione fornite. Esaminando il report, puoi verificare se le regole di conservazione della libreria sono eccessivamente ampie, impedendo a R8 di eseguire ottimizzazioni critiche sulla tua codebase. Questo controllo garantisce che le librerie selezionate siano in linea con gli obiettivi di rendimento della tua app e non introducano un aumento della configurazione non necessario.
  • Le librerie non devono richiedere di copiare e incollare le regole di conservazione dalla documentazione in un file del progetto, soprattutto non le regole di conservazione a livello di pacchetto. A lungo termine, queste regole diventano un onere di manutenzione per lo sviluppatore di app e sono difficili da ottimizzare e modificare nel tempo.

Abilita l'ottimizzazione dopo aver aggiunto una nuova libreria

Quando aggiungi una nuova libreria, abilita l'ottimizzazione in un secondo momento e verifica se sono presenti errori. In caso di errori, cerca alternative alla libreria o scrivi regole di conservazione. Se una libreria non è compatibile con l'ottimizzazione, segnala un bug alla libreria.

Filtra le regole di conservazione errate (avanzato)

Le regole di conservazione sono cumulative. Ciò significa che alcune regole incluse in una dipendenza della libreria non possono essere rimosse e potrebbero influire sulla compilazione di altre parti dell'app. Ad esempio, se una libreria include una regola per disattivare le ottimizzazioni del codice, questa regola disattiva le ottimizzazioni per l'intero progetto.

Dovresti evitare le librerie con regole di conservazione che mantengono il codice che dovrebbe essere rimosso. Tuttavia, se devi utilizzarle, puoi filtrare le regole come mostrato nel codice seguente:

// 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")
        }
    }
}

Caso di studio: perché Gson non funziona con le ottimizzazioni

Gson è una libreria di serializzazione che spesso causa problemi con l'ottimizzazione delle app perché utilizza molto la reflection. Il seguente snippet di codice mostra come viene in genere utilizzato Gson, che può causare arresti anomali in fase di runtime. Tieni presente che quando utilizzi Gson per ottenere un elenco di oggetti User, non chiami il costruttore né passi una factory alla funzione fromJson(). La creazione o l'utilizzo di classi definite dall'app senza una delle seguenti opzioni indica che una libreria potrebbe utilizzare la reflection aperta:

  • Classe dell'app che implementa una libreria o un'interfaccia o una classe standard
  • Plug-in di generazione di codice come 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()

Per capire come funziona R8 su Gson, consulta le regole del consumatore Gson. Quando R8 analizza questo codice e non vede l'istanza di UserList o User da nessuna parte, può rinominare i campi o rimuovere i costruttori che non sembrano essere utilizzati, causando l'arresto anomalo dell'app. Se utilizzi altre librerie in modo simile, devi verificare che non interferiscano con l'ottimizzazione delle app e, in caso contrario, evitarle.

Per definire le classi in modo compatibile con le regole del consumatore di Gson, utilizza il seguente snippet come riferimento:

class User(@com.google.gson.annotations.SerializedName("name") val name: String)
class UserList(@com.google.gson.annotations.SerializedName("users") val users: List<User>)

Tieni presente che Room, Hilt e Moshi con la generazione di codice creano tipi definiti dall'app, ma utilizzano la generazione di codice per evitare la necessità di reflection.