Il sistema di build della piattaforma Android (Soong) esegue il compilatore R8 su
target che compilano il bytecode DEX, tra cui app per Android (android_app), test
(android_test) e librerie Java installabili con compile_dex: true (ad esempio
services.jar), per ridurre, ottimizzare e rimuovere codice e risorse inutilizzati. Nelle librerie statiche (android_library e java_library statico), Soong non esegue R8 direttamente, ma utilizza il blocco di proprietà optimize per collegare e propagare le regole di conservazione dei consumer (export_proguard_flags_files: true) ai target DEX downstream che le collegano staticamente.
Per i platform engineer che creano immagini di sistema o pacchetti di fornitori, l'eliminazione di regole di conservazione eccessivamente ampie offre vantaggi diretti per l'integrità del sistema:
- Impatto ridotto della partizione di sistema:R8 rimuove classi, metodi e risorse non utilizzati prima di pacchettizzare APK e JAR in
/system,/system_ext,/producte/vendor. - Artefatti compilati più piccoli:un numero inferiore di metodi DEX significa file
.odexe.vdexpiù piccoli generati dadex2oatdurante la compilazione in fase di build o sul dispositivo. - Minore pressione sulla memoria di runtime:poiché Android mappa
.odex,.vdexe le tabelle delle risorse APK nella memoria di processo utilizzando chiamatemmapcon paginazione on demand, i binari più piccoli riducono gli errori di pagina principali durante l'avvio dell'app e riducono la memoria del codice e delle risorse residenti nei processi. Per saperne di più sull'impatto del codice dell'app compilato sulla memoria del dispositivo, consulta Il codice dell'app è memoria. - Ottimizzazione più efficace dell'intero programma:i vincoli di conservazione ristretti consentono a R8 di incorporare i metodi, devirtualizzare le chiamate virtuali e di interfaccia, eliminare i campi inutilizzati e propagare le costanti tra le classi.
Configura l'ottimizzazione R8 in Soong
Nei file Android.bp, configura l'ottimizzazione R8 utilizzando il blocco della proprietà optimize sui target android_app e java_library installabili (o sui moduli android_library e java_library statici per esportare le regole di conservazione dei consumer):
android_app {
name: "MySystemApp",
srcs: ["src/**/*.java"],
optimize: {
obfuscate: true,
shrink_resources: true,
},
}
La tabella seguente riassume le proprietà optimize più comuni in Soong (definite in build/soong/java/dex.go):
| Proprietà | Descrizione |
|---|---|
enabled |
Controlla se R8 viene eseguito sulla destinazione. Il valore predefinito è true per tutti i target android_app.
|
shrink |
Controlla il tree shaking per rimuovere classi, campi e metodi non raggiungibili. Il valore predefinito è true per i target android_app (false per i moduli java_library e di test autonomi, che passano -dontshrink se non impostato esplicitamente).
|
optimize |
Controlla le ottimizzazioni del bytecode, come l'inlining dei metodi, l'unione delle classi, la propagazione delle costanti e la rimozione dei rami inattivi. Il valore predefinito è true per i target android_app (controllati dal flag di build di release RELEASE_R8_OPTIMIZE_BY_DEFAULT).
|
obfuscate |
Controlla la minimizzazione e la ridenominazione degli identificatori. Il valore predefinito è
false per la compatibilità storica, ma impostalo su
true ove possibile per ridurre le dimensioni del DEX e sbloccare
ottimizzazioni più approfondite (consulta
Attiva l'offuscamento ove possibile).
|
shrink_resources |
Rimuove le risorse inutilizzate (res/ voci) dall'APK pacchettizzato dopo la riduzione del codice. Il valore predefinito è false.
|
optimized_shrink_resources |
Esegue la pipeline integrata di riduzione del codice e delle risorse R8 in modo che R8 tracci congiuntamente i riferimenti al codice e alle risorse in un unico passaggio. Se shrink_resources: true è impostato, il valore predefinito è RELEASE_USE_OPTIMIZED_RESOURCE_SHRINKING_BY_DEFAULT (true nelle build della piattaforma standard).
|
proguard_flags_files |
Elenca i file .flags o .pro specifici del modulo contenenti regole di conservazione personalizzate. Evita di aggiungere file personalizzati quando sono sufficienti le annotazioni o i valori predefiniti standard.
|
export_proguard_flags_files |
Propaga il proguard_flags_files di questa libreria ai moduli downstream che dipendono da questa in modo statico.
|
trace_references_from |
Elenca i target dipendenti diretti dalla compilazione DEX (connessi tramite instrumentation_for o libs) i cui riferimenti di bytecode in questo target R8 vengono tracciati e conservati automaticamente (anche propagati tra le varianti override_android_app e override_android_test e supportati per le librerie Java companion non dipendenti se accoppiati con una suddivisione .impl).
|
allow_traced_references |
Consente ai target dipendenti dalla compilazione DEX diretta (connessi tramite
instrumentation_for o libs, ad esempio
test tra repository o di fornitori che non possono essere elencati in
trace_references_from) di attivare il tracciamento dei riferimenti al bytecode
in questo target impostando
trace_references: true.
|
trace_references |
Se impostato su true in un dipendente di compilazione DEX diretto di una destinazione con allow_traced_references: true, esegue R8 tracereferences su questo modulo e passa le regole di conservazione generate al modulo di destinazione.
|
protect_api_surface |
Passa --protect-api-surface a R8. Se impostato su un java_sdk_library, genera e collega automaticamente anche le regole di conservazione dell'API Metalava in tutti gli ambiti API abilitati (pubblico, di sistema, module-lib, system-server e di test) alla libreria di implementazione.
|
Anche le librerie statiche (android_library, java_library e importazioni AAR) possono impostare required_downstream_optimization: { enabled: true, optimize: true } in Android.bp per richiedere che qualsiasi target di compilazione DEX downstream che le colleghi staticamente (direttamente o in modo transitivo) attivi la riduzione R8 e l'ottimizzazione del bytecode. Soong attiva questa funzionalità automaticamente su
androidx.compose.runtime_runtime in modo che i target che collegano Jetpack Compose siano
ottimizzati con R8.
Attiva l'offuscamento, ove possibile
In Soong, obfuscate viene impostato su false per impostazione predefinita per la compatibilità con le versioni precedenti, ma devi impostare esplicitamente obfuscate: true su android_app e sui target DEX autonomi, ove possibile:
- DEX e
.vdexpiù piccoli: la ridenominazione di pacchetti, classi, campi e metodi in identificatori brevi (a,b) riduce il pool di stringhe DEX (string_idsestring_data_item) e i descrittori dei tipi. Poiché Android mappa i file.vdexe.odexnella memoria di processo, le tabelle dei simboli più piccole riducono direttamente sia le dimensioni della partizione di sistema sia il footprint della memoria di runtime. - Ottimizzazioni più approfondite dell'intero programma:consentire a R8 di rinominare gli identificatori sblocca l'unione delle classi, l'appiattimento dei pacchetti e la deduplicazione delle classi sintetiche tra i pacchetti che altrimenti R8 deve saltare per evitare conflitti di nomi.
- Simbolizzazione completa dell'analisi dello stack:l'attivazione dell'offuscamento in Soong non riduce la possibilità di eseguire il debug. Per ogni target compilato con R8, Soong genera un file di mappatura
proguard_dictionarynella directory intermedia del modulo, raggruppa tutti i dizionari del modulo nell'artefattoproguard-dict.zipdella build, incorpora un hash di mappatura (--map-id-template) nell'intestazione DEX e riscrive gli attributiSourceFiledella classe (--source-file-template) in modo cheretracepossa simbolizzare automaticamente le analisi dello stack.
Mantieni obfuscate: false (o conserva esplicitamente i nomi delle API pubbliche) solo per:
- Librerie condivise nel bootclasspath o nel classpath
system_server: i moduli (java_libraryojava_sdk_library) che espongono una superficie API esterna collegata dinamicamente da altri moduli in fase di runtime (come i targetframework.jar,services.jaro<uses-library>) devono mantenereobfuscate: falseo preservare esplicitamente la loro superficie API utilizzando le regoleprotect_api_surface: true,@KeepForApio-keepin modo che i chiamanti compilati in base ai loro stub possano risolvere i nomi di classi e membri in fase di runtime. Nei modulijava_sdk_library, l'impostazioneprotect_api_surface: truegenera e collega automaticamente le regole di conservazione di Metalava ProGuard in tutti gli ambiti API abilitati (pubblico, di sistema, module-lib, system-server e di test) alla libreria di implementazione. Quando l'ottimizzazione R8 generalizzata è abilitata su target bootclasspath non aggiornabili (framework-minus-apex,exteframework-location), questi includono ancheframework-hiddenapi-proguard-rules(generato dahiddenapi_to_proguard) per conservare i membri elencati nei fileboot/hiddenapi/*.txtattivi (omettendohiddenapi-max-target-o.txt).
Prima di abilitare obfuscate: true su un'applicazione, verifica i seguenti prerequisiti di migrazione:
- Dipendenze APK di test esterno:se un APK
android_testesterno impostainstrumentation_for: "MyApp"e richiama direttamente classi o metodi interni diMyApp, la ridenominazione di questi simboli causaNoSuchMethodErroroNoClassDefFoundErrorin fase di esecuzione del test. Configuratrace_references_from(oallow_traced_referencescontrace_references: true), struttura il test come APK di autostrumentazione che si collega staticamente aMyApp.implo annota gli hook di test con@VisibleForTesting(vedi Passaggio 2: esegui la migrazione delle regole di conservazione basate sui test) in modo che i simboli interni necessari per i test vengano conservati durante l'offuscamento del resto dell'app. - Reflection basata su stringhe e JNI: se un modulo cerca classi, metodi o campi in base ai nomi delle stringhe letterali (
Class.forName,getDeclaredMethodo JNIFindClasseGetMethodID) senza regole o annotazioni di conservazione, l'offuscamento rinomina i target e interrompe le ricerche di runtime. (Tieni presente che la reflection non annotata non riesce anche inshrink: trueeoptimize: true). Annota questi entry point con la guida alle annotazioni Keep (@UsesReflection,@UsedByReflectiono@UsedByNative) in modo che R8 conservi i loro nomi e offuschi il resto del modulo.
Segui il principio di zero flag personalizzati
Il modulo della piattaforma Android ideale non ha file proguard.flags o keep.xml personalizzati. Nella build della piattaforma Android, la stragrande maggioranza delle regole di conservazione personalizzate è ridondante:
- Baseline della piattaforma e AAPT2: la maggior parte degli entry point viene già conservata automaticamente dalle baseline della piattaforma globale (come
@Keep, metodi JNInativee@VisibleForTesting) o generata da AAPT2 daAndroidManifest.xmle dalle risorse di layout (consulta Informazioni sulle regole di conservazione predefinite della piattaforma). - Alternative mirate: se gli entry point devono essere conservati, preferisci le annotazioni del sito di dichiarazione (
keepanno,@VisibleForTesting), le regole della libreria esportate (export_proguard_flags_files: true) o il collegamento dei test statici rispetto ai file.flagsseparati (consulta Controlla ed esegui la migrazione delle regole keep esistenti).
Prima di aggiungere o conservare un file proguard.flags o keep.xml personalizzato, verifica se la regola è già gestita dalle baseline predefinite o se può essere migrata alle annotazioni del codice.
Informazioni sulle regole di conservazione predefinite della piattaforma
Soong passa automaticamente le seguenti regole di base globali a ogni chiamata R8 sui target di compilazione DEX (android_app, android_test e moduli java_library installabili), configurati in build/soong/java/dex.go:
build/make/core/proguard.flags: conserva le classi e i membri annotati con@com.android.internal.annotations.VisibleForTestingin tutti i pacchetti e con@VisibleForTesting(androidx.annotation.VisibleForTestingocom.google.common.annotations.VisibleForTesting) nei pacchettiandroid.**,com.android.**ecom.google.android.**. Conserva anche@TestApi,@Keep(androidx.annotation,android.support.annotation,com.android.internal.annotations),@KeepForWeakReference,@WeaklyReferencedCallbacke@dalvik.annotation.optimization.**.build/make/core/proguard_basic_keeps.flags: conserva gli attributiSourceFileper le analisi dello stack, le annotazioni di visibilità di runtime (RuntimeVisible*Annotations), gli attributiExceptionseAnnotationDefault, i metodinative, i membriSerializable, i metodi@JavascriptInterface, i costruttoriThrowable(String), i campiParcelable$CREATORe i campi protobufMessageLite.build/make/core/proguard/kotlin.flags: silenzia gli avvisi innocui per meta-annotazioni Kotlin specifiche (kotlin.Metadataekotlin.annotation.{AnnotationRetention,AnnotationTarget,Retention,Target}) e rimuove le annotazioni KotlinDebugMetadatanelle build di release.build/make/core/proguard/checknotnull.flags: sostituisce le chiamate helper di controllo null comuni (com.google.common.base.Preconditions.checkNotNulledagger.internal.Preconditions.checkNotNull*) con controlli null di bytecode concisi. Questo file omette intenzionalmenteObjects.requireNonNullper preservare i messaggi di eccezione espliciti oltre i limiti dell'API del framework.build/make/core/proguard/enumvalues.flags: conserva i metodivaluesevalueOfsui tipienum, a meno che non siano disattivati dalla configurazione di compilazione.- Regole generate automaticamente da AAPT2:AAPT2 ispeziona i file
AndroidManifest.xmluniti, file XML di layout e file XML di preferenze per generare regole di conservazione esatte per ogniActivity,Service,BroadcastReceiver,ContentProvider,BackupAgent,Application, sottoclasseViewpersonalizzata, sottoclassePreferencee metodoandroid:onClickXML registrati.
Controlla ed esegui la migrazione delle regole di conservazione esistenti
Quando esegui il controllo dei file proguard.flags o keep.xml esistenti in un repository della piattaforma, valuta ogni regola in ordine rispetto alla seguente gerarchia in quattro passaggi:
Passaggio 1: elimina le regole ridondanti o obsolete
Elimina le regole già coperte dalle baseline globali, dalle regole di layout e manifest AAPT2 o dalle impostazioni predefinite di R8, nonché le regole che fanno riferimento a classi o pacchetti che non esistono più.
Se tutte le regole di un file .flags sono ridondanti, elimina il file e rimuovi proguard_flags_files da Android.bp. Se il blocco optimize rimanente si limita a riaffermare le impostazioni predefinite, rimuovi il blocco ridondante e formatta il file di build con bpfmt -w Android.bp. Quando esegui la pulizia di un pacchetto, controlla se i moduli complementari nelle sottodirectory (come le varianti di destinazione Kotlin) fanno riferimento allo stesso file di flag e aggiornali insieme.
Passaggio 2: esegui la migrazione delle regole di conservazione basate su test
Sposta le regole di conservazione basate su test dai file .flags di produzione personalizzati utilizzando uno dei seguenti pattern:
Traccia i riferimenti dai target di test o companion (
trace_references_fromoallow_traced_references): quando sia il target sia un modulo dipendente diretto compilano il proprio DEX (android_app,android_testo unjava_librarydexed) e sono collegati tramiteinstrumentation_forolibs, Soong può eseguire R8tracereferencessul modulo dipendente e inserire le regole di conservazione generate nella compilazione R8 del target senza creare un ciclo di dipendenza o richiedere una suddivisione manuale della libreria.impl:Test nello stesso repository (
trace_references_from): elenca i dipendenti direttiinstrumentation_forolibssul modulo di destinazione (questo si propaga automaticamente anche tra le variantioverride_android_appeoverride_android_teste continua a supportare le librerie Java companion non dipendenti se accoppiate a una suddivisione.impl):android_app { name: "MyApp", srcs: ["src/**/*.java"], optimize: { obfuscate: true, shrink_resources: true, trace_references_from: ["MyAppTests"], }, } android_test { name: "MyAppTests", instrumentation_for: "MyApp", srcs: ["tests/**/*.java"], }Test tra repository o fornitori (
allow_traced_referencesetrace_references): quando il modulo di destinazione non può elencare il dipendente direttamente intrace_references_from(ad esempio i test in un altro repository o albero del fornitore), impostaallow_traced_references: truesul modulo di destinazione e attiva l'opzione da ogni dipendente contrace_references: true:android_app { name: "MyApp", srcs: ["src/**/*.java"], optimize: { allow_traced_references: true, }, } android_test { name: "MyAppVendorTests", instrumentation_for: "MyApp", srcs: ["tests/**/*.java"], optimize: { trace_references: true, }, }Reflection basata su stringhe nei test:
tracereferencesispeziona i riferimenti al pool di costanti del bytecode e non può rilevare la reflection basata su stringhe nei test (Class.forName,getDeclaredField,getDeclaredMethodo helper di field injection riflessiva). Annota i campi e i metodi di produzione a cui si accede in modo riflessivo con@VisibleForTesting(o@UsedByReflection).
Collega la libreria di implementazione ai test (
static_libs): se vuoi che nell'APK di produzione non vengano mantenuti entry point solo per i test e non vuoi che le modifiche ai test attivino nuovamente R8 sul target di produzione, inserisci i file di codice sorgente dell'app in unandroid_library(MyApp.impl) e collega staticamenteMyApp.implsia aMyAppsia aMyAppTests:android_library { name: "MyApp.impl", srcs: ["src/**/*.java"], manifest: "AndroidManifest.xml", } android_app { name: "MyApp", static_libs: ["MyApp.impl"], optimize: { obfuscate: true, shrink_resources: true, }, } android_test { name: "MyAppTests", srcs: ["tests/**/*.java"], static_libs: ["MyApp.impl"], }Questo pattern a 3 moduli consente a
MyAppTestsdi essere eseguito come test di instrumentazione automatica con accesso completo alle classi interne, consentendo al contempo aMyAppdi ridurre e offuscare esclusivamente i punti di accesso di produzione.Annota gli hook di test con
@VisibleForTesting: se un APK di test esterno chiama solo alcuni metodi o costruttori interni su un targetandroid_app, puoi annotare queste dichiarazioni con@VisibleForTesting. La baseline globalebuild/make/core/proguard.flagsconserva automaticamente gli elementi@VisibleForTestingnei pacchettiandroid.**,com.android.**ecom.google.android.**senza file.flagspersonalizzati. (Per i moduli del fornitore esterni a questi spazi dei nomi, utilizzatrace_references_from,@UsedByReflectiono le regole della libreria esportata.) Quando annoti manualmente con@VisibleForTestinganziché utilizzaretrace_references_fromotrace_references, annota anche le classi passate a Mockitomock(...)ospy(...)(non solo i metodi con stub), perchémockito-target(dexmaker-mockito) on-device genera sottoclassi di runtime e R8 può contrassegnare le classi senza sottoclassifinal(MockitoException: Can't mock/spy class ... - final class).Evita o pacchettizza nuovamente le librerie statiche sovrapposte (
jarjar_rules): quando sia unandroid_appsia il relativo APK di testinstrumentation_forcollegano staticamente la stessa libreria (direttamente, ad esempioguava, o in modo transitivo),tracereferencesvede le classi definite nel test e non emette regole di conservazione per l'app. Inoltre, in fase di runtime, il classloader principale dell'app oscura la copia del test con le classi ridotte di R8, causandoIllegalAccessErroroNoSuchMethodError. Se il test non richiede una copia in bundle, rimuovi il duplicato dal filestatic_libsdel test (o collegalo tramitelibs) per evitare la collisione. In caso contrario, pacchettizza nuovamente i pacchetti di librerie statiche sovrapposti dell'app con un prefisso specifico del modulo utilizzandojarjar_rules.
Passaggio 3: esporta le regole dalla libreria proprietaria
Se un java_library o android_library condiviso richiede regole di conservazione per la propria riflessione interna o i callback JNI, definisci le regole sul target della libreria e imposta export_proguard_flags_files: true:
java_library {
name: "my-shared-library",
srcs: ["src/**/*.java"],
optimize: {
proguard_flags_files: ["proguard.flags"],
export_proguard_flags_files: true,
},
}
Tutti i target android_app e della libreria downstream che si collegano staticamente a my-shared-library ereditano automaticamente queste regole, quindi le app downstream non devono duplicarle. Quando più moduli in directory diverse devono condividere un set di regole non associato a una singola libreria di codice, pubblica il file .flags tramite un filegroup esplicito in Android.bp in modo che i moduli possano fare riferimento a :my-shared-flags in modo pulito oltre i confini del pacchetto. Per indicazioni generali sulla creazione di regole di conservazione per i consumer di librerie senza limitare l'ottimizzazione delle app downstream, consulta Ottimizzazione per gli autori di librerie.
Passaggio 4: annota le dichiarazioni nel codice sorgente
Quando si accede a una classe, un metodo, un costruttore o un campo tramite reflection o JNI e non è coperto da AAPT2 o da baseline globali, sostituisci le regole -keep separate nei file .flags con annotazioni di origine della Guida alle annotazioni Keep (consulta il riferimento Javadoc keepanno):
@UsesReflection: preferisci questa annotazione quando controlli il codice che esegue la reflection. Inseriscila nel sito di chiamata della reflection per dichiarare quali classi, metodi o campi di destinazione sono accessibili dinamicamente. Poiché@UsesReflectioncodifica automaticamente una precondizione che il sito di chiamata annotato stesso sia raggiungibile, R8 elimina sia il chiamante sia la destinazione riflessiva se il sito di chiamata non viene utilizzato.@UsedByReflection: da inserire in classi, metodi, campi o costruttori di cui viene creata un'istanza o che vengono richiamati in modo riflessivo da codice o librerie esterni, ad esempio classi caricate conClass.forNameda chiaviBundleoSettingsoppure classi di plug-in caricate tra caricatori di classi dinamici. Specificakind(ad esempioKeepItemKind.CLASS_AND_METHODS),preconditions(@KeepCondition) e i vincoli dei parametri in modo che R8 mantenga solo il contratto di riflessione esatto e possa comunque ottimizzare o eliminare i membri non utilizzati della classe. Non aggiungere@UsedByReflectionai componenti registrati nel manifest comeJobServiceoBroadcastReceiver, perché AAPT2 li conserva già.@UsedByNative: posiziona su metodi o campi a cui si accede dal codice JNI C o C++ utilizzandoGetMethodID,GetStaticMethodIDoGetFieldID.@KeepForApi: da inserire nelle classi o nei membri dell'API della libreria che devono rimanere invariati quando una libreria stessa viene ridotta prima della distribuzione.@Keep(androidx.annotation.Keep): da utilizzare come opzione di riserva quandokeepannonon è applicabile. L'applicazione di@Keepa una classe conserva la classe e tutti i relativi elementi in modo incondizionato. L'applicazione di@Keepa un metodo o a un campo funge da punto di accesso incondizionato che conserva il membro e la classe che lo contiene anche se non viene mai creata un'istanza della classe, mentrekeepannoesprime la raggiungibilità condizionale.
Per utilizzare le annotazioni R8 keepanno in un modulo Soong:
Aggiungi
"keepanno-annotations"alibsinAndroid.bp:libs: [ "keepanno-annotations", ],Importa
com.android.tools.r8.keepanno.annotations.*e annota la dichiarazione o il sito di chiamata nel codice sorgente Java o Kotlin:import com.android.tools.r8.keepanno.annotations.KeepItemKind; import com.android.tools.r8.keepanno.annotations.UsedByReflection; public final class CustomPluginController { @UsedByReflection( description = "Instantiated via Class.forName from plugin config", kind = KeepItemKind.CLASS_AND_METHODS) public CustomPluginController(Context context) { // ... } }R8 traduce le annotazioni
keepannodirettamente nel suo modello di regola di conservazione interna e rimuove le annotazioni dall'output DEX finale, aggiungendo un overhead del bytecode di runtime pari a zero.
Evita gli errori comuni
Le sezioni seguenti descrivono le insidie più frequenti di proguard.flags e keep.xml nel codice della piattaforma e come risolverle.
Regole di conservazione dei componenti generali
# Don't do this:
-keep class * extends android.app.Activity
-keep class * extends android.app.Service
-keep class * extends android.content.BroadcastReceiver
- Perché influisce negativamente sulle prestazioni: questa regola è completamente ridondante con AAPT2.
Poiché utilizza un carattere jolly senza verificare se il componente è registrato nel file
AndroidManifest.xmlunito finale, forza R8 a conservare ogni sottoclasseActivity,ServiceoBroadcastReceivertrovata ovunque nel classpath, inclusi i componenti della libreria inutilizzati e le attività di debug disattivate. - Correzione consigliata: elimina la regola. AAPT2 ispeziona il manifest unito e genera regole
-keepesatte per i componenti registrati.
Regole di conservazione del gestore dei clic XML generico
# Don't do this:
-keepclassmembers class * {
public void *(android.view.View);
}
- Perché influisce negativamente sulle prestazioni: la conservazione di ogni metodo
public void *(View)in ogni classe del modulo impedisce a R8 di rimuovere o inserire in linea qualsiasi metodo che accetta un singolo parametroView. - Correzione consigliata: elimina la regola. AAPT2 analizza i file XML di layout e genera regole di conservazione mirate per i metodi a cui fanno riferimento gli attributi
android:onClick.
Regole di conservazione dei metodi getter e setter di View generici
# Don't do this:
-keep public class * extends android.view.View {
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
public void set*(...);
public *** get*();
}
- Perché influisce negativamente sulle prestazioni: questa regola forza R8 a conservare ogni getter e setter in ogni sottoclasse
Viewdell'app e in tutte le librerie collegate (incluse le librerie AndroidX e Material), bloccando la rimozione dei metodi non utilizzati e l'incorporamento nel codice dell'interfaccia utente. - Correzione consigliata: elimina la regola. AAPT2 conserva già i costruttori per le classi
Viewpersonalizzate create da file XML di layout. Se il codice anima una proprietà della visualizzazione utilizzando nomi di stringhe riflessive comeObjectAnimator.ofFloat(view, "translationZ", ...), sostituisci il nome della stringa con un riferimento alla proprietà digitato (View.TRANSLATION_Zo un'implementazione personalizzata diFloatPropertyoIntProperty) per evitare completamente la riflessione. Se non è possibile evitare l'accesso alle proprietà tramite reflection, annota il getter o il setter specifico con@UsedByReflection.
Ottimizzazione o offuscamento generale disattivati
# Don't do this in proguard.flags:
-dontoptimize
-dontshrink
-dontobfuscate
- Perché influisce negativamente sulle prestazioni: l'inserimento di
-dontoptimizeo-dontshrinkall'interno di un file.flagsesegue l'override silenzioso delle impostazioniAndroid.bpdel modulo e disattiva i passaggi di ottimizzazione nell'intero target. Inoltre, se una libreria esporta un file.flagscontenente-dontoptimize, disattiva l'ottimizzazione R8 per ogniandroid_appdownstream che si collega alla libreria. - Correzione consigliata: rimuovi
-dontoptimize,-dontshrinke-dontobfuscatedai file.flags. Controlla il comportamento di ottimizzazione in modo esplicito inAndroid.bputilizzando il bloccooptimize(shrink,optimize,obfuscate) sul target foglia.
Flag di diagnostica nelle regole impegnate
# Don't do this in proguard.flags:
-verbose
-printmapping proguard.map
-printusage usage.txt
-printconfiguration config.txt
- Perché danneggia la build: i flag di diagnostica inondano i log di build o tentano di scrivere file di output in percorsi locali durante le build Soong in sandbox.
- Correzione consigliata: elimina questi flag dai file
.flagsdi cui è stato eseguito il commit. Soong scrive automaticamente gli output di mappatura e utilizzo di R8, comeproguard_dictionaryeproguard_usage.zip, nella directory intermedia del modulo inout/soong/.intermediates/.
Regole di conservazione in produzione per il codice di test
- Perché influisce negativamente sulle prestazioni: l'aggiunta di regole
-keeppersonalizzate esclusivamente per l'accesso ai test forza il codice a rimanere non offuscato e conservato nelle build di produzione. Sebbene la configurazione ditrace_references_from(oallow_traced_referencescontrace_references: true) o l'annotazione dei metodi con@VisibleForTestingeviti di mantenere regole-keepmanuali e conservi solo ciò a cui fanno riferimento i test, questi simboli vengono comunque inclusi nel file binario di produzione. - Correzione consigliata:elimina le regole
-keepdi test personalizzate attivandotrace_references_from(oallow_traced_referencesetrace_references: true) oppure struttura l'app in una libreria.implcollegata a un APK di test di autoinstrumentazione utilizzandostatic_libsper mantenere i punti di accesso di solo test completamente fuori dall'APK di produzione, come descritto nel Passaggio 2: esegui la migrazione delle regole di conservazione basate su test.
Caratteri jolly delle risorse onnicomprensivi in keep.xml
<!-- Don't do this in res/raw/keep.xml: -->
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@raw/*,@drawable/*,@string/*" />
- Perché influisce negativamente sulle prestazioni: i caratteri jolly generali conservano ogni risorsa nel tipo corrispondente nel modulo e nelle sue dipendenze, annullando la riduzione delle risorse (
shrink_resources: true) e aumentando le dimensioni dell'APK e della tabellaresources.arscmappata. - Correzione consigliata:
- Preferisci i riferimenti alle risorse statiche rispetto a
keep.xml: evita di utilizzare il metodoResources.getIdentifierper cercare dinamicamente un insieme limitato di risorse, ad esempio stringhe di esperimenti numerate o elementi drawable a tema. Utilizza invece un'istruzioneswitchin fase di compilazione o esegui la mappatura su costanti staticheR.id,R.stringoR.drawable. I riferimenti statici consentono a R8 e AAPT2 di tracciare le risorse attive esatte, eliminare l'overhead di ricerca delle stringhe di runtime e rimuovere completamente la necessità dikeep.xml. - Elenca nomi di risorse specifici: se è richiesta la ricerca dinamica, ad esempio
da un lettore di licenze di terze parti, elenca gli identificatori esatti delle risorse in
tools:keep(ad esempio,tools:keep="@raw/third_party_licenses"). - Elimina i file
keep.xmlridondanti:se le risorse elencate sono già referenziate staticamente nel codice (R.raw.foo) o nel file XML (@raw/foo), lo shrinker le conserva automaticamente, quindi puoi eliminareres/raw/keep.xml.
- Preferisci i riferimenti alle risorse statiche rispetto a
Verifica e controlla le modifiche alle regole
Ogni volta che rimuovi o restringi le regole keep in proguard.flags o keep.xml,
verifica che la build del modulo sia corretta, superi i test delle unità e di instrumentazione
e mantenga tutti gli entry point richiesti.
Crea e testa i moduli
Crea il modulo in modo pulito per verificare che R8 e AAPT2 vengano completati senza avvisi di riferimento mancanti:
m <MODULE_NAME>Esegui i test delle unità e di instrumentazione per il modulo utilizzando
atest:atest <TEST_MODULE_NAME>Per le app di sistema, i servizi con privilegi o i moduli dipendenti dall'hardware, esegui test di strumentazione e della UI sui dispositivi fisici di destinazione o nel tuo lab di test dei dispositivi per esercitare i percorsi di riflessione di runtime, le associazioni IPC e l'inflation delle risorse.
Ispeziona le differenze di DEX e risorse
Confronta l'APK o il JAR compilato prima e dopo le modifiche alle regole per verificare che R8 rimuova il codice e le risorse non utilizzati senza eliminare gli entry point previsti:
Utilizza
apkanalyzerodexdumpper ispezionare le classi, i metodi e i campi conservati nei file APK o DEX di output:apkanalyzer dex packages $OUT/system/priv-app/<APP_NAME>/<APP_NAME>.apkQuando modifichi
keep.xmlo attivishrink_resources, utilizzaaapt2 dump resourcesper verificare che la build rimuova le risorse inutilizzate e conservi quelle richieste:aapt2 dump resources $OUT/system/priv-app/<APP_NAME>/<APP_NAME>.apkQuando esegui
prebuilts/r8/r8.jardirettamente sulclasses.jarpre-R8 di un modulo per verificare le modifiche ai flag offline senza una build completa della piattaforma, passa sia i JAR del classpath--libdel modulo (android.jar,framework.jarelibscondiviso) sia le regoleproguard.optionsgenerate da AAPT2 daAndroidManifest.xmle dagli XML di layout. Senza questi input, R8 non può risolvere i supertipi del framework (Activity,Service,Phone,Handler) e conserva in modo insufficiente i metodi della gerarchia.
Analizza il raggio di conservazione e la sussunzione delle regole con R8
Il compilatore R8 open source include un analizzatore Keep Radius (esposto anche in Android Studio e Gradle come R8 Configuration Analyzer) che misura l'impatto esatto di ogni regola di conservazione durante la compilazione. Soong integra questo analizzatore direttamente nella build della piattaforma Android (configurata in build/soong/java/dex.go).
Per analizzare le regole di conservazione per un modulo della piattaforma o per un'intera build:
Esegui la build con la variabile di ambiente
R8_DUMP_KEEP_RADIUS=true:R8_DUMP_KEEP_RADIUS=true m <MODULE_NAME>Quando
R8_DUMP_KEEP_RADIUS=trueè impostato, Soong indica a R8 di registrare le metriche delle regole di conservazione in un filer8keepradius.pbintermedio per ogni modulo compilato inout/soong/.intermediates/. Per ogni regola keep e annotazionekeepanno, R8 registra:- Raggio di conservazione immediato:le classi, i campi e i metodi esatti conservati dalla regola, insieme ai vincoli specifici che applica per la riduzione, l'ottimizzazione o l'offuscamento.
- Sussunzione delle regole:quali altre regole di conservazione o annotazioni conservano già gli stessi elementi. Se una regola personalizzata è completamente inclusa in una regola AAPT2 o in una regola di riferimento globale, puoi eliminarla in sicurezza.
- Regole globali e a livello di pacchetto: regole che utilizzano caratteri jolly ampi a livello di pacchetto o applicano istruzioni di configurazione globali.
Converti l'output
r8keepradius.pbin un report HTML interattivo utilizzandoKeepRadiusHtmlReportGenerator(incluso inprebuilts/r8/r8.jar):# Generate an HTML report for a single module INTERMEDIATES=out/soong/.intermediates/packages/apps/<APP_NAME>/<APP_NAME> java -cp prebuilts/r8/r8.jar \ com.android.tools.r8.keepradius.KeepRadiusHtmlReportGenerator \ $INTERMEDIATES/android_common/r8keepradius.pb \ keep_radius_report.html # Scan all built modules under out/soong/.intermediates and generate # per-module HTML reports plus an aggregate keepradius.html summary java -cp prebuilts/r8/r8.jar \ com.android.tools.r8.keepradius.KeepRadiusHtmlReportGenerator \ out/soong/.intermediates \ out/keep_radius_reportsQuando viene fornita una directory,
KeepRadiusHtmlReportGeneratoresamina tutti i file*keepradius*.pb, genera un report HTML per ogni modulo e creaout/keep_radius_reports/keepradius.htmlche riassume i conteggi degli elementi attivi, i conteggi degli elementi conservati e le regole di conservazione con il raggio più alto nella build. Per saperne di più sull'interpretazione dei punteggi di riduzione, ottimizzazione e offuscamento nel report generato, consulta Utilizza R8 Configuration Analyzer.
Soong supporta anche variabili di ambiente aggiuntive per la diagnostica e il loop interno di R8 in build/soong/java/dex.go:
R8_DUMP_INPUT=true: scriver8inputs.zipnella directory intermedia del modulo contenente tutti i JAR di input, i JAR della libreria e le configurazioni ProGuard unite per la riproduzione R8 autonoma.R8_DUMP_PERFETTO_TRACE=true: scriver8trace.ptracenella directory intermedia del modulo per ispezionare i passaggi di compilazione R8 in Perfetto.SOONG_SKIP_TRACE_REFERENCES=true: ignora l'esecuzione delle regoletracereferencesconfigurate datrace_references_fromotrace_referencesper velocizzare le build del loop interno locale durante l'iterazione sul codice di test.
Convalida le regole del consumer della libreria
Quando un java_library o android_library esporta regole keep agli app per utenti finali downstream (export_proguard_flags_files: true), queste regole non devono includere flag globali che alterano o disattivano l'ottimizzazione per l'app per utenti finali.
R8 fornisce un analizzatore di regole di conservazione open source (ProcessKeepRules), esposto nella build della piattaforma Android come strumento host process-keep-rules (definito in prebuilts/r8/Android.bp):
# Build the R8 keep rules validator host binary
m process-keep-rules
# Validate one or more ProGuard configuration files
out/host/linux-x86/bin/process-keep-rules <PROGUARD_FLAGS_PATH>
Lo strumento process-keep-rules analizza ogni file di configurazione e non va a buon fine con la diagnostica di file e righe se rileva istruzioni non consentite nelle regole dei consumer della libreria. Le istruzioni non consentite includono l'ottimizzazione globale, la riduzione
o la disattivazione dell'offuscamento (come -dontoptimize o -dontshrink), il repackaging
del pacchetto e i flag di modifica dell'accesso, i flag di diagnostica o mappatura e
-keepattributes a livello di app.
Controlla gli alberi di origine con pgaudit.py
Per controllare le directory di origine non create insieme agli analizzatori in fase di creazione di R8, l'albero della piattaforma Android include lo script pgaudit.py in build/make/core/proguard/tools/. Questo strumento di analisi statica analizza i file Android.bp,
proguard.flags e keep.xml in un repository per segnalare le regole già
coperte da AAPT2 o dalle baseline della piattaforma globale, i caratteri jolly generici, gli override di -dontoptimize
e le regole di conservazione solo per i test:
# Audit a single package directory
./build/make/core/proguard/tools/pgaudit.py packages/apps/Provision/
# Scan a subsystem tree and write a structured JSON report
./build/make/core/proguard/tools/pgaudit.py packages/ --json=audit_report.json
I team di piattaforma e OEM possono combinare pgaudit.py per una rapida classificazione dell'albero di origine,
process-keep-rules per convalidare le regole delle librerie esportate e
R8_DUMP_KEEP_RADIUS=true con KeepRadiusHtmlReportGenerator per misurare il
raggio di conservazione esatto di classi, campi e metodi di ogni regola in una build.