Novità sul prodotto
Configurare e risolvere i problemi relativi alle regole Keep di R8
Lettura di 7 minuti
Nello sviluppo moderno di Android, la distribuzione di un'applicazione piccola, veloce e sicura è un'aspettativa fondamentale degli utenti. Lo strumento principale del sistema di compilazione Android per raggiungere questo obiettivo è l'ottimizzatore R8 , il compilatore che gestisce la rimozione di codice e risorse inutilizzati per la riduzione, la ridenominazione o la minimizzazione del codice e l'ottimizzazione delle app.
L'attivazione di R8 è un passaggio fondamentale per preparare un'app per la release, ma richiede agli sviluppatori di fornire indicazioni sotto forma di "regole di conservazione".
Dopo aver letto questo articolo, guarda il video della settimana di Performance Spotlight su come attivare, eseguire il debug e risolvere i problemi relativi all'ottimizzatore R8 su YouTube.
Perché sono necessarie le regole di conservazione
La necessità di scrivere regole di conservazione deriva da un conflitto fondamentale: R8 è uno strumento di analisi statica, ma le app per Android spesso si basano su pattern di esecuzione dinamica come la reflection o le chiamate in entrata e in uscita dal codice nativo utilizzando JNI (Java Native Interface).
R8 crea un grafico del codice utilizzato analizzando le chiamate dirette. Quando si accede al codice in modo dinamico, l'analisi statica di R8 non può prevederlo e lo identifica come non utilizzato e lo rimuove, causando arresti anomali in fase di runtime.
Una regola di conservazione è un'istruzione esplicita per il compilatore R8, che indica: "Questa classe, questo metodo o questo campo specifico è un punto di ingresso a cui verrà eseguito l'accesso in modo dinamico in fase di runtime. Devi conservarlo, anche se non riesci a trovare un riferimento diretto."
Per ulteriori dettagli sulle regole di conservazione, consulta la guida ufficiale.
Dove scrivere le regole di conservazione
Le regole di conservazione personalizzate per un'applicazione vengono scritte in un file di testo. Per convenzione, questo file si chiama proguard-rules.pro e si trova nella radice del modulo dell'app o della libreria. Questo file viene quindi specificato nel tipo di compilazione release del file build.gradle.kts del modulo.
release {
isShrinkResources = true
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
)
}Utilizzare il file predefinito corretto
Il metodo getDefaultProguardFile importa un insieme predefinito di regole fornite dall'SDK Android. Se utilizzi il file sbagliato, l'app potrebbe non essere ottimizzata. Assicurati di utilizzare proguard-android-optimize.txt. Questo file fornisce le regole di conservazione predefinite per i componenti Android standard e consente le ottimizzazioni del codice di R8. La versione obsoleta di proguard-android.txt fornisce solo le regole di conservazione, ma non attiva le ottimizzazioni di R8.
Poiché si tratta di un grave problema di prestazioni, a partire dal Feature Drop 3 di Android Studio Narwhal, inizieremo ad avvisare gli sviluppatori dell'utilizzo del file errato. A partire dal plug-in Android per Gradle versione 9.0, non supportiamo più il file proguard-android.txtobsoleto. Assicurati quindi di eseguire l'upgrade alla versione ottimizzata.
Come scrivere le regole di conservazione
Una regola di conservazione è composta da tre parti principali:
- Un'opzione come
-keepo-keepclassmembers - Modificatori facoltativi come
allowshrinking - Una specifica di classe che definisce il codice da abbinare
Per la sintassi completa e gli esempi, consulta le indicazioni per aggiungere regole di conservazione.
Anti-pattern della regola Keep
È importante conoscere le best practice, ma anche gli anti-pattern. Questi anti-pattern spesso derivano da incomprensioni o scorciatoie per la risoluzione dei problemi e possono essere catastrofici per le prestazioni di una build di produzione.
Opzioni globali
Questi flag sono attivazioni/disattivazioni globali che non devono mai essere utilizzate in una build di rilascio. Servono solo per il debug temporaneo per isolare un problema.
L'utilizzo di -dontotptimize disattiva efficacemente le ottimizzazioni delle prestazioni di R8, con conseguente rallentamento dell'app.
Quando utilizzi -dontobfuscate disabiliti tutta la ridenominazione e l'utilizzo di -dontshrink disattiva la rimozione del codice inutilizzato. Entrambe queste regole globali aumentano le dimensioni dell'app.
Evita di utilizzare questi flag globali in un ambiente di produzione, se possibile, per un'esperienza utente dell'app più performante.
Regole di conservazione eccessivamente generiche
Il modo più semplice per annullare i vantaggi di R8 è scrivere regole di conservazione troppo ampie. Regole come quella riportata di seguito indicano all'ottimizzatore R8 di non ridurre, non offuscare e non ottimizzare nessuna classe in questo pacchetto o nessuno dei relativi sottopacchetti. In questo modo, i vantaggi di R8 vengono completamente rimossi per l'intero pacchetto. Prova invece a scrivere regole di conservazione specifiche e ristrette.
-keep class com.example.package.** { *;} // WIDE KEEP RULES CAUSE PROBLEMS
L'operatore di inversione (!)
L'operatore di inversione (!) sembra un modo efficace per escludere un pacchetto da una regola. Ma non è così semplice. Considera questo esempio:
-keep class !com.example.my_package.** { *; } // USE WITH CAUTION
Potresti pensare che questa regola significhi "non conservare le classi incom.example.package", ma in realtà significa "conserva ogni classe, metodo e proprietà nell'intera applicazione che non si trova in com.example.package". Se questa informazione ti ha sorpreso, ti consigliamo di verificare la presenza di negazioni nella configurazione di R8.
Regole ridondanti per i componenti Android
Un altro errore comune è l'aggiunta manuale di regole di conservazione per Activities, Services o BroadcastReceivers della tua app. Questa operazione è inutile. Il file proguard-android-optimize.txt predefinito include già le regole pertinenti per il funzionamento immediato di questi componenti Android standard.
Inoltre, molte librerie hanno le proprie regole di conservazione. Pertanto, non dovresti dover scrivere regole personalizzate per questi casi. In caso di problemi con le regole Keep di una libreria che stai utilizzando, è meglio contattare l'autore della libreria per capire qual è il problema.
Best practice per le regole
Ora che sai cosa non fare, parliamo delle best practice.
Scrivere regole di conservazione specifiche
Le regole di conservazione efficaci devono essere il più ristrette e specifiche possibile. Devono conservare solo ciò che è necessario, consentendo a R8 di ottimizzare tutto il resto.
| Regola | Qualità |
|---|---|
| Basso:mantiene un intero pacchetto e i relativi pacchetti secondari |
| Basso: mantiene un'intera classe, che probabilmente è ancora troppo ampia |
| Alto:vengono mantenuti solo i metodi e le proprietà pertinenti di una classe specifica |
Utilizzare antenati comuni
Invece di scrivere regole di conservazione separate per più modelli di dati diversi, scrivi una regola che abbia come target una classe base o un'interfaccia comune. La regola riportata di seguito indica a R8 di conservare tutti i membri delle classi che implementano questa interfaccia ed è altamente scalabile.
# Keep all fields of any class that implements SerializableModel -keepclassmembers class * implements com.example.models.SerializableModel { <fields>; }
Utilizzare le annotazioni per scegliere come target più classi
Crea un'annotazione personalizzata (ad es. @Serialize) e utilizzala per "taggare" le classi di cui è necessario conservare i campi. Si tratta di un altro pattern pulito, dichiarativo e altamente scalabile. Puoi anche creare regole di conservazione per le annotazioni già esistenti dai framework che utilizzi.
# Keep all fields of any class annotated with @Serialize -keepclassmembers class * { @com.example.annotations.Serialize <fields>; }
Scegliere l'opzione di Keep giusta
L'opzione di conservazione è la parte più importante della regola. La scelta di un'opzione errata può disattivare inutilmente l'ottimizzazione.
| Opzione Keep | Cosa fa |
-keep | Impedisce la rimozione o la ridenominazione della classe e dei membri menzionati nella dichiarazione . |
-keepclassmembers | Impedisce la rimozione o la ridenominazione dei membri specificati, ma consente la rimozione del corso stesso solo se non è già stato rimosso. |
-keepclasseswithmembers | Una combinazione: mantiene il corso e i suoi membri solo se sono presenti tutti i membri specificati. |
Per saperne di più sull'opzione di conservazione, consulta la nostra documentazione sulle opzioni di conservazione.
Consenti l'ottimizzazione con i modificatori
Modificatori come allowshrinking e allowobfuscation rilassano una regola -keep generale, restituendo il potere di ottimizzazione a R8. Ad esempio, se una libreria legacy ti costringe a utilizzare -keep su un'intera classe, potresti recuperare parte dell'ottimizzazione consentendo la riduzione e l'offuscamento:
# Keep this class, but allow R8 to remove it if it's unused and allow R8 to rename it. -keep,allowshrinking,allowobfuscation class com.example.LegacyClass
Aggiungere opzioni globali per un'ulteriore ottimizzazione
Oltre alle regole di conservazione, puoi aggiungere flag globali al file di configurazione R8 per favorire un'ulteriore ottimizzazione.
-repackageclasses è un'opzione potente che indica a R8 di spostare tutte le classi offuscate in un unico pacchetto. In questo modo si risparmia spazio significativo nel file DEX rimuovendo le stringhe ridondanti del nome del pacchetto.
-allowaccessmodification consente a R8 di ampliare l'accesso (ad es. da private a public) per consentire l'incorporamento più aggressivo. Questa opzione è ora abilitata per impostazione predefinita quando si utilizza proguard-android-optimize.txt.
Avviso: gli autori delle librerie non devono mai aggiungere questi flag di ottimizzazione globali alle regole dei consumatori, in quanto verrebbero applicati forzatamente all'intera app.
Per rendere il tutto ancora più chiaro, nella versione 9.0 del plug-in Android per Gradle inizieremo a ignorare completamente i flag di ottimizzazione globali delle librerie.
Best practice per le librerie
Ogni app per Android si basa sulle librerie in un modo o nell'altro. Vediamo quindi le best practice per le librerie.
Per gli sviluppatori di librerie
Se la tua libreria utilizza la reflection o JNI, è tua responsabilità fornire le regole di conservazione necessarie ai suoi consumatori. Queste regole vengono inserite in un file consumer-rules.pro, che viene poi raggruppato automaticamente all'interno del file AAR della libreria.
android {
defaultConfig {
consumerProguardFiles("consumer-rules.pro")
}
...
}Per gli utenti della raccolta
Filtrare le regole di conservazione problematiche
Se devi utilizzare una libreria che include regole Keep problematiche, puoi filtrarle nel file build.gradle.kts a partire da AGP 9.0. In questo modo, R8 ignora le regole provenienti da una dipendenza specifica.
release {
optimization.keepRules {
// Ignore all consumer rules from this specific library
it.ignoreFrom("com.somelibrary:somelibrary")
}
}La migliore regola di conservazione è nessuna regola di conservazione
La strategia di configurazione R8 definitiva consiste nel rimuovere completamente la necessità di scrivere regole Keep. Per molte app, questo risultato può essere ottenuto scegliendo librerie moderne che favoriscono la generazione di codice rispetto alla reflection.Con la generazione di codice, l'ottimizzatore può determinare più facilmente quale codice viene effettivamente utilizzato in fase di runtime e quale può essere rimosso. Inoltre, se non utilizzi alcuna reflection dinamica, non ci sono punti di ingresso "nascosti" e, di conseguenza, non sono necessarie regole di conservazione. Quando scegli una nuova libreria, preferisci sempre una soluzione che utilizzi la generazione di codice rispetto alla reflection.
Per saperne di più su come scegliere le librerie, consulta Scegliere la libreria giusta.
Debug e risoluzione dei problemi della configurazione di R8
Quando R8 rimuove codice che avrebbe dovuto conservare o il tuo APK è più grande del previsto, utilizza questi strumenti per diagnosticare il problema.
Trovare regole di conservazione duplicate e globali
Poiché R8 unisce le regole di decine di origini, può essere difficile sapere qual è il set di regole "finale". L'aggiunta di questo flag al file proguard-rules.pro genera un report completo:
# Outputs the final, merged set of rules to the specified file -printconfiguration build/outputs/logs/configuration.txt
Puoi cercare in questo file regole ridondanti o risalire a una regola problematica (ad esempio -dontoptimize) fino alla libreria specifica che la include.
Chiedi a R8: perché lo conservi?
Se un corso che ti aspettavi di rimuovere è ancora presente nell'app, R8 può dirti il motivo. Aggiungi questa regola:
# Asks R8 to explain why it's keeping a specific class class com.example.MyUnusedClass -whyareyoukeeping
Durante la compilazione, R8 stampa la catena esatta di riferimenti che ha causato il mantenimento della classe, consentendoti di tracciare il riferimento e modificare le regole.
Per una guida completa, consulta la sezione Risoluzione dei problemi di R8.
Passaggi successivi
R8 è un potente strumento per migliorare le prestazioni delle app per Android. La sua efficacia dipende da una corretta comprensione del suo funzionamento come motore di analisi statica.
Scrivendo regole specifiche a livello di membro, sfruttando gli antenati e le annotazioni e scegliendo con attenzione le opzioni di conservazione giuste, puoi conservare esattamente ciò che è necessario. La pratica più avanzata consiste nell'eliminare completamente la necessità di regole scegliendo librerie moderne basate sulla generazione di codice rispetto alle loro versioni precedenti basate sulla reflection.
Mentre segui la settimana di Performance Spotlight, assicurati di guardare il video di oggi su YouTube e continua con la nostra sfida R8. Utilizza #optimizationEnabled per qualsiasi domanda sull'attivazione o la risoluzione dei problemi di R8. Siamo a tua disposizione.
È ora di scoprire i vantaggi per te.
Ti invitiamo ad attivare la modalità completa R8 per la tua app oggi stesso.
- Per iniziare, segui le nostre guide per gli sviluppatori: Attivare l'ottimizzazione delle app.
- Controlla se utilizzi ancora
proguard-android.txte sostituiscilo conproguard-android-optimize.txt. - Poi, misura l'impatto. Non limitarti a sentire la differenza, verificala. Misura i miglioramenti delle prestazioni adattando il codice della nostra app di esempio Macrobenchmark su GitHub per misurare i tempi di avvio prima e dopo.
Siamo certi che noterai un miglioramento significativo delle prestazioni della tua app.
Già che ci sei, usa l'hashtag #AskAndroid per porre le tue domande. Durante la settimana, i nostri esperti monitorano e rispondono alle tue domande.
Non perderti l'appuntamento di domani, in cui parleremo dell'ottimizzazione guidata dal profilo con i profili di baseline e di avvio, condivideremo i miglioramenti del rendimento del rendering di Compose nelle versioni precedenti e prenderemo in esame le considerazioni sul rendimento per il lavoro in background.
Continua a leggere
-
Novità sul prodotto
Testare le interazioni multi-dispositivo è ora più facile che mai con l'emulatore Android.
Steven Jenkins • Lettura di 2 minuti
-
Novità sul prodotto
Android Studio supporta Gemma 4: il nostro modello locale più potente per la programmazione agentica
Il flusso di lavoro e le esigenze di ogni sviluppatore in materia di AI sono unici ed è importante poter scegliere in che modo l'AI può aiutarti nello sviluppo. A gennaio abbiamo introdotto la possibilità di scegliere qualsiasi modello di AI locale o remoto per potenziare la funzionalità di AI in Android Studio
Matthew Warner • Lettura di 2 minuti
-
Novità sul prodotto
Android Studio Panda 3 è ora stabile e pronto per l'uso in produzione. Questa release ti offre ancora più controllo e personalizzazione sui tuoi flussi di lavoro basati sull'AI, rendendo più facile che mai creare app per Android di alta qualità.
Matt Dyor • Lettura di 3 minuti
Segui gli aggiornamenti
Ricevi ogni settimana gli ultimi approfondimenti sullo sviluppo per Android direttamente nella tua casella di posta.