Das Build-System der Android-Plattform (Soong) führt den R8-Compiler auf Zielen aus, die DEX-Bytecode kompilieren, einschließlich Android-Apps (android_app), Tests (android_test) und installierbaren Java-Bibliotheken mit compile_dex: true (z. B. services.jar), um ungenutzten Code und ungenutzte Ressourcen zu verkleinern, zu optimieren und zu entfernen. Bei statischen Bibliotheken (android_library und statische java_library) führt Soong R8 nicht direkt aus, sondern verwendet den Attributblock optimize, um Consumer-Keep-Regeln (export_proguard_flags_files: true) an nachgelagerte DEX-Ziele anzuhängen und weiterzugeben, die sie statisch verknüpfen.
Für Plattform-Entwickler, die System-Images oder Anbieterpakete erstellen, bietet das Entfernen zu weit gefasster Beibehaltungsregeln direkte Vorteile für die Systemintegrität:
- Geringerer Speicherbedarf der Systempartition:R8 entfernt ungenutzte Klassen, Methoden und Ressourcen, bevor APKs und JARs auf
/system,/system_ext,/productund/vendorgepackt werden. - Kleinere kompilierte Artefakte:Weniger DEX-Methoden bedeuten kleinere
.odex- und.vdex-Dateien, die vondex2oatwährend der Build-Zeit oder der Kompilierung auf dem Gerät generiert werden. - Geringere Arbeitsspeicherauslastung zur Laufzeit:Da Android
.odex,.vdexund APK-Ressourcentabellen mithilfe vonmmap-Aufrufen mit Demand Paging in den Prozessspeicher abbildet, reduzieren kleinere Binärdateien die Anzahl der Seitenfehler beim App-Start und verringern den residenten Code- und Ressourcenspeicher über Prozesse hinweg. Weitere Informationen dazu, wie sich kompilierter App-Code auf den Gerätespeicher auswirkt, finden Sie unter App-Code ist Speicher. - Effektivere Optimierung des gesamten Programms: Durch engere Keep-Einschränkungen kann R8 Methoden inline einfügen, Schnittstellen- und virtuelle Aufrufe devirtualisieren, nicht verwendete Felder entfernen und Konstanten über Klassen hinweg weitergeben.
R8-Optimierung in Soong konfigurieren
Konfigurieren Sie in Android.bp-Dateien die R8-Optimierung mit dem Attributblock optimize für android_app- und installierbare java_library-Ziele (oder für android_library- und statische java_library-Module, um Consumer-Keep-Regeln zu exportieren):
android_app {
name: "MySystemApp",
srcs: ["src/**/*.java"],
optimize: {
obfuscate: true,
shrink_resources: true,
},
}
In der folgenden Tabelle sind die gängigsten optimize-Eigenschaften in Soong (definiert in build/soong/java/dex.go) zusammengefasst:
| Attribut | Beschreibung |
|---|---|
enabled |
Steuert, ob R8 auf dem Ziel ausgeführt wird. Die Standardeinstellung ist true für alle android_app-Ziele.
|
shrink |
Steuert das Tree-Shaking, um nicht erreichbare Klassen, Felder und Methoden zu entfernen. Der Standardwert ist true für android_app-Ziele (false für eigenständige java_library- und Testmodule, die -dontshrink übergeben, sofern nicht explizit festgelegt).
|
optimize |
Steuert Bytecode-Optimierungen wie Method-Inlining, Zusammenführen von Klassen, konstante Weitergabe und Entfernen von ungenutzten Verzweigungen. Der Standardwert ist true für android_app-Ziele (gesteuert durch das Release-Build-Flag RELEASE_R8_OPTIMIZE_BY_DEFAULT).
|
obfuscate |
Steuert die Minimierung und Umbenennung von Kennungen. Der Standardwert ist false, um die Kompatibilität mit älteren Versionen zu gewährleisten. Setzen Sie ihn jedoch nach Möglichkeit auf true, um die DEX-Größe zu reduzieren und tiefgreifendere Optimierungen zu ermöglichen (siehe Verschleierung nach Möglichkeit aktivieren).
|
shrink_resources |
Entfernt nicht verwendete Ressourcen (res/-Einträge) nach der Codekomprimierung aus dem gepackten APK. Die Standardeinstellung ist false.
|
optimized_shrink_resources |
Führt die integrierte R8-Pipeline zur Code- und Ressourcenminimierung aus, sodass R8 Code- und Ressourcenreferenzen gemeinsam in einem einzigen Durchlauf verfolgt. Wenn shrink_resources: true festgelegt ist, wird standardmäßig RELEASE_USE_OPTIMIZED_RESOURCE_SHRINKING_BY_DEFAULT verwendet (true in Standard-Plattform-Builds).
|
proguard_flags_files |
Listet modulspezifische .flags- oder .pro-Dateien auf, die benutzerdefinierte Beibehaltungsregeln enthalten. Fügen Sie keine benutzerdefinierten Dateien hinzu, wenn Standardanmerkungen oder Standardeinstellungen ausreichen.
|
export_proguard_flags_files |
Gibt die proguard_flags_files dieser Bibliothek an nachgelagerte Module weiter, die statisch davon abhängen.
|
trace_references_from |
Listet direkte DEX-kompilierungsabhängige Ziele auf (verbunden über instrumentation_for oder libs), deren Bytecode-Verweise in dieses Ziel von R8 automatisch verfolgt und beibehalten werden (auch über override_android_app- und override_android_test-Varianten weitergegeben und für nicht abhängige Java-Begleitbibliotheken unterstützt, wenn sie mit einem .impl-Split gekoppelt sind).
|
allow_traced_references |
Ermöglicht es, dass direkt DEX-kompilierende abhängige Ziele (verbunden über instrumentation_for oder libs, z. B. Repository-übergreifende oder Anbieter-Tests, die nicht in trace_references_from aufgeführt werden können) die Verfolgung ihrer Bytecode-Referenzen in dieses Ziel aktivieren, indem trace_references: true festgelegt wird.
|
trace_references |
Wenn für eine direkte DEX-kompilierende Abhängigkeit eines Ziels mit allow_traced_references: true auf true gesetzt, wird R8 tracereferences für dieses Modul ausgeführt und die generierten Beibehaltungsregeln werden an das Zielmodul übergeben.
|
protect_api_surface |
Übergibt --protect-api-surface an R8. Wenn diese Option für ein java_sdk_library festgelegt ist, werden automatisch Metalava API-Keep-Regeln für alle aktivierten API-Bereiche (public, system, module-lib, system-server und test) generiert und an die Implementierungsbibliothek angehängt.
|
Statische Bibliotheken (android_library, java_library und AAR-Importe) können auch required_downstream_optimization: { enabled: true, optimize: true } in Android.bp festlegen, um zu erzwingen, dass alle nachgelagerten DEX-Kompilierungsziele, die sie statisch (direkt oder transitiv) verknüpfen, die R8-Verkleinerung und Bytecode-Optimierung aktivieren. Soong aktiviert dies automatisch auf androidx.compose.runtime_runtime, sodass Ziele, die mit Jetpack Compose verknüpft sind, mit R8 optimiert werden.
Verschleierung nach Möglichkeit aktivieren
In Soong ist obfuscate aus Gründen der Abwärtskompatibilität standardmäßig auf false festgelegt. Du solltest jedoch nach Möglichkeit obfuscate: true für android_app und eigenständige DEX-Ziele explizit festlegen:
- Kleinerer DEX- und
.vdex-Speicherbedarf: Durch das Umbenennen von Paketen, Klassen, Feldern und Methoden in kurze Kennungen (a,b) werden der DEX-Stringpool (string_idsundstring_data_item) und die Typdeskriptoren verkleinert. Da Android.vdex- und.odex-Dateien dem Prozessspeicher zuordnet, reduzieren kleinere Symboltabellen sowohl die Größe der Systempartition als auch den Speicherbedarf zur Laufzeit. - Tiefere Optimierungen des gesamten Programms:Wenn Sie R8 erlauben, Kennungen umzubenennen, werden Klassen zusammengeführt, Pakete vereinfacht und synthetische Klassen über Pakete hinweg dedupliziert, die R8 sonst überspringen müsste, um Namenskonflikte zu vermeiden.
- Vollständige Stacktrace-Symbolisierung:Wenn die Obfuskation in Soong aktiviert wird, wird die Debugging-Fähigkeit nicht beeinträchtigt. Für jedes mit R8 kompilierte Ziel gibt Soong eine
proguard_dictionary-Zuordnungsdatei im Zwischenverzeichnis des Moduls aus, bündelt alle Modulwörterbücher improguard-dict.zip-Artefakt des Builds, bettet einen Zuordnungshash (--map-id-template) in den DEX-Header ein und schreibt dieSourceFile-Attribute (--source-file-template) der Klasse neu, damitretraceStacktraces automatisch symbolisieren kann.
Behalten Sie obfuscate: false (oder explizit öffentliche API-Namen) nur für Folgendes bei:
- Gemeinsam genutzte Bibliotheken im Bootclasspath oder
system_server-Classpath: Module (java_libraryoderjava_sdk_library), die eine externe API-Oberfläche bereitstellen, die zur Laufzeit dynamisch von anderen Modulen verknüpft wird (z. B.framework.jar-,services.jar- oder<uses-library>-Ziele), müssen entwederobfuscate: falsebeibehalten oder ihre API-Oberfläche explizit mitprotect_api_surface: true-,@KeepForApi- oder-keep-Regeln beibehalten, damit Aufrufer, die mit ihren Stubs kompiliert wurden, Klassen- und Mitgliedsnamen zur Laufzeit auflösen können. Beijava_sdk_library-Modulen werden durch das Festlegen vonprotect_api_surface: trueautomatisch Metalava-ProGuard-Keep-Regeln für alle aktivierten API-Bereiche (public, system, module-lib, system-server und test) generiert und an die Implementierungsbibliothek angehängt. Wenn die allgemeine R8-Optimierung für nicht aktualisierbare Bootclasspath-Ziele (framework-minus-apex,extundframework-location) aktiviert ist, enthalten sie auchframework-hiddenapi-proguard-rules(generiert vonhiddenapi_to_proguard), um die in aktivenboot/hiddenapi/*.txt-Dateien aufgeführten Elemente beizubehalten (ohnehiddenapi-max-target-o.txt).
Prüfen Sie vor dem Aktivieren von obfuscate: true für eine Anwendung die folgenden Migrationsvoraussetzungen:
- Externe Test-APK-Abhängigkeiten: Wenn ein externes
android_test-APKinstrumentation_for: "MyApp"festlegt und interne Klassen oder Methoden vonMyAppdirekt aufruft, führt das Umbenennen dieser Symbole zur Testlaufzeit zuNoSuchMethodErroroderNoClassDefFoundError. Konfigurieretrace_references_from(oderallow_traced_referencesmittrace_references: true), strukturiere den Test als selbstinstrumentierendes APK, das statisch mitMyApp.implverknüpft ist, oder annotiere Test-Hooks mit@VisibleForTesting(siehe Schritt 2: Testgesteuerte Keep-Regeln migrieren), damit die für Tests erforderlichen internen Symbole beibehalten werden, während der Rest der App verschleiert wird. - Stringbasierte Reflexion und JNI: Wenn ein Modul Klassen, Methoden oder Felder anhand von Stringliteralen (
Class.forName,getDeclaredMethododer JNIFindClassundGetMethodID) ohne Beibehaltungsregeln oder Annotationen sucht, werden diese Ziele durch die Verschleierung umbenannt und die Laufzeitsuchen funktionieren nicht mehr. Beachten Sie, dass die nicht annotierte Reflexion auch untershrink: trueundoptimize: truefehlschlägt. Annotieren Sie diese Einstiegspunkte mit dem Leitfaden zum Beibehalten von Annotationen (@UsesReflection,@UsedByReflectionoder@UsedByNative), damit R8 ihre Namen beibehält und den Rest des Moduls verschleiert.
Prinzip „Keine benutzerdefinierten Flags“ befolgen
Das ideale Android-Plattformmodul enthält keine benutzerdefinierten proguard.flags- oder keep.xml-Dateien. Im Build der Android-Plattform sind die meisten benutzerdefinierten Beibehaltungsregeln redundant:
- Plattform-Baselines und AAPT2: Die meisten Einstiegspunkte werden bereits automatisch durch globale Plattform-Baselines (wie
@Keep, JNI-native-Methoden und@VisibleForTesting) beibehalten oder von AAPT2 ausAndroidManifest.xmlund Layoutressourcen generiert (siehe Standardmäßige Beibehaltungsregeln der Plattform). - Gezielte Alternativen: Wenn Einstiegspunkte beibehalten werden müssen, sollten Sie Deklarations-Site-Annotationen (
keepanno,@VisibleForTesting), exportierte Bibliotheksregeln (export_proguard_flags_files: true) oder statische Testverknüpfungen gegenüber separaten.flags-Dateien bevorzugen (siehe Vorhandene Beibehaltungsregeln prüfen und migrieren).
Bevor Sie eine benutzerdefinierte proguard.flags- oder keep.xml-Datei hinzufügen oder beibehalten, prüfen Sie, ob die Regel bereits von Standard-Baselines verarbeitet wird oder zu Codeannotationen migriert werden kann.
Standardmäßige Behaltensregeln der Plattform
Soong übergibt die folgenden globalen Baseline-Regeln automatisch an jeden R8-Aufruf für DEX-Kompilierungsziele (android_app, android_test und installierbare java_library-Module), die in build/soong/java/dex.go konfiguriert sind:
build/make/core/proguard.flags: Behält Klassen und Mitglieder bei, die mit@com.android.internal.annotations.VisibleForTestingin allen Paketen und mit@VisibleForTesting(androidx.annotation.VisibleForTestingodercom.google.common.annotations.VisibleForTesting) in den Paketenandroid.**,com.android.**undcom.google.android.**annotiert sind. Außerdem werden@TestApi,@Keep(androidx.annotation,android.support.annotation,com.android.internal.annotations),@KeepForWeakReference,@WeaklyReferencedCallbackund@dalvik.annotation.optimization.**beibehalten.build/make/core/proguard_basic_keeps.flags: BehältSourceFile-Attribute für Stacktraces, Laufzeitsichtbarkeits-Annotationen (RuntimeVisible*Annotations),Exceptions- undAnnotationDefault-Attribute,native-Methoden,Serializable-Mitglieder,@JavascriptInterface-Methoden,Throwable(String)-Konstruktoren,Parcelable$CREATOR-Felder und Protobuf-MessageLite-Felder bei.build/make/core/proguard/kotlin.flags: Unterdrückt harmlose Warnungen für bestimmte Kotlin-Meta-Annotationen (kotlin.Metadataundkotlin.annotation.{AnnotationRetention,AnnotationTarget,Retention,Target}) und entfernt Kotlin-DebugMetadata-Annotationen in Release-Builds.build/make/core/proguard/checknotnull.flags: Ersetzt gängige Null-Prüfungs-Hilfsaufrufe (com.google.common.base.Preconditions.checkNotNullunddagger.internal.Preconditions.checkNotNull*) durch prägnante Bytecode-Null-Prüfungen. In dieser Datei wirdObjects.requireNonNullabsichtlich weggelassen, um explizite Ausnahmemeldungen über Framework-API-Grenzen hinweg beizubehalten.build/make/core/proguard/enumvalues.flags: Behält die MethodenvaluesundvalueOffürenum-Typen bei, sofern sie nicht durch die Build-Konfiguration deaktiviert wurden.- Automatisch generierte AAPT2-Regeln: AAPT2 prüft zusammengeführte
AndroidManifest.xml-, Layout-XML- und Präferenz-XML-Dateien, um genaue Beibehaltungsregeln für jede registrierteActivity,Service,BroadcastReceiver,ContentProvider,BackupAgent,Application, benutzerdefinierteView-Unterklasse,Preference-Unterklasse und XML-android:onClick-Methode zu generieren.
Bestehende Beibehaltungsregeln prüfen und migrieren
Wenn Sie vorhandene proguard.flags- oder keep.xml-Dateien in einem Plattform-Repository prüfen, bewerten Sie jede Regel der Reihe nach anhand der folgenden vierstufigen Hierarchie:
Schritt 1: Redundante oder veraltete Regeln löschen
Lösche Regeln, die bereits durch globale Baselines, AAPT2-Manifest- und Layoutregeln oder R8-Standardeinstellungen abgedeckt sind, sowie Regeln, die auf nicht mehr vorhandene Klassen oder Pakete verweisen.
Wenn alle Regeln in einer .flags-Datei redundant sind, löschen Sie die Datei und entfernen Sie proguard_flags_files aus Android.bp. Wenn der verbleibende optimize-Block nur Standardeinstellungen wiederholt, entfernen Sie den redundanten Block und formatieren Sie die Build-Datei mit bpfmt -w Android.bp. Prüfen Sie beim Bereinigen eines Pakets, ob Companion-Module in Unterverzeichnissen (z. B. Kotlin-Zielvarianten) auf dieselbe Flag-Datei verweisen, und aktualisieren Sie sie gemeinsam.
Schritt 2: Testgesteuerte Aufbewahrungsregeln migrieren
Verschieben Sie testgesteuerte Beibehaltungsregeln aus benutzerdefinierten .flags-Produktionsdateien mit einem der folgenden Muster:
Verweise von Test- oder Companion-Zielen (
trace_references_fromoderallow_traced_references) verfolgen: Wenn sowohl das Ziel als auch ein direkt abhängiges Modul ihre eigene DEX (android_app,android_testoder eine dexiertejava_library) kompilieren und überinstrumentation_foroderlibsverbunden sind, kann Soong R8tracereferencesauf dem abhängigen Modul ausführen und die generierten Beibehaltungsregeln in die R8-Kompilierung des Ziels einspeisen, ohne einen Abhängigkeitszyklus zu erstellen oder eine manuelle.impl-Bibliotheksaufteilung zu erfordern:Tests im selben Repository (
trace_references_from): Listet die direkteninstrumentation_for- oderlibs-Abhängigkeiten des Zielmoduls auf. Dies wird auch automatisch überoverride_android_app- undoverride_android_test-Varianten weitergegeben und unterstützt weiterhin nicht abhängige Java-Begleitbibliotheken, wenn sie mit einem.impl-Split gekoppelt sind: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"], }Repository- oder herstellerübergreifende Tests (
allow_traced_referencesundtrace_references): Wenn das Zielmodul die Abhängigkeit nicht direkt intrace_references_fromauflisten kann (z. B. Tests in einem anderen Repository oder Herstellerbaum), legen Sieallow_traced_references: truefür das Zielmodul fest und aktivieren Sie die Option für jede Abhängigkeit mittrace_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, }, }Stringbasierte Reflexion in Tests:
tracereferencesprüft Bytecode-Konstantenpool-Referenzen und kann keine stringbasierte Reflexion in Tests erkennen (Class.forName,getDeclaredField,getDeclaredMethododer reflektierende Feldinjektions-Hilfsprogramme). Annotieren Sie Produktionsfelder und -methoden, auf die reflektiv zugegriffen wird, mit@VisibleForTesting(oder@UsedByReflection).
Implementierungsbibliothek in Tests verknüpfen (
static_libs): Wenn Sie keine reinen Testeinstiegspunkte im Produktions-APK beibehalten möchten und nicht möchten, dass Teständerungen R8 auf dem Produktionsziel noch einmal auslösen, platzieren Sie die App-Quelldateien in einemandroid_library(MyApp.impl) und verknüpfen SieMyApp.implstatisch mitMyAppundMyAppTests: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"], }Dieses 3-Modul-Muster ermöglicht es,
MyAppTestsals selbstinstrumentierenden Test mit vollem Zugriff auf interne Klassen auszuführen, währendMyApprein für Produktionseinstiegspunkte verkleinert und verschleiert werden kann.Test-Hooks mit
@VisibleForTestingannotieren: Wenn ein externes Test-APK nur wenige interne Methoden oder Konstruktoren für einandroid_app-Ziel aufruft, können Sie diese Deklarationen mit@VisibleForTestingannotieren. Die globalebuild/make/core/proguard.flags-Baseline behält@VisibleForTesting-Elemente in den Paketenandroid.**,com.android.**undcom.google.android.**automatisch ohne benutzerdefinierte.flags-Dateien bei. Verwenden Sie für Herstellermodule außerhalb dieser Namespacestrace_references_from,@UsedByReflectionoder exportierte Bibliotheksregeln. Wenn Sie manuell mit@VisibleForTestingannotieren, anstatttrace_references_fromodertrace_referenceszu verwenden, annotieren Sie auch Klassen, die an Mockitomock(...)oderspy(...)übergeben werden (nicht nur die Stub-Methoden), damockito-target(dexmaker-mockito) auf dem Gerät Laufzeit-Unterklassen generiert und R8 Klassen ohne Unterklassen alsfinal(MockitoException: Can't mock/spy class ... - final class) markieren kann.Sich überschneidende statische Bibliotheken (
jarjar_rules) vermeiden oder neu verpacken: Wenn sowohl einandroid_appals auch das zugehörigeinstrumentation_for-Test-APK dieselbe Bibliothek statisch verknüpfen (direkt, z. B.guava, oder transitiv), siehttracereferencesdiese im Test definierten Klassen und gibt keine Keep-Regeln für die App aus. Zur Laufzeit überschattet der übergeordnete Classloader der App die Kopie des Tests mit seinen durch R8 verkleinerten Klassen, was zuIllegalAccessErroroderNoSuchMethodErrorführt. Wenn der Test keine eigene gebündelte Kopie benötigt, entfernen Sie das Duplikat aus derstatic_libsdes Tests (oder verknüpfen Sie es überlibs), um den Konflikt zu vermeiden. Andernfalls packen Sie die sich überschneidenden statischen Bibliothekspakete der App mitjarjar_rulesunter einem modulspezifischen Präfix neu.
Schritt 3: Regeln aus der Inhaberbibliothek exportieren
Wenn für eine freigegebene java_library oder android_library Aufbewahrungsregeln für die eigene interne Reflexion oder JNI-Callbacks erforderlich sind, definieren Sie die Regeln für das Bibliotheksziel und legen Sie export_proguard_flags_files: true fest:
java_library {
name: "my-shared-library",
srcs: ["src/**/*.java"],
optimize: {
proguard_flags_files: ["proguard.flags"],
export_proguard_flags_files: true,
},
}
Alle nachgelagerten android_app- und Bibliotheksziele, die statisch mit my-shared-library verknüpft sind, übernehmen diese Regeln automatisch. Nachgelagerte Apps müssen sie also nicht duplizieren. Wenn mehrere Module in verschiedenen Verzeichnissen einen Regelsatz gemeinsam nutzen müssen, der nicht an eine einzelne Codebibliothek gebunden ist, veröffentlichen Sie die Datei .flags über eine explizite filegroup in Android.bp, damit Module über Paketgrenzen hinweg sauber auf :my-shared-flags verweisen können. Allgemeine Informationen zum Erstellen von Keep-Regeln für Bibliotheksnutzer, ohne die Optimierung nachgelagerter Apps einzuschränken, finden Sie unter Optimierung für Bibliotheksautoren.
Schritt 4: Deklarationen im Quellcode annotieren
Wenn auf eine Klasse, eine Methode, einen Konstruktor oder ein Feld über Reflection oder JNI zugegriffen wird und diese nicht von AAPT2 oder globalen Referenzen abgedeckt werden, ersetzen Sie die losgelösten -keep-Regeln in .flags-Dateien durch Quellanmerkungen aus dem Leitfaden zum Beibehalten von Anmerkungen (siehe keepanno-Javadoc-Referenz):
@UsesReflection: Verwenden Sie diese Annotation, wenn Sie den Code steuern, der die Reflexion ausführt. Platzieren Sie sie an der Aufrufstelle der Reflexion, um zu deklarieren, auf welche Zielklassen, ‑methoden oder ‑felder dynamisch zugegriffen wird. Da@UsesReflectionautomatisch eine Vorbedingung codiert, dass die annotierte Aufrufstelle selbst erreichbar ist, entfernt R8 sowohl den Aufrufer als auch das reflektierende Ziel, wenn die Aufrufstelle nicht verwendet wird.@UsedByReflection: Platzieren Sie diese Annotation auf Klassen, Methoden, Feldern oder Konstruktoren, die von externem Code oder externen Bibliotheken reflektiv instanziiert oder aufgerufen werden, z. B. Klassen, die mitClass.forNameausBundle- oderSettings-Schlüsseln geladen werden, oder Plug-in-Klassen, die über dynamische Klassenlader geladen werden. Geben Siekind(z. B.KeepItemKind.CLASS_AND_METHODS),preconditions(@KeepCondition) und Parametereinschränkungen an, damit R8 nur den genauen reflektierenden Vertrag beibehält und nicht verwendete Mitglieder der Klasse weiterhin optimieren oder entfernen kann. Füge@UsedByReflectionnicht zu im Manifest registrierten Komponenten wieJobServiceoderBroadcastReceiverhinzu, da AAPT2 sie bereits behält.@UsedByNative: Wird auf Methoden oder Felder angewendet, auf die über C- oder C++-JNI-Code mitGetMethodID,GetStaticMethodIDoderGetFieldIDzugegriffen wird.@KeepForApi: Wird auf Bibliotheks-API-Klassen oder -Mitglieder angewendet, die intakt bleiben müssen, wenn eine Bibliothek vor der Verteilung verkleinert wird.@Keep(androidx.annotation.Keep): Als Fallback verwenden, wennkeepannonicht anwendbar ist. Wenn Sie@Keepauf eine Klasse anwenden, werden die Klasse und alle ihre Mitglieder bedingungslos beibehalten. Wenn Sie@Keepauf eine Methode oder ein Feld anwenden, wird ein bedingungsloser Einstiegspunkt erstellt, der das Mitglied und die enthaltende Klasse beibehält, auch wenn die Klasse nie instanziiert wird.keepannodrückt dagegen eine bedingte Erreichbarkeit aus.
So verwenden Sie R8-keepanno-Annotationen in einem Soong-Modul:
Fügen Sie
"keepanno-annotations"zulibsinAndroid.bphinzu:libs: [ "keepanno-annotations", ],Importieren Sie
com.android.tools.r8.keepanno.annotations.*und annotieren Sie die Deklaration oder Aufrufstelle im Java- oder Kotlin-Quellcode: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 übersetzt
keepanno-Annotationen direkt in sein internes Keep-Rule-Modell und entfernt die Annotationen aus der endgültigen DEX-Ausgabe, ohne dass Laufzeit-Bytecode-Overhead entsteht.
Häufige Fehler vermeiden
In den folgenden Abschnitten werden häufige proguard.flags- und keep.xml-Fehler im Plattformcode und deren Behebung beschrieben.
Allgemeine Aufbewahrungsregeln für Komponenten
# Don't do this:
-keep class * extends android.app.Activity
-keep class * extends android.app.Service
-keep class * extends android.content.BroadcastReceiver
- Warum die Leistung beeinträchtigt wird:Diese Regel ist bei AAPT2 völlig redundant.
Da ein Platzhalter verwendet wird, ohne zu prüfen, ob die Komponente in der endgültigen zusammengeführten
AndroidManifest.xmlregistriert ist, wird R8 gezwungen, jedeActivity-,Service- oderBroadcastReceiver-Unterklasse beizubehalten, die irgendwo im Klassenpfad gefunden wird, einschließlich nicht verwendeter Bibliothekskomponenten und deaktivierter Debug-Aktivitäten. - Empfohlene Lösung:Löschen Sie die Regel. AAPT2 prüft das zusammengeführte Manifest und generiert genaue
-keep-Regeln für registrierte Komponenten.
Allgemeine XML-Klick-Handler-Beibehaltungsregeln
# Don't do this:
-keepclassmembers class * {
public void *(android.view.View);
}
- Warum die Leistung beeinträchtigt wird:Wenn jede
public void *(View)-Methode in jeder Klasse im Modul beibehalten wird, kann R8 keine Methode entfernen oder inline einfügen, die einen einzelnenView-Parameter akzeptiert. - Empfohlene Lösung:Löschen Sie die Regel. AAPT2 scannt Layout-XML-Dateien und generiert gezielte Beibehaltungsregeln für Methoden, auf die durch
android:onClick-Attribute verwiesen wird.
Allgemeine Getter- und Setter-Keep-Regeln für Ansichten
# 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*();
}
- Warum die Leistung beeinträchtigt wird:Diese Regel zwingt R8, alle Getter und Setter für jede
View-Unterklasse in der App und allen verknüpften Bibliotheken (einschließlich AndroidX- und Material-Bibliotheken) beizubehalten. Dadurch wird das Entfernen von ungenutzten Methoden und das Inlining im UI-Code blockiert. - Empfohlene Lösung:Löschen Sie die Regel. AAPT2 behält bereits Konstruktoren für benutzerdefinierte
View-Klassen bei, die aus Layout-XML-Dateien generiert wurden. Wenn im Code eine Ansichtseigenschaft mit reflektierenden Stringnamen wieObjectAnimator.ofFloat(view, "translationZ", ...)animiert wird, ersetzen Sie den Stringnamen durch einen typisierten Eigenschaftsverweis (View.TRANSLATION_Zoder eine benutzerdefinierteFloatProperty- oderIntProperty-Implementierung), um die Reflexion vollständig zu vermeiden. Wenn sich der Zugriff auf reflektierende Properties nicht vermeiden lässt, kommentieren Sie den spezifischen Getter oder Setter mit@UsedByReflection.
Pauschale Optimierung oder Verschleierung deaktiviert
# Don't do this in proguard.flags:
-dontoptimize
-dontshrink
-dontobfuscate
- Warum sich das negativ auf die Leistung auswirkt:Wenn Sie
-dontoptimizeoder-dontshrinkin einer.flags-Datei platzieren, werden dieAndroid.bp-Einstellungen des Moduls ohne Benachrichtigung überschrieben und Optimierungsdurchläufe für das gesamte Ziel deaktiviert. Noch schlimmer: Wenn eine Bibliothek eine.flags-Datei mit-dontoptimizeexportiert, wird die R8-Optimierung für jede nachgelagerteandroid_appdeaktiviert, die die Bibliothek verknüpft. - Empfohlene Korrektur: Entfernen Sie
-dontoptimize,-dontshrinkund-dontobfuscateaus.flags-Dateien. Steuern Sie das Optimierungsverhalten explizit inAndroid.bpmit dem Blockoptimize(shrink,optimize,obfuscate) auf dem Blattziel.
Diagnose-Flags in übernommenen Regeln
# Don't do this in proguard.flags:
-verbose
-printmapping proguard.map
-printusage usage.txt
-printconfiguration config.txt
- Warum es den Build beeinträchtigt: Diagnose-Flags überfluten Build-Logs oder versuchen, Ausgabedateien während Sandbox-Soong-Builds in lokale Pfade zu schreiben.
- Empfohlene Lösung: Löschen Sie diese Flags aus den festgeschriebenen
.flags-Dateien. Soong schreibt automatisch R8-Zuordnungs- und Nutzungsausgaben wieproguard_dictionaryundproguard_usage.zipin das Zwischenverzeichnis des Moduls unterout/soong/.intermediates/.
Produktionsaufbewahrungsregeln für Testcode
- Warum das die Leistung beeinträchtigt:Wenn du benutzerdefinierte
-keep-Regeln nur für den Testzugriff hinzufügst, muss dieser Code unverschleiert bleiben und in Produktions-Builds beibehalten werden. Wenn Sietrace_references_from(oderallow_traced_referencesmittrace_references: true) konfigurieren oder Methoden mit@VisibleForTestingannotieren, müssen Sie keine manuellen-keep-Regeln verwalten und nur das beibehalten, worauf sich Tests beziehen. Diese Symbole werden jedoch weiterhin in der Produktionsbinärdatei bereitgestellt. - Empfohlene Lösung:Entfernen Sie benutzerdefinierte Test-
-keep-Regeln, indem Sietrace_references_from(oderallow_traced_referencesundtrace_references: true) aktivieren, oder strukturieren Sie die App in eine.impl-Bibliothek, die mitstatic_libsmit einem selbstinstrumentierenden Test-APK verknüpft ist, um reine Testeinstiegspunkte vollständig aus dem Produktions-APK herauszuhalten, wie in Schritt 2: Testgesteuerte Beibehaltungsregeln migrieren beschrieben.
Alles einschließende Platzhalter für Ressourcen 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/*" />
- Warum die Leistung beeinträchtigt wird: Pauschale Platzhalter behalten jede Ressource des übereinstimmenden Typs im gesamten Modul und seinen Abhängigkeiten bei. Dadurch wird die Entfernung von Ressourcen (
shrink_resources: true) verhindert und das APK sowie die zugeordneteresources.arsc-Tabelle werden aufgebläht. - Empfohlene Lösung:
- Statische Ressourcenreferenzen gegenüber
keep.xmlbevorzugen: Vermeide die Verwendung der MethodeResources.getIdentifier, um eine begrenzte Anzahl von Ressourcen dynamisch zu suchen, z. B. nummerierte Teststrings oder thematische Drawables. Verwenden Sie stattdessen eineswitch-Anweisung zur Kompilierzeit oder ordnen Sie statischeR.id-,R.string- oderR.drawable-Konstanten zu. Mit statischen Verweisen können R8 und AAPT2 die genauen Live-Ressourcen verfolgen, den Overhead für die Stringsuche zur Laufzeit eliminieren und die Notwendigkeit vonkeep.xmlvollständig beseitigen. - Bestimmte Ressourcennamen auflisten: Wenn eine dynamische Suche erforderlich ist, z. B. durch einen Drittanbieter-Lizenzleser, listen Sie die genauen Ressourcenkennungen in
tools:keepauf (z. B.tools:keep="@raw/third_party_licenses"). - Redundante
keep.xml-Dateien löschen: Wenn die aufgeführten Ressourcen bereits statisch im Code (R.raw.foo) oder XML (@raw/foo) referenziert sind, werden sie vom Shrinker automatisch beibehalten, sodass Sieres/raw/keep.xmllöschen können.
- Statische Ressourcenreferenzen gegenüber
Regeländerungen prüfen und auditieren
Wenn Sie Aufbewahrungsregeln in proguard.flags oder keep.xml entfernen oder einschränken, prüfen Sie, ob das Modul fehlerfrei erstellt wird, Unit- und Instrumentierungstests besteht und alle erforderlichen Einstiegspunkte beibehält.
Module erstellen und testen
Erstelle das Modul sauber, um zu prüfen, ob R8 und AAPT2 ohne Warnungen zu fehlenden Referenzen abgeschlossen werden:
m <MODULE_NAME>Führen Sie mit
atestUnit- und Instrumentierungstests für das Modul aus:atest <TEST_MODULE_NAME>Führe für System-Apps, privilegierte Dienste oder hardwareabhängige Module Instrumentierungs- und UI-Tests auf physischen Zielgeräten oder in deinem Gerätetestlabor aus, um Laufzeitreflexionspfade, IPC-Bindungen und Ressourceninflation zu testen.
DEX- und Ressourcendifferenzen prüfen
Vergleicht das kompilierte APK oder JAR vor und nach den Regeländerungen, um zu prüfen, ob R8 ungenutzten Code und Ressourcen entfernt, ohne erwartete Einstiegspunkte zu entfernen:
Verwenden Sie
apkanalyzeroderdexdump, um beibehaltene Klassen, Methoden und Felder in den Ausgabe-APK- oder DEX-Dateien zu prüfen:apkanalyzer dex packages $OUT/system/priv-app/<APP_NAME>/<APP_NAME>.apkWenn du
keep.xmländerst odershrink_resourcesaktivierst, kannst du mitaapt2 dump resourcesprüfen, ob im Build nicht verwendete Ressourcen entfernt und erforderliche Ressourcen beibehalten werden:aapt2 dump resources $OUT/system/priv-app/<APP_NAME>/<APP_NAME>.apkWenn Sie
prebuilts/r8/r8.jardirekt auf derclasses.jar-Datei eines Moduls vor R8 ausführen, um Flag-Änderungen offline ohne einen vollständigen Plattform-Build zu prüfen, übergeben Sie sowohl die--lib-Klassenpfad-JARs des Moduls (android.jar,framework.jarund freigegebenelibs) als auch die von AAPT2 generiertenproguard.options-Regeln ausAndroidManifest.xmlund Layout-XMLs. Ohne diese Eingaben kann R8 keine Framework-Supertypen (Activity,Service,Phone,Handler) auflösen und behält nicht genügend Hierarchiemethoden bei.
Keep-Radius und Regel-Subsumption mit R8 analysieren
Der Open-Source-R8-Compiler enthält einen Keep Radius-Analysator (in Android Studio und Gradle auch als R8 Configuration Analyzer verfügbar), der die genaue Auswirkung jeder Keep-Regel während der Kompilierung misst. Soong integriert diesen Analyzer direkt in den Build der Android-Plattform (konfiguriert in build/soong/java/dex.go).
So analysieren Sie Beibehaltungsregeln für ein Plattformmodul oder einen gesamten Build:
Führen Sie den Build mit der Umgebungsvariablen
R8_DUMP_KEEP_RADIUS=trueaus:R8_DUMP_KEEP_RADIUS=true m <MODULE_NAME>Wenn
R8_DUMP_KEEP_RADIUS=truefestgelegt ist, weist Soong R8 an, Messwerte für Beibehaltungsregeln in einer Zwischendateir8keepradius.pbfür jedes kompilierte Modul unterout/soong/.intermediates/aufzuzeichnen. Für jede Beibehaltungsregel undkeepanno-Annotation zeichnet R8 Folgendes auf:- Unmittelbarer Beibehaltungsradius: Die genauen Klassen, Felder und Methoden, die durch die Regel beibehalten werden, sowie die spezifischen Einschränkungen, die sie für die Verkleinerung, Optimierung oder Verschleierung erzwingt.
- Regelsubsumtion:Welche anderen Aufbewahrungsregeln oder Annotationen bereits dieselben Elemente aufbewahren. Wenn eine benutzerdefinierte Regel vollständig von einer AAPT2-Regel oder einer globalen Baseline-Regel abgedeckt wird, können Sie sie bedenkenlos löschen.
- Paketweite und globale Regeln: Regeln, die allgemeine paketweite Platzhalter verwenden oder globale Konfigurationsanweisungen anwenden.
Konvertieren Sie die
r8keepradius.pb-Ausgabe mitKeepRadiusHtmlReportGenerator(inprebuilts/r8/r8.jarenthalten) in einen interaktiven HTML-Bericht:# 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_reportsWenn Sie ein Verzeichnis angeben, durchsucht
KeepRadiusHtmlReportGeneratoralle*keepradius*.pb-Dateien, generiert für jedes Modul einen HTML-Bericht und erstelltout/keep_radius_reports/keepradius.html, in dem die Anzahl der Live-Elemente, die Anzahl der beibehaltenen Elemente und die Beibehaltungsregeln mit dem größten Radius im gesamten Build zusammengefasst werden. Weitere Informationen zur Interpretation der Werte für Verkleinerung, Optimierung und Obfuskation im generierten Bericht finden Sie unter R8 Configuration Analyzer verwenden.
Soong unterstützt auch zusätzliche R8-Diagnose- und Inner-Loop-Umgebungsvariablen in build/soong/java/dex.go:
R8_DUMP_INPUT=true: Schreibtr8inputs.zipin das Zwischenverzeichnis des Moduls, das alle Eingabe-JARs, Bibliotheks-JARs und zusammengeführten ProGuard-Konfigurationen für die eigenständige R8-Reproduktion enthält.R8_DUMP_PERFETTO_TRACE=true: Schreibtr8trace.ptracein das Zwischenverzeichnis des Moduls, um R8-Kompilierungsdurchläufe in Perfetto zu prüfen.SOONG_SKIP_TRACE_REFERENCES=true: Überspringt die Ausführung vontracereferences-Regeln, die vontrace_references_fromodertrace_referenceskonfiguriert wurden, um lokale Inner-Loop-Builds beim Iterieren von Testcode zu beschleunigen.
Nutzerregeln für die Bibliothek validieren
Wenn ein java_library oder android_library Aufbewahrungsregeln an nachgelagerte Nutzer (export_proguard_flags_files: true) exportiert, dürfen diese Regeln keine globalen Flags enthalten, die die Optimierung für die nutzende App ändern oder deaktivieren.
R8 bietet einen Open-Source-Keep-Rule-Analyzer (ProcessKeepRules), der im Android-Plattform-Build als Host-Tool process-keep-rules (definiert in prebuilts/r8/Android.bp) bereitgestellt wird:
# 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>
Das process-keep-rules-Tool parst jede Konfigurationsdatei und schlägt mit Datei- und Zeilendiagnosen fehl, wenn es auf Anweisungen stößt, die in den Regeln für Bibliotheksnutzer nicht zulässig sind. Zu den unzulässigen Anweisungen gehören die Deaktivierung der globalen Optimierung, der Verkleinerung oder der Verschleierung (z. B. -dontoptimize oder -dontshrink), Flags für das Umpacken von Paketen und die Zugriffsänderung, Flags für die Diagnose oder Zuordnung sowie -keepattributes auf App-Ebene.
Quellbäume mit pgaudit.py prüfen
Um nicht erstellte Quellverzeichnisse zusammen mit den Build-Zeit-Analysetools von R8 zu prüfen, enthält der Android-Plattformbaum das Skript pgaudit.py unter build/make/core/proguard/tools/. Dieses statische Analysetool scannt Android.bp-, proguard.flags- und keep.xml-Dateien in einem Repository, um Regeln zu kennzeichnen, die bereits von AAPT2 oder globalen Plattform-Baselines abgedeckt sind, sowie allgemeine Platzhalter, -dontoptimize-Überschreibungen und Regeln, die nur für Tests gelten:
# 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
Plattform- und OEM-Teams können pgaudit.py für eine schnelle Quellbaum-Triage, process-keep-rules zur Validierung exportierter Bibliotheksregeln und R8_DUMP_KEEP_RADIUS=true mit KeepRadiusHtmlReportGenerator kombinieren, um den genauen Aufbewahrungsradius für Klassen, Felder und Methoden jeder Regel in einem Build zu messen.