Riduci, offusca e ottimizza la tua app

Per ridurre le dimensioni dell'app e ridurle il più possibile, devi ottimizzare e minimizzare la build della release con isMinifyEnabled = true.

Ciò consente la contrazione, che rimuove il codice e le risorse inutilizzati. offuscamento, che abbrevia i nomi dei corsi e dei membri della tua app; e ottimizzazione, che applica strategie più aggressive per ridurre ulteriormente le dimensioni e migliorare le prestazioni dell'app. In questa pagina viene descritto come R8 esegue queste attività di compilazione per il tuo progetto e come puoi personalizzare che li rappresentano.

Quando crei il progetto utilizzando plug-in Android per Gradle 3.4.0 o versioni successive; Il plug-in non utilizza più ProGuard per eseguire l'ottimizzazione del codice in fase di compilazione. Il plug-in utilizza invece il compilatore R8 per gestire quanto segue attività di compilazione:

  • La riduzione del codice o l'abbattimento degli alberi: rileva e rimuove in modo sicuro i contenuti inutilizzati classi, campi, metodi e attributi della tua app e della relativa libreria (il che lo rende uno strumento prezioso per lavorare limite di riferimento di 64.000). Ad esempio, se utilizzi solo poche API di una dipendenza da una libreria, la riduzione consente di identificare che la tua app non utilizza e rimuovi soltanto quel codice dall'app. A per saperne di più, vai alla sezione su come ridurre il codice.
  • Riduzione delle risorse: rimuove le risorse inutilizzate dall'app in pacchetto, incluse le risorse inutilizzate nelle dipendenze di libreria dell'app. Funziona in congiuntamente alla riduzione del codice, in modo che una volta rimosso il codice inutilizzato, puoi rimuovere in sicurezza anche tutte le risorse a cui non si fa più riferimento. Per ulteriori informazioni vai alla sezione su come ridurre le risorse.
  • Ottimizzazione: ispeziona e riscrive il codice per migliorare il runtime delle prestazioni e a ridurre ulteriormente le dimensioni dei file DEX dell'app. Questo migliora le prestazioni di runtime del codice fino al 30%, migliorando drasticamente l'avvio e la durata del frame. Ad esempio, se R8 rileva che else {} ramo per una determinata istruzione if/else non è mai stata utilizzata, R8 rimuove il codice nel ramo else {}. Per saperne di più, vai alla sezione su ottimizzazione del codice.
  • Offuscamento (o minimizzazione degli identificatori): abbrevia il nome delle classi. e membri, con conseguente riduzione delle dimensioni dei file DEX. Per scoprire di più, visita la sezione su come offuscare il codice.

Quando crei la versione di release della tua app, R8 può essere configurato per eseguire le attività di compilazione descritte sopra. Puoi anche disattivare o personalizzare il comportamento di R8 mediante i file di regole ProGuard. Infatti, R8 funziona con tutti i file di regole ProGuard esistenti, quindi l'aggiornamento del plug-in Android per Gradle per l'utilizzo di R8 non dovrebbe richiedere la modifica le regole esistenti.

Attiva restringimento, offuscamento e ottimizzazione

Quando si usa Android Studio 3.4 o il plug-in Android Gradle 3.4.0 e versioni successive, R8 è il compilatore predefinito che converte il bytecode Java del progetto disponibile sulla piattaforma Android. Tuttavia, quando crei un nuovo progetto usando Android Studio, la riduzione, l'offuscamento e l'ottimizzazione del codice non sono sono abilitate per impostazione predefinita. Il motivo è che queste ottimizzazioni in fase di compilazione aumentano di tempo per il tuo progetto e potrebbero introdurre bug se non li personalizzi il codice da conservare.

Quindi, è meglio abilitare queste attività in fase di compilazione durante la creazione della versione finale dell'app che test prima della pubblicazione. Per consentire il restringimento, l'offuscamento e ottimizzazione, includi quanto segue nello script di build a livello di progetto.

Kotlin

android {
    buildTypes {
        getByName("release") {
            // Enables code shrinking, obfuscation, and optimization for only
            // your project's release build type. Make sure to use a build
            // variant with `isDebuggable=false`.
            isMinifyEnabled = true

            // Enables resource shrinking, which is performed by the
            // Android Gradle plugin.
            isShrinkResources = true

            proguardFiles(
                // Includes the default ProGuard rules files that are packaged with
                // the Android Gradle plugin. To learn more, go to the section about
                // R8 configuration files.
                getDefaultProguardFile("proguard-android-optimize.txt"),

                // Includes a local, custom Proguard rules file
                "proguard-rules.pro"
            )
        }
    }
    ...
}

Alla moda

android {
    buildTypes {
        release {
            // Enables code shrinking, obfuscation, and optimization for only
            // your project's release build type. Make sure to use a build
            // variant with `debuggable false`.
            minifyEnabled true

            // Enables resource shrinking, which is performed by the
            // Android Gradle plugin.
            shrinkResources true

            // Includes the default ProGuard rules files that are packaged with
            // the Android Gradle plugin. To learn more, go to the section about
            // R8 configuration files.
            proguardFiles getDefaultProguardFile(
                    'proguard-android-optimize.txt'),
                    'proguard-rules.pro'
        }
    }
    ...
}

File di configurazione R8

R8 utilizza i file di regole ProGuard per modificare il comportamento predefinito e, Comprendere la struttura dell'app, ad esempio le classi che fungono da punti di contatto. nel codice della tua app. Sebbene sia possibile modificare alcuni di questi file delle regole, le regole possono essere generate automaticamente da strumenti di compilazione, come AAPT2, ereditati dalle dipendenze di libreria dell'app. Nella tabella che segue vengono descritti i dei file di regole ProGuard utilizzati da R8.

Origine Posizione Descrizione
Android Studio <module-dir>/proguard-rules.pro Quando crei un nuovo modulo utilizzando Android Studio, l'IDE crea un proguard-rules.pro nella directory radice del modulo in questione.

Per impostazione predefinita, questo file non applica alcuna regola. Quindi, includi la tua le regole di ProGuard, ad esempio personalizzato mantenere le regole.

Plug-in Android per Gradle Generato dal plug-in Android Gradle al momento della compilazione. Il plug-in Android Gradle genera proguard-android-optimize.txt, che include regole che sono è utile per la maggior parte dei progetti Android e consente @Keep* annotazioni.

Per impostazione predefinita, quando crei un nuovo modulo utilizzando Android Studio, il campo lo script di build include questo file delle regole nella build della release per te.

Nota:il plug-in Android Gradle include una serie aggiuntiva di ProGuard predefiniti di regole, ma è consigliabile utilizzare proguard-android-optimize.txt.

Dipendenze libreria

In una libreria AAR:
proguard.txt

In una libreria JAR:
META-INF/proguard/<ProGuard-rules-file>

Oltre a queste posizioni, è disponibile anche il plug-in Android per Gradle 3.6 o versioni successive supporta le regole di riduzione scelte come target.

Se una libreria AAR o JAR viene pubblicata con il proprio file di regole e include la libreria come dipendenza in fase di compilazione, e applicare queste regole durante la compilazione del progetto.

Oltre alle regole convenzionali di ProGuard, il plug-in Android Gradle La versione 3.6 o successiva supporta anche regole di riduzione scelte come target. Queste sono regole che hanno come target shrinker specifici (R8 o ProGuard), nonché versioni più ridotte.

L'utilizzo di file di regole pacchettizzati con le librerie è utile se per il corretto funzionamento della libreria, ovvero sviluppatore ha eseguito la procedura di risoluzione dei problemi per te.

Tuttavia, tieni presente che, poiché le regole sono additive, alcune regole incluse in una dipendenza della libreria non possono essere rimosse, ciò potrebbe influire sulla compilazione di altre parti della tua app. Ad esempio, se un include una regola per disabilitare le ottimizzazioni del codice, che disabilita ottimizzazioni per l'intero progetto.

Strumento 2 per i pacchetti di asset Android (AAPT2) Dopo aver creato il progetto con minifyEnabled true: <module-dir>/build/intermediates/aapt_proguard_file/.../aapt_rules.txt AAPT2 genera regole Keep basate sui riferimenti alle classi nel file manifest, layout e altre risorse dell'app. Ad esempio, AAPT2 include una mantenere la regola per ogni attività registrata nel file manifest della tua app come il punto di accesso.
File di configurazione personalizzati Per impostazione predefinita, quando crei un nuovo modulo utilizzando Android Studio, crea <module-dir>/proguard-rules.pro per consentirti di aggiungere il tuo le regole del caso. Puoi includere configurazioni aggiuntive, mentre R8 le applica al momento della compilazione.

Quando imposti la proprietà minifyEnabled su true, R8 combina le regole di tutti le fonti disponibili elencate sopra. È importante ricordare che risolvere i problemi con R8, perché altre dipendenze del tempo di compilazione come le dipendenze della libreria, potrebbero introdurre modifiche al comportamento R8 di cui non si è a conoscenza.

Per generare un report completo di tutte le regole applicate da R8 durante la creazione includi quanto segue nel file proguard-rules.pro del modulo:

// You can specify any path and filename.
-printconfiguration ~/tmp/full-r8-config.txt

Regole di riduzione target

Il plug-in Android per Gradle 3.6 o versioni successive supporta le librerie regole che hanno come target shrinker specifici (R8 o ProGuard) e versioni di shrinker specifici. Questo Consente agli sviluppatori di librerie di personalizzare le regole in modo che funzionino in modo ottimale nei progetti che usano nuove versioni più restringenti, permettendo al contempo che le regole esistenti utilizzate in progetti con versioni più vecchie di shrinker.

Per specificare regole di riduzione target, gli sviluppatori di librerie dovranno includerle in punti specifici all'interno di una libreria AAR o JAR, come descritto di seguito.

In an AAR library:
    proguard.txt (legacy location)
    classes.jar
    └── META-INF
        └── com.android.tools (targeted shrink rules location)
            ├── r8-from-<X>-upto-<Y>/<R8-rules-file>
            └── proguard-from-<X>-upto-<Y>/<ProGuard-rules-file>

In a JAR library:
    META-INF
    ├── proguard/<ProGuard-rules-file> (legacy location)
    └── com.android.tools (targeted shrink rules location)
        ├── r8-from-<X>-upto-<Y>/<R8-rules-file>
        └── proguard-from-<X>-upto-<Y>/<ProGuard-rules-file>

Ciò significa che le regole di riduzione target vengono archiviate nell'META-INF/com.android.tools di un file JAR o nella directory META-INF/com.android.tools all'interno classes.jar di un AAR.

Sotto questa directory possono essere presenti più directory con nomi nel formato di r8-from-<X>-upto-<Y> o proguard-from-<X>-upto-<Y> per indicare quale delle quali si riducono le regole all'interno delle directory. Tieni presente che le parti -from-<X> e -upto-<Y> sono facoltative, la versione <Y> è esclusivo e gli intervalli di versioni devono essere continui.

Ad esempio, r8-upto-8.0.0, r8-from-8.0.0-upto-8.2.0 e r8-from-8.2.0 e formeranno un insieme valido di regole di riduzione scelte come target. Le regole secondo le La directory r8-from-8.0.0-upto-8.2.0 verrà utilizzata da R8 dalla versione 8.0.0 fino a ma esclusa la versione 8.2.0.

Alla luce di queste informazioni, il plug-in Android Gradle 3.6 o versioni successive selezionerà dalle directory R8 corrispondenti. Se una libreria non specifica di restringimento delle regole, il plug-in Android Gradle selezionerà le regole località (proguard.txt per un AAR o META-INF/proguard/<ProGuard-rules-file> per un JAR).

Gli sviluppatori di librerie possono scegliere se includere regole di riduzione target o legacy le regole ProGuard nelle loro librerie o in entrambi i tipi se vogliono mantenere compatibilità con il plug-in Android per Gradle precedente alla 3.6 o con altri strumenti.

Includi configurazioni aggiuntive

Quando crei un nuovo progetto o modulo utilizzando Android Studio, l'IDE crea un <module-dir>/proguard-rules.pro per includere le tue regole. Tu puoi anche includere regole aggiuntive di altri file aggiungendole all'elenco proguardFiles nello script di build del tuo modulo.

Ad esempio, puoi aggiungere regole specifiche per ogni variante della build aggiungendo un'altra proprietà proguardFiles nel blocco productFlavor corrispondente. La seguente file Gradle aggiunge flavor2-rules.pro alla versione di prodotto flavor2. Ora flavor2 utilizza tutte e tre le regole ProGuard perché quelle dell'release vengono applicati anche i blocchi.

Inoltre, puoi aggiungere la proprietà testProguardFiles, che specifica una elenco di file ProGuard inclusi soltanto nell'APK di prova:

Kotlin

android {
    ...
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                // List additional ProGuard rules for the given build type here. By default,
                // Android Studio creates and includes an empty rules file for you (located
                // at the root directory of each module).
                "proguard-rules.pro"
            )
            testProguardFiles(
                // The proguard files listed here are included in the
                // test APK only.
                "test-proguard-rules.pro"
            )
        }
    }
    flavorDimensions.add("version")
    productFlavors {
        create("flavor1") {
            ...
        }
        create("flavor2") {
            proguardFile("flavor2-rules.pro")
        }
    }
}

Alla moda

android {
    ...
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles
                getDefaultProguardFile('proguard-android-optimize.txt'),
                // List additional ProGuard rules for the given build type here. By default,
                // Android Studio creates and includes an empty rules file for you (located
                // at the root directory of each module).
                'proguard-rules.pro'
            testProguardFiles
                // The proguard files listed here are included in the
                // test APK only.
                'test-proguard-rules.pro'
        }
    }
    flavorDimensions "version"
    productFlavors {
        flavor1 {
            ...
        }
        flavor2 {
            proguardFile 'flavor2-rules.pro'
        }
    }
}

Riduci il codice

La riduzione del codice con R8 è abilitata per impostazione predefinita quando imposti minifyEnabled a true.

La riduzione del codice (nota anche come "scuotimento degli alberi") è il processo di rimozione del codice secondo R8 non è necessaria in fase di runtime. Questo processo può ridurre notevolmente le dimensioni dell'app se, ad esempio, l'app include molte dipendenze di libreria, utilizza solo una piccola parte delle sue funzionalità.

Per ridurre il codice della tua app, prima R8 determina tutti i punti di ingresso nella basato sull'insieme combinato di file di configurazione. Questi punti di contatto includono tutti i corsi che la piattaforma Android può utilizzare per aprire le attività o i servizi della tua app. Partendo da ogni punto di ingresso, R8 esegue l'ispezione il codice della tua app per creare un grafico di tutti i metodi, delle variabili membro e di altre i corsi a cui la tua app potrebbe accedere in fase di runtime. Codice non connesso a il grafico è considerato non raggiungibile e potrebbe essere rimosso dall'app.

La figura 1 mostra un'app con una dipendenza dalla libreria di runtime. Durante l'ispezione del il codice dell'app, R8 determina che i metodi foo(), faz() e bar() sono raggiungibile dal punto di ingresso MainActivity.class. Tuttavia, i modelli OkayApi.class o il relativo metodo baz() non vengono mai utilizzati dalla tua app in fase di runtime e R8 rimuove questo codice quando si riduce la tua app.

Figura 1. Al momento della compilazione, R8 crea basato sulle regole di conservazione combinate del progetto per determinare il codice non raggiungibile.

R8 determina i punti di ingresso tramite -keep regole nel File di configurazione R8: Vale a dire, mantieni le regole specificate che R8 non deve ignorare durante la riduzione dell'app, mentre R8 considera queste classi come possibili punti di ingresso nella tua app. Plug-in Android per Gradle e AAPT2 generano automaticamente le regole Keep richieste dalla maggior parte delle app i progetti per te, come le attività, le viste e i servizi della tua app. Tuttavia, Se devi personalizzare questo comportamento predefinito con regole di conservazione aggiuntive, leggi questa sezione su come personalizzare il codice da conservare.

Se invece vuoi solo ridurre le dimensioni delle risorse della tua app, passa alla sezione su come condurre le risorse.

Tieni presente che, se un progetto biblioteca viene ridotto, un'app che dipende da quella libreria include classi della libreria ridotta. Potresti dover modificare le regole di conservazione della libreria non ci sono corsi nell'APK della raccolta. Se crei e pubblichi contenuti una libreria in formato AAR, i file JAR locali da cui dipende la libreria non sono ridotto nel file AAR.

Personalizza il codice da conservare

Nella maggior parte dei casi, il file di regole di ProGuard predefinito (proguard-android-optimize.txt) sia sufficiente affinché R8 rimuova solo il codice inutilizzato. Tuttavia, alcune situazioni sono difficili da analizzare correttamente per R8 e potrebbe il codice di cui la tua app ha effettivamente bisogno. Alcuni esempi di casi in cui potrebbe essere rimossa in modo errato il codice include:

  • Quando la tua app chiama un metodo dalla Java Native Interface (JNI)
  • Quando la tua app cerca codice in fase di runtime (ad esempio con riflessione)

Il test dell'app dovrebbe rivelare eventuali errori causati dalla rimozione inappropriata ma puoi anche controllare quale codice è stato rimosso generando un report sul codice rimosso.

Per correggere gli errori e forzare R8 a conservare un determinato codice, aggiungi un -keep nel file delle regole di ProGuard. Ad esempio:

-keep public class MyClass

In alternativa, puoi aggiungere @Keep al codice che vuoi che vuoi conservare. Se aggiungi @Keep a un corso, l'intero corso rimane invariato. Se lo aggiungi a un metodo o a un campo, verranno mantenuti anche il metodo/campo (e il relativo nome) il nome della classe intatto. Tieni presente che questa annotazione è disponibile solo quando utilizzi il Raccolta di annotazioni AndroidX Inoltre, quando includi il file delle regole di ProGuard contenuto nell'app Plug-in Gradle, come descritto nella sezione su come abilita la riduzione.

Quando utilizzi l'opzione -keep, ci sono molte considerazioni da fare. della per ulteriori informazioni sulla personalizzazione del file delle regole, leggi Manuale di ProGuard. La Risoluzione dei problemi descrive altri problemi comuni che potresti riscontrare quando il codice viene smontato.

Rimuovi le librerie native

Per impostazione predefinita, le librerie di codice native vengono eliminate nelle build di release della tua app. Questa rimozione consiste nella rimozione della tabella dei simboli e delle informazioni di debug contenuti in eventuali librerie native utilizzate dalla tua app. Rimozione del codice nativo le librerie consentono di risparmiare notevolmente sulle dimensioni; ma è impossibile diagnosticare si arresta in modo anomalo su Google Play Console a causa di informazioni mancanti (ad esempio nomi di classi e funzioni).

Supporto nativo degli arresti anomali

Google Play Console segnala gli arresti anomali nativi in Android vitals. Con alcune puoi generare e caricare un file di simboli di debug nativo per la tua app. Questo file consente le analisi dello stack in caso di arresto anomalo nativo simbolizzate (che includono elementi di classe e nomi di funzioni) in Android vitals per aiutarti a eseguire il debug dell'app in produzione. Questi passaggi variano a seconda della versione del plug-in Android per Gradle utilizzata in del progetto e l'output della build del progetto.

Plug-in Android per Gradle versione 4.1 o successive

Se il tuo progetto crea un Android App Bundle, puoi includere automaticamente il campo di simboli di debug nativi. Per includere questo file nelle build di release, aggiungi seguente al file build.gradle.kts dell'app:

android.buildTypes.release.ndk.debugSymbolLevel = { SYMBOL_TABLE | FULL }

Seleziona il livello del simbolo di debug tra i seguenti:

  • Usa SYMBOL_TABLE per recuperare i nomi delle funzioni nelle analisi dello stack simbolizzate di Play Console. Questo livello supporta le lapidi.
  • Usa FULL per recuperare nomi di funzioni, file e numeri di riga nella e analisi dello stack simbolizzate.

Se il tuo progetto crea un APK, usa l'impostazione di build build.gradle.kts mostrata per generare il file di simboli di debug nativo separatamente. Manualmente caricare il file di simboli di debug nativi a Google Play Console. Nell'ambito del processo di creazione, Android Gradle Il plug-in genera questo file nella seguente posizione del progetto:

app/build/outputs/native-debug-symbols/variant-name/native-debug-symbols.zip

Plug-in Android per Gradle versione 4.0 o precedente (e altri sistemi di build)

Nell'ambito del processo di compilazione, il plug-in Android Gradle conserva una copia in una directory di progetto. La struttura di questa directory è simile alla seguente:

app/build/intermediates/cmake/universal/release/obj/
├── armeabi-v7a/
│   ├── libgameengine.so
│   ├── libothercode.so
│   └── libvideocodec.so
├── arm64-v8a/
│   ├── libgameengine.so
│   ├── libothercode.so
│   └── libvideocodec.so
├── x86/
│   ├── libgameengine.so
│   ├── libothercode.so
│   └── libvideocodec.so
└── x86_64/
    ├── libgameengine.so
    ├── libothercode.so
    └── libvideocodec.so
  1. Comprimi i contenuti di questa directory:

    cd app/build/intermediates/cmake/universal/release/obj
    zip -r symbols.zip .
    
  2. Manualmente carica il file symbols.zip a Google Play Console.

Riduci le risorse

La riduzione delle risorse funziona solo insieme alla riduzione del codice. Dopo il lo shrinker rimuove tutto il codice inutilizzato, lo shrinker di risorse può le risorse che l'app continua a utilizzare. Ciò è particolarmente vero quando aggiungi codice librerie che includono risorse: devi rimuovere il codice libreria inutilizzato in modo che le risorse della libreria diventano senza riferimenti e, di conseguenza, rimovibili dalla risorsa si rimpicciolisce.

Per abilitare la riduzione delle risorse, imposta la proprietà shrinkResources a true nello script di build (insieme minifyEnabled per la riduzione del codice). Ad esempio:

Kotlin

android {
    ...
    buildTypes {
        getByName("release") {
            isShrinkResources = true
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android.txt"),
                "proguard-rules.pro"
            )
        }
    }
}

Alla moda

android {
    ...
    buildTypes {
        release {
            shrinkResources true
            minifyEnabled true
            proguardFiles
                getDefaultProguardFile('proguard-android.txt'),
                'proguard-rules.pro'
        }
    }
}

Se non hai già creato la tua app utilizzando minifyEnabled per riduzione del codice, quindi prova a farlo prima di abilitare shrinkResources, perché potresti dover modificare il file proguard-rules.pro in mantenere le classi o i metodi che vengono creati o richiamati dinamicamente prima di inizia a rimuovere le risorse.

Personalizza le risorse da conservare

Se ci sono risorse specifiche che vuoi conservare o ignorare, crea un file XML file nel tuo progetto con un tag <resources> e specifica ogni risorsa da mantenere nell'attributo tools:keep e ogni risorsa per ignora nell'attributo tools:discard. Entrambi gli attributi accettano un un elenco separato da virgole di nomi delle risorse. Puoi utilizzare l'asterisco come carattere jolly.

Ad esempio:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"
    tools:discard="@layout/unused2" />

Salva il file nelle risorse del tuo progetto, ad esempio in res/raw/my.package.keep.xml. La build non pacchettizza questo file dell'app.

Nota: assicurati di utilizzare un nome univoco per il file keep. Quando librerie diverse vengono collegate tra loro, le loro regole di Keep sarebbero in conflitto altrimenti, potrebbero verificarsi problemi con regole ignorate o non necessarie Google Cloud.

Specificare quali risorse scartare può sembrare un po' sciocco quando eliminale, ma questo può essere utile quando utilizzi varianti della build. Per Ad esempio, potresti inserire tutte le risorse nella directory comune quindi crea un file my.package.build.variant.keep.xml diverso per ogni una variante di build quando sai che una determinata risorsa sembra essere usata nel codice (e pertanto non verranno rimosse dallo shrinker), ma sai che in realtà non utilizzata per la variante di build specificata. È anche possibile che gli strumenti di creazione ha identificato erroneamente una risorsa come necessario, il che è possibile perché il compilatore aggiunge gli ID risorsa in linea e l'analizzatore di risorse potrebbe non conoscere la differenza tra una risorsa a cui viene fatto effettivamente riferimento e un valore intero nel codice che hanno lo stesso valore.

Attiva controlli rigidi dei riferimenti

Normalmente, lo shrinker di risorse può determinare con precisione se una risorsa . Tuttavia, se il codice effettua una chiamata Resources.getIdentifier() (o se una delle tue librerie lo fa, il file AppCompat libreria tradizionale, significa che il codice cerca i nomi delle risorse in base generate in modo dinamico. In questo caso, lo shrinker si comporta in modo difensivo per impostazione predefinita e contrassegna tutte le risorse con un formato del nome corrispondente come potenzialmente utilizzate e non disponibili per la rimozione.

Ad esempio, il seguente codice fa sì che tutte le risorse con Prefisso img_ da contrassegnare come utilizzato.

Kotlin

val name = String.format("img_%1d", angle + 1)
val res = resources.getIdentifier(name, "drawable", packageName)

Java

String name = String.format("img_%1d", angle + 1);
res = getResources().getIdentifier(name, "drawable", getPackageName());

Lo shrinker di risorse esamina anche tutte le costanti stringa in di risorse e varie risorse res/raw/, alla ricerca di Gli URL hanno un formato simile a file:///android_res/drawable//ic_plus_anim_016.png. Se rileva stringhe come questa o altre che sembrano essere utilizzate per creare URL come questa, non li rimuove.

Questi sono esempi della modalità di riduzione sicura abilitata per impostazione predefinita. Tuttavia, puoi disattivare questa opzione elaborazione e specificare che lo shrinker di risorse mantenga solo le risorse effettivamente utilizzate. A fai questo, imposta shrinkMode su strict nel keep.xml, come segue:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:shrinkMode="strict" />

Se abiliti la modalità di riduzione rigorosa e il codice fa riferimento anche con stringhe generate dinamicamente, come mostrato sopra, devi mantenere manualmente queste risorse utilizzando l'attributo tools:keep.

Rimuovi le risorse alternative inutilizzate

Lo shrinker di risorse Gradle rimuove solo le risorse a cui non viene fatto riferimento con il codice dell'app, il che significa che non rimuoverà risorse alternative per le diverse configurazioni dei dispositivi. Se necessario, puoi utilizzare la proprietà resConfigs del plug-in Android Gradle per rimuovere i file di risorse alternativi di cui la tua app non ha bisogno.

Ad esempio, se utilizzi una libreria che include risorse linguistiche (come AppCompat o Google Play Services), l'app include tutti le stringhe di lingua tradotte per i messaggi in quelle librerie, a prescindere dal fatto che mentre il resto dell'app viene tradotto o meno nelle stesse lingue. Se desideri mantenere solo le lingue supportate ufficialmente dalla tua app, puoi specificare queste lingue utilizzando la proprietà resConfig. Qualsiasi risorsa per le lingue non specificate vengono rimosse.

Lo snippet seguente mostra come limitare le risorse linguistiche Inglese e francese:

Kotlin

android {
    defaultConfig {
        ...
        resourceConfigurations.addAll(listOf("en", "fr"))
    }
}

Alla moda

android {
    defaultConfig {
        ...
        resConfigs "en", "fr"
    }
}

Quando rilasci un'app utilizzando il formato Android App Bundle (per impostazione predefinita) le lingue configurate sul dispositivo di un utente vengono scaricate al momento dell'installazione dell'app. Analogamente, solo le risorse che corrispondono alla densità dello schermo del dispositivo Nel download sono incluse le librerie corrispondenti all'ABI del dispositivo. Per maggiori informazioni informazioni si riferiscono App per Android Configurazione bundle.

Per le app precedenti che vengono rilasciate con APK (create prima di agosto 2021), puoi: personalizza la densità dello schermo o le risorse ABI da includere nell'APK creare diversi APK che ognuno ha come target una diversa configurazione del dispositivo.

Unire le risorse duplicate

Per impostazione predefinita, Gradle unisce anche risorse con nomi identici, con lo stesso nome, che potrebbero trovarsi in cartelle di risorse diverse. Questo comportamento non è controllato dalla proprietà shrinkResources, non possono essere disabilitati perché è necessario evitare errori quando a quello che il codice cerca.

L'unione delle risorse avviene solo quando due o più file condividono il nome, il tipo e il qualificatore della risorsa. Gradle seleziona il file che prende in considerazione la scelta migliore tra i duplicati (in base a un ordine di priorità descritto di seguito) e passa solo quella risorsa all'AAPT per la distribuzione l'artefatto finale.

Gradle cerca risorse duplicate nelle seguenti posizioni:

  • Le risorse principali, associate all'insieme di origini principale, in genere si trova in src/main/res/.
  • Le varianti si sovrappongono in base al tipo e alle versioni di build.
  • Le dipendenze del progetto di libreria.

Gradle unisce le risorse duplicate nel seguente ordine di priorità a cascata:

Dipendenze → Principale → Versione build → Tipo di build

Ad esempio, se una risorsa duplicata viene visualizzata sia nelle risorse principali che una versione build, Gradle ne seleziona quella.

Se nello stesso set di origini vengono visualizzate risorse identiche, Gradle non può essere unita ed emette un errore di unione delle risorse. Ciò può accadere se definisci più nella proprietà sourceSet del tuo build.gradle.kts, ad esempio se entrambi src/main/res/ e src/main/res2/ contengono risorse identiche.

Offuscare il codice

Lo scopo dell'offuscamento è ridurre le dimensioni dell'app abbreviando i nomi dei le classi, i metodi e i campi della tua app. Di seguito è riportato un esempio dell'offuscamento con R8:

androidx.appcompat.app.ActionBarDrawerToggle$DelegateProvider -> a.a.a.b:
androidx.appcompat.app.AlertController -> androidx.appcompat.app.AlertController:
    android.content.Context mContext -> a
    int mListItemLayout -> O
    int mViewSpacingRight -> l
    android.widget.Button mButtonNeutral -> w
    int mMultiChoiceItemLayout -> M
    boolean mShowTitle -> P
    int mViewSpacingLeft -> j
    int mButtonPanelSideLayout -> K

L'offuscamento non rimuove il codice dall'app, ma consente un significativo risparmio in termini di dimensioni possono essere visualizzati nelle app con file DEX che indicizzano molte classi, metodi e campi. Tuttavia, poiché l'offuscamento rinomina diverse parti del codice, alcune attività, come l'ispezione delle analisi dello stack, richiedono strumenti aggiuntivi. Per comprendere le stacktrace dopo l'offuscamento, leggi la sezione su come decodificare un'analisi dello stack offuscata.

Inoltre, se il codice si basa su una denominazione prevedibile per i metodi della tua app e classi. Quando usi la riflessione, ad esempio, dovresti trattare quelle le firme come punti di ingresso e specificare le relative regole di conservazione, come descritto su come personalizzare il codice da conservare. Queste mantengono indicano a R8 di non solo mantenere quel codice nel DEX finale dell'app, ma anche il suo nome originale.

Decodifica un'analisi dello stack offuscata

Dopo che R8 ha offuscato il codice, è difficile comprendere un'analisi dello stack (se non impossibile) perché i nomi delle classi e dei metodi potrebbero essere stati è cambiato. Per ottenere l'analisi dello stack originale, ritracciare l'analisi dello stack.

Ottimizzazione del codice

Per ottimizzare ulteriormente la tua app, R8 analizza il codice a un livello più approfondito per rimuovere il codice inutilizzato o, se possibile, riscrivere il codice per e avere un livello di dettaglio minore. Di seguito sono riportati alcuni esempi di queste ottimizzazioni:

  • Se il tuo codice non prende mai il ramo else {} per una determinata istruzione if/else, R8 potrebbe rimuovere il codice per il ramo else {}.
  • Se il codice chiama un metodo solo in alcune posizioni, R8 potrebbe rimuovere il metodo e incorporarlo nei pochi siti di chiamata.
  • Se R8 determina che una classe ha una sola sottoclasse unica e la classe non viene creata un'istanza (ad esempio, una classe base astratta usata solo una classe di implementazione concreta), R8 può combinare le due classi rimuovere un corso dall'app.
  • Per saperne di più, leggi il Post del blog sull'ottimizzazione R8 di Jake Wharton.

R8 non consente di disabilitare o abilitare ottimizzazioni discrete né di modificare il comportamento di un'ottimizzazione. Infatti, R8 ignora qualsiasi regola ProGuard che tenta di per modificare le ottimizzazioni predefinite, come -optimizations e -optimizationpasses. Questa restrizione è importante perché, dato che R8 continua migliorare, mantenere un comportamento standard per le ottimizzazioni aiuta Android Il team di Studio risolverà con facilità gli eventuali problemi riscontrati.

Tieni presente che l'attivazione dell'ottimizzazione modificherà le analisi dello stack per il tuo un'applicazione. Ad esempio, l'incorporamento rimuove gli stack frame. Consulta la sezione sulle retracing per imparare a ottenere le analisi dello stack originali.

Impatto sulle prestazioni di runtime

Se le operazioni di riduzione, offuscamento e ottimizzazione sono tutte abilitate, R8 migliorerà prestazioni del codice in fase di runtime (inclusi avvio e durata frame nel thread dell'interfaccia utente) fino al 30%. La disattivazione di una di queste opzioni limita drasticamente l'insieme di ottimizzazioni R8.

Se R8 è abilitato, dovresti anche creare profili di startup per prestazioni delle startup ancora migliori.

Consenti ottimizzazioni più aggressive

R8 include una serie di ottimizzazioni aggiuntive (indicate come "modalità completa") che si comporta in modo diverso da ProGuard. Queste ottimizzazioni vengono attivate predefinito da plug-in Android per Gradle versione 8.0.0.

Puoi disattivare queste ottimizzazioni aggiuntive includendo quanto segue in il file gradle.properties del tuo progetto:

android.enableR8.fullMode=false

Poiché le ottimizzazioni aggiuntive fanno sì che R8 si comporti diversamente da ProGuard, potrebbero richiedere l'inclusione di regole ProGuard aggiuntive per evitare se utilizzi regole progettate per ProGuard. Ad esempio, supponiamo che il codice fa riferimento a una classe tramite l'API Java Reflection. Quando non utilizzi "modalità completa" R8 presuppone che l'utente intenda esaminare e manipolare gli oggetti alla classe in fase di runtime, anche se il tuo codice in realtà non lo fa, e lo strumento mantiene la classe e il suo inizializzatore statico.

Tuttavia, quando si utilizza la "modalità completa", R8 non fa questa ipotesi e, se R8 asserisce che il codice non utilizza mai la classe in fase di runtime, rimuove dal file DEX finale dell'app. Vale a dire, se vuoi mantenere il corso e i suoi un inizializzatore statico, devi includere una regola Keep nel file delle regole. quello.

Se riscontri problemi durante l'utilizzo della "modalità completa" di R8, consulta la Pagina delle domande frequenti su R8 una possibile soluzione. Se non riesci a risolvere il problema, segnala un bug.

Ripercorrere le analisi dello stack

Il codice elaborato da R8 viene modificato in vari modi per eseguire analisi dello stack più difficili da capire perché le analisi dello stack non corrispondono esattamente al codice sorgente. Questo ad esempio le modifiche ai numeri di riga quando le informazioni di debug sono non vengono conservati. Ciò può essere dovuto a ottimizzazioni come l'incorporamento e la struttura. La che contribuisce maggiormente è l'offuscamento, in cui anche le classi e i metodi cambia i nomi.

Per recuperare l'analisi dello stack originale, R8 fornisce a riga di comando retrace, in bundle con il pacchetto degli strumenti a riga di comando.

Per supportare il ritracciamento delle analisi dello stack dell'applicazione, devi assicurarti che conserva informazioni sufficienti da ripercorrere aggiungendo quanto segue: regole nel file proguard-rules.pro del modulo:

-keepattributes LineNumberTable,SourceFile
-renamesourcefileattribute SourceFile

L'attributo LineNumberTable conserva le informazioni sulla posizione con metodi per cui queste posizioni vengono stampate nelle analisi dello stack. Attributo SourceFile assicura che tutti i potenziali runtime stampino effettivamente le informazioni sulla posizione. L'istruzione -renamesourcefileattribute imposta il nome del file di origine nello stack fino a SourceFile. Il nome effettivo del file di origine originale non è richiesta durante il ritracciamento perché il file di mappatura contiene il file sorgente originale.

R8 crea un file mapping.txt ogni volta che viene eseguito, che contiene le informazioni necessarie per mappare le analisi dello stack all'originale delle analisi dello stack. Android Studio salva il file nel <module-name>/build/outputs/mapping/<build-type>/ .

Quando pubblichi la tua app su Google Play, puoi caricare il file mapping.txt per ogni versione dell'app. Quando pubblichi contenuti usando Android App Bundle, viene incluso automaticamente nei contenuti dell'app bundle. Google Google Play ripercorre le analisi dello stack in arrivo dai problemi segnalati dall'utente, così potrai puoi rivederli in Play Console. Per ulteriori informazioni, consulta il Centro assistenza articolo su come deoffuscare le analisi dello stack in caso di arresto anomalo.

Risolvere i problemi con R8

In questa sezione vengono descritte alcune strategie per la risoluzione dei problemi durante l'attivazione restringimento, offuscamento e ottimizzazione con R8. Se non trovi una soluzione al tuo problema di seguito, leggi anche Pagina delle domande frequenti su R8 e Guida alla risoluzione dei problemi di ProGuard.

Generare un report relativo al codice rimosso (o conservato)

Per aiutarti a risolvere determinati problemi relativi a R8, può essere utile visualizzare un report tutto il codice che R8 ha rimosso dalla tua app. Per ogni modulo che vuoi per generare questo report, aggiungi -printusage <output-dir>/usage.txt alla tua del file di regole. Quando abiliti R8 e crei la tua app, R8 restituisce un con il percorso e il nome file specificati. Il report sul codice rimosso è simile al seguente:

androidx.drawerlayout.R$attr
androidx.vectordrawable.R
androidx.appcompat.app.AppCompatDelegateImpl
    public void setSupportActionBar(androidx.appcompat.widget.Toolbar)
    public boolean hasWindowFeature(int)
    public void setHandleNativeActionModesEnabled(boolean)
    android.view.ViewGroup getSubDecor()
    public void setLocalNightMode(int)
    final androidx.appcompat.app.AppCompatDelegateImpl$AutoNightModeManager getAutoNightModeManager()
    public final androidx.appcompat.app.ActionBarDrawerToggle$Delegate getDrawerToggleDelegate()
    private static final boolean DEBUG
    private static final java.lang.String KEY_LOCAL_NIGHT_MODE
    static final java.lang.String EXCEPTION_HANDLER_MESSAGE_SUFFIX
...

Se invece vuoi visualizzare un report dei punti di ingresso da cui viene determinato R8 regole di Keep del progetto , includi -printseeds <output-dir>/seeds.txt nel di regole personalizzate. Quando abiliti R8 e crei la tua app, R8 genera un output. un report con il percorso e il nome file specificati. Il report sulla voce mantenuta ha un aspetto simile al seguente:

com.example.myapplication.MainActivity
androidx.appcompat.R$layout: int abc_action_menu_item_layout
androidx.appcompat.R$attr: int activityChooserViewStyle
androidx.appcompat.R$styleable: int MenuItem_android_id
androidx.appcompat.R$styleable: int[] CoordinatorLayout_Layout
androidx.lifecycle.FullLifecycleObserverAdapter
...

Risolvere i problemi relativi alla riduzione delle risorse

Quando riduci le risorse, lo strumento Build mostra un riepilogo e risorse rimosse dall'app. Devi prima fare clic su Attiva/disattiva visualizzazione. sul lato sinistro della finestra per visualizzare l'output di testo dettagliato da Gradle). Ad esempio:

:android:shrinkDebugResources
Removed unused resources: Resource data reduced from 2570KB to 1711KB: Removed 33%
:android:validateDebugSigning

Gradle crea anche un file di diagnostica denominato resources.txt in <module-name>/build/outputs/mapping/release/ (lo stesso come file di output di ProGuard). Questo file include dettagli come risorse fa riferimento ad altre risorse e quali risorse vengono utilizzate o rimosso.

Ad esempio, per scoprire perché @drawable/ic_plus_anim_016 è ancora nella tua app, apri il file resources.txt e cercalo nome del file. Potresti notare che vi viene fatto riferimento da un'altra risorsa, che segue:

16:25:48.005 [QUIET] [system.out] @drawable/add_schedule_fab_icon_anim : reachable=true
16:25:48.009 [QUIET] [system.out]     @drawable/ic_plus_anim_016

Ora devi sapere perché @drawable/add_schedule_fab_icon_anim è raggiungibile e se cerchi più in alto vedrai che la risorsa è elencata in "Le risorse raggiungibili principali sono:". Ciò significa che c'è un riferimento di codice a add_schedule_fab_icon_anim (ossia, l'ID R.drawable era nel codice raggiungibile).

Se non utilizzi il controllo rigoroso, gli ID risorsa possono essere contrassegnati come raggiungibili se ci sono costanti stringa che potrebbero essere utilizzate per costruire per le risorse caricate dinamicamente. In questo caso, se cerchi l'output di build per il nome della risorsa, potresti visualizzare un messaggio simile al seguente:

10:32:50.590 [QUIET] [system.out] Marking drawable:ic_plus_anim_016:2130837506
    used because it format-string matches string pool constant ic_plus_anim_%1$d.

Se vedi una di queste stringhe e sei sicuro che la stringa non sia per caricare dinamicamente la risorsa specifica, puoi utilizzare Attributo tools:discard per indicare al sistema di compilazione di rimuoverlo, come descritto nella sezione su come personalizzare le risorse da conservare.