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 ramoelse {}
. 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
|
Dipendenze libreria |
In una libreria AAR:
In una libreria JAR: 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
Comprimi i contenuti di questa directory:
cd app/build/intermediates/cmake/universal/release/obj
zip -r symbols.zip .
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 mantiene solo quelle che è certo siano 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 ulteriori 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 tuo 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 ramoelse {}
. - 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
di risorse che vengono 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.