Als Bibliotheksautor müssen Sie dafür sorgen, dass App-Entwickler Ihre Bibliothek problemlos in ihre App einbinden können und gleichzeitig eine hohe Endnutzerqualität gewährleistet ist. Das bedeutet, dass Ihre Bibliothek mit der Android-Optimierung (R8) kompatibel sein muss, ohne dass der Entwickler zusätzliche Einstellungen vornehmen muss. Alternativ müssen Sie dokumentieren, dass die Bibliothek möglicherweise nicht für die Verwendung auf Android geeignet ist. Es ist entscheidend, dass Bibliotheken, die für die Verwendung auf Android vorgesehen sind, keine wichtigen App Optimierungen verhindern und zusätzliche Optimierungsanforderungen erfüllen.
Diese Dokumentation richtet sich an Entwickler von veröffentlichten Bibliotheken, kann aber auch für Entwickler von internen Bibliotheksmodulen in einer großen, modularisierten App nützlich sein.
Wenn Sie App-Entwickler sind und mehr über die Optimierung Ihrer Android-App erfahren möchten, lesen Sie App-Optimierung aktivieren. Informationen dazu, welche Bibliotheken geeignet sind finden Sie unter Bibliotheken sorgfältig auswählen.
Typen von Keep-Regeln
Es gibt zwei verschiedene Arten von Keep-Regeln, die in Bibliotheken verwendet werden können:
- Keep-Regeln für Nutzer müssen Regeln angeben, die alles beibehalten, was die Bibliothek widerspiegelt. Wenn eine Bibliothek Reflection oder JNI verwendet, um ihren Code oder Code aufzurufen, der von einer Client-App definiert wurde, müssen diese Regeln beschreiben, welcher Code beibehalten werden muss. Bibliotheken sollten Keep-Regeln für Nutzer enthalten, die dasselbe Format wie Keep-Regeln für Apps verwenden. Diese Regeln werden in Bibliotheksartefakten (AARs oder JARs) gebündelt und bei der Optimierung von Android-Apps automatisch verwendet, wenn die Bibliothek verwendet wird. Diese Regeln werden in der
Datei verwaltet, die mit der
consumerProguardFilesProperty in Ihrerbuild.gradle.kts(oderbuild.gradle) Datei angegeben ist. Weitere Informationen finden Sie unter Weitere Empfehlungen. - Keep-Regeln für den Bibliotheks-Build werden angewendet, wenn Ihre Bibliothek erstellt wird. Sie sind nur erforderlich, wenn Sie Ihre Bibliothek zur Build-Zeit teilweise optimieren möchten. Sie müssen verhindern, dass die öffentliche API der Bibliothek entfernt wird. Andernfalls ist die öffentliche API nicht in der Bibliotheksverteilung vorhanden und App-Entwickler können die Bibliothek nicht verwenden. Diese Regeln werden in der Datei
verwaltet, die mit der
proguardFilesProperty in der Dateibuild.gradle.kts(oderbuild.gradle) angegeben ist. Weitere Informationen finden Sie unter AAR-Bibliotheks-Build optimieren.
Optimierungsanforderungen und -richtlinien
Die R8-Konfiguration in Bibliotheken hat globale Auswirkungen auf die endgültige Binärgröße und Leistung der verbrauchenden App. Abgesehen von den allgemeinen Best Practices für Keep-Regeln müssen Bibliotheksautoren bestimmte Anforderungen erfüllen und zusätzliche Richtlinien beachten.
Optimierungsanforderungen einhalten
Ineffizienzen in Bibliotheken tragen maßgeblich zu App-Bloat, verschwendetem Arbeitsspeicher, langsamen Starts und ANRs (App antwortet nicht-Fehlern) bei. Bibliotheken müssen die folgenden Anforderungen erfüllen, um die App-Qualität und die Nutzerfreundlichkeit nicht erheblich zu beeinträchtigen.
Keine umfassenden oder paketweiten Keep-Regeln:Ihre Bibliothek darf keine umfassenden Keep-Regeln enthalten, die den Großteil des Codes in Ihrer Bibliothek oder in einer anderen Bibliothek beibehalten. Umfassende Keep-Regeln können Abstürze kurzfristig beheben, aber sie erhöhen die App-Größe aller Apps, die Ihre Bibliothek verwenden.
Fügen Sie keine paketweiten Keep-Regeln (z. B.
-keep class com.mylibrary.** {*; }) für Pakete in Ihrer Bibliothek oder anderen referenzierten Bibliotheken ein. Solche Regeln beschränken die Optimierung für diese Pakete in allen Apps, die Ihre Bibliothek verwenden.Keine unangemessenen globalen Regeln: Verwenden Sie niemals globale Optionen wie
-dontobfuscateoder-allowaccessmodification.Nach Möglichkeit Codegen anstelle von Reflection verwenden:Verwenden Sie nach Möglichkeit Codegenerierung (codegen) anstelle von Reflection. Codegen und Reflection sind beides gängige Ansätze, um beim Programmieren Boilerplate-Code zu vermeiden. Codegen ist jedoch besser mit einem App-Optimierungstool wie R8 kompatibel.
Bei der Codegenerierung wird der Code während des Build-Prozesses analysiert und geändert. Da nach der Kompilierung keine größeren Änderungen mehr vorgenommen werden, weiß das Optimierungstool, welcher Code letztendlich benötigt wird und welcher sicher entfernt werden kann.
Bei der Reflection wird der Code zur Laufzeit analysiert und manipuliert. Da der Code erst bei der Ausführung wirklich fertig ist, weiß das Optimierungstool nicht, welcher Code sicher entfernt werden kann. Es wird wahrscheinlich Code entfernt, der zur Laufzeit dynamisch durch Reflection verwendet wird, was zu App-Abstürzen bei Nutzern führt.
Viele moderne Bibliotheken verwenden Codegen anstelle von Reflection. Siehe KSP für einen gängigen Einstiegspunkt, der von Room, Hilt und vielen anderen verwendet wird.
Vollständigen R8-Modus unterstützen: Ihre Bibliothek sollte nicht abstürzen, wenn der vollständige R8-Modus aktiviert ist. Der vollständige Modus von R8 ist der empfohlene Modus für die Verwendung von R8 und ist seit AGP 8.0, das 2023 stabilisiert wurde, der Standard. Wenn Ihre Bibliothek unter R8 abstürzt, besteht die Lösung darin, den spezifischen Reflection- oder JNI-Einstiegspunkt zu identifizieren und eine gezielte Regel hinzuzufügen, anstatt das gesamte Paket beizubehalten.
Weitere Empfehlungen
Neben den Optimierungsanforderungen gibt es noch weitere Empfehlungen.
- Verwenden Sie
-repackageclassesnicht in der Datei mit den Keep-Regeln für Nutzer Ihrer Bibliothek. Um den Build Ihrer Bibliothek zu optimieren, können Sie-repackageclassesmit einem internen Paketnamen wie<your.library.package>.internalin der Datei mit den Keep-Regeln für den Build Ihrer Bibliothek verwenden. Dadurch kann die Effizienz Ihrer Bibliothek in nicht optimierten Apps verbessert werden. Im Allgemeinen ist dies jedoch nicht erforderlich, da Apps ebenfalls optimiert werden sollten. - Deklarieren Sie alle Attribute, die für die Funktion Ihrer Bibliothek erforderlich sind, in den Dateien mit den Keep-Regeln Ihrer Bibliothek, auch wenn es eine Überschneidung mit den in
proguard-android-optimize.txtdefinierten Attributen gibt. - Wenn Sie die folgenden Attribute in der Verteilung Ihrer Bibliothek benötigen, verwalten Sie sie in der Datei mit den Keep-Regeln für den Build Ihrer Bibliothek und nicht in der Datei mit den Keep-Regeln für Nutzer Ihrer Bibliothek:
AnnotationDefaultEnclosingMethodExceptionsInnerClassesRuntimeInvisibleAnnotationsRuntimeInvisibleParameterAnnotationsRuntimeInvisibleTypeAnnotationsRuntimeVisibleAnnotationsRuntimeVisibleParameterAnnotationsRuntimeVisibleTypeAnnotationsSignature
- Bibliotheksautoren sollten das Attribut
RuntimeVisibleAnnotationsin ihren Keep-Regeln für Nutzer beibehalten, wenn Annotationen zur Laufzeit verwendet werden. - Bibliotheksautoren sollten die folgenden globalen Optionen nicht in ihren Keep-Regeln für Nutzer verwenden:
-include-basedirectory-injars-outjars-libraryjars-repackageclasses-flattenpackagehierarchy-allowaccessmodification-renamesourcefileattribute-ignorewarnings-addconfigurationdebugging-printconfiguration-printmapping-printusage-printseeds-applymapping-obfuscationdictionary-classobfuscationdictionary-packageobfuscationdictionary
Wann Reflection in Ordnung ist
Wenn Sie Reflection verwenden müssen, sollten Sie sie nur für eine der folgenden Optionen verwenden:
- Bestimmte Zieltypen (bestimmte Schnittstellenimplementierer oder Unterklassen)
- Code mit einer bestimmten Laufzeitannotation
Durch die Verwendung von Reflection auf diese Weise werden die Laufzeitkosten begrenzt und gezielte Keep-Regeln für Nutzer können geschrieben werden.
Diese spezifische und gezielte Form der Reflection ist ein Muster, das sowohl im Android-Framework (z. B. beim Laden von Systemressourcen) als auch in AndroidX-Bibliotheken (z. B. beim Erstellen von WorkManager
ListenableWorkers oder RoomDatabases) zu finden ist. Im Gegensatz dazu ist die offene Reflection
von Gson ist nicht für die Verwendung in Android-Apps geeignet.
Häufige Missverständnisse
Einige häufige Missverständnisse können dazu führen, dass Sie R8 falsch konfigurieren. Dazu gehören:
Falsches Verständnis der Optimierungen von R8: Anders als allgemein angenommen beschränken sich die Optimierungen von R8 nicht nur auf die Verschleierung, sondern umfassen auch die Codekomprimierung und logische Optimierungen mit Methoden-Inlining und Klassen-Merging-Techniken. Weitere Informationen finden Sie unter R8-Optimierung Übersicht.
Optimierung von verschleierten Bibliotheken umgehen: Ein häufiger Fehler besteht darin, eine Bibliothek von der Optimierung auszuschließen, weil sie beim Kompilieren in ein AAR (Android-Archiv) oder JAR (Java-Archiv) optimiert oder verschleiert wurde. Die Optimierungen während der Build-Zeit der Bibliothek sind begrenzt und Ihre App sollte die Optimierung der Bibliothek nicht deaktivieren, indem sie in eine Keep-Regel aufgenommen wird. Weitere Informationen finden Sie unter Übersicht über die R8-Optimierung.
Falsches Verständnis der Option
-keep: Die Regel-keepverhindert, dass R8 seine Optimierungsdurchläufe ausführt. Weitere Informationen finden Sie unter Die richtige Keep-Option auswählen.
Qualität der Keep-Regeln für Nutzer validieren
Wenn Sie prüfen möchten, ob Ihre Bibliothek bei der Einbettung in eine App zu viel Codeoptimierung verhindert, verwenden Sie eine Bibliotheksbeispiel- oder Integrationstest-App. Aktivieren Sie R8 und prüfen Sie die allgemeine Qualität der R8-Konfiguration der App mit dem R8 Configuration Analyzer.
Wenn Ihre Nutzerregeln die Optimierung für große Teile des Codes Ihrer Bibliothek einschränken, müssen sie verfeinert werden. Beschränken Sie die Nutzerregeln und testen Sie alle betroffenen Aspekte Ihrer Bibliothek in einer App, in der R8 aktiviert ist.
Regelverpackung konfigurieren
Damit Ihre Keep-Regeln für Nutzer korrekt angewendet werden, müssen Sie sie je nach Bibliotheksformat entsprechend verpacken.
AAR-Bibliotheken
Wenn Sie Keep-Regeln für Nutzer für eine AAR-Bibliothek hinzufügen möchten, verwenden Sie die Option consumerProguardFiles im Build-Skript des Android-Bibliotheksmoduls. Weitere Informationen finden Sie in unserem
Leitfaden zum Erstellen von Bibliotheksmodulen.
Kotlin
android {
defaultConfig {
consumerProguardFiles("consumer-proguard-rules.pro")
}
...
}
Groovy
android {
defaultConfig {
consumerProguardFiles 'consumer-proguard-rules.pro'
}
...
}
JAR-Bibliotheken
Wenn Sie Regeln mit Ihrer Kotlin- oder Java-Bibliothek bündeln möchten, die als JAR ausgeliefert wird, legen Sie die Regeldatei mit einem beliebigen Dateinamen in das Verzeichnis META-INF/proguard/ der endgültigen JAR-Datei.
Wenn sich Ihr Code beispielsweise in <libraryroot>/src/main/kotlin befindet, legen Sie eine Datei mit den Regeln für Nutzer unter <libraryroot>/src/main/resources/META-INF/proguard/consumer-proguard-rules.pro
ab. Die Regeln werden dann an der richtigen Stelle in der Ausgabe-JAR-Datei gebündelt.
Prüfen Sie, ob die Regeln in der endgültigen JAR-Datei korrekt gebündelt sind, indem Sie prüfen, ob sie sich im Verzeichnis META-INF/proguard befinden.
AAR-Bibliotheks-Build optimieren (erweitert)
Im Allgemeinen müssen Sie einen Bibliotheks-Build nicht direkt optimieren, da die möglichen Optimierungen zur Build-Zeit der Bibliothek sehr begrenzt sind. Als Bibliotheksentwickler müssen Sie mehrere Optimierungsstufen und das Verhalten von Keep-Regeln sowohl zur Build-Zeit der Bibliothek als auch der App berücksichtigen, bevor Sie die Bibliothek optimieren.
Wenn Sie Ihre Bibliothek dennoch zur Build-Zeit optimieren möchten, wird dies vom Android-Gradle-Plug-in unterstützt.
Kotlin
android {
buildTypes {
release {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
configureEach {
consumerProguardFiles("consumer-rules.pro")
}
}
}
Groovy
android {
buildTypes {
release {
minifyEnabled true
proguardFiles
getDefaultProguardFile('proguard-android-optimize.txt'),
'proguard-rules.pro'
}
configureEach {
consumerProguardFiles "consumer-rules.pro"
}
}
}
Das Verhalten von proguardFiles unterscheidet sich stark von consumerProguardFiles:
proguardFileswerden zur Build-Zeit verwendet, oft zusammen mitgetDefaultProguardFile("proguard-android-optimize.txt"), um zu definieren, welcher Teil Ihrer Bibliothek während des Bibliotheks-Builds beibehalten werden soll. Mindestens ist dies Ihre öffentliche API.consumerProguardFileswerden dagegen in die Bibliothek verpackt, um die Optimierungen zu beeinflussen, die später während des Builds einer App erfolgen, die Ihre Bibliothek verwendet.
Wenn Ihre Bibliothek beispielsweise Reflection verwendet, um interne Klassen zu erstellen, müssen Sie die Keep-Regeln möglicherweise sowohl in proguardFiles als auch in consumerProguardFiles definieren.
Wenn Sie -repackageclasses im Build Ihrer Bibliothek verwenden, verpacken Sie die Klassen in ein Unterpaket innerhalb des Pakets Ihrer Bibliothek. Verwenden Sie beispielsweise -repackageclasses
'com.example.mylibrary.internal' anstelle von -repackageclasses 'internal'.
Compose UI-Leistung optimieren
Wenn Ihre Bibliothek Compose UI-Komponenten enthält, optimieren Sie sie sowohl für die Binärverkleinerung mit R8 (wie zuvor beschrieben) als auch für die Effizienz der Neukomposition:
- Stabile Composables entwerfen: Damit der Compiler Ihre Composables sicher überspringen kann, wenn sich die Eingaben nicht geändert haben, strukturieren Sie Ihre Datenmodelle und Bibliothekskonfigurationen so, dass sie den Stabilitätsregeln entsprechen. Weitere Informationen finden Sie unter Stabilität in Compose.
- Compiler-Messwerte prüfen: Verwenden Sie die Compose-Compiler-Messwerte, um zu prüfen, ob die Einstiegspunkte Ihrer Bibliothek als neu startbar und überspringbar gekennzeichnet sind. Dadurch wird der Aufwand für die Neukomposition für Nutzer-Apps minimiert. Weitere Informationen finden Sie unter Stabilitätsprobleme diagnostizieren.
Verschiedene R8-Versionen unterstützen (erweitert)
Sie können Regeln an bestimmte Versionen von R8 anpassen. Dadurch kann Ihre Bibliothek optimal in Projekten verwendet werden, die neuere R8-Versionen verwenden, während vorhandene Regeln weiterhin in Projekten mit älteren R8-Versionen verwendet werden können.
Wenn Sie gezielte R8-Regeln angeben möchten, müssen Sie sie in das Verzeichnis META-INF/com.android.tools in classes.jar eines AAR oder in das Verzeichnis META-INF/com.android.tools eines JAR einfügen.
In an AAR library:
proguard.txt (legacy location, the file name must be "proguard.txt")
classes.jar
└── META-INF
└── com.android.tools (location of targeted R8 rules)
├── r8-from-<X>-upto-<Y>/<R8-rule-files>
└── ... (more directories with the same name format)
In a JAR library:
META-INF
├── proguard/<ProGuard-rule-files> (legacy location)
└── com.android.tools (location of targeted R8 rules)
├── r8-from-<X>-upto-<Y>/<R8-rule-files>
└── ... (more directories with the same name format)
Im META-INF/com.android.tools Verzeichnis können mehrere
Unterverzeichnisse mit Namen im Format r8-from-<X>-upto-<Y> vorhanden sein, um anzugeben,
für welche R8-Versionen die Regeln geschrieben wurden. Jedes Unterverzeichnis kann eine oder mehrere Dateien mit den R8-Regeln enthalten, mit beliebigen Dateinamen und Erweiterungen.
Beachten Sie, dass die -from-<X> und -upto-<Y> Teile optional sind, die <Y> Version
ist exklusiv und die Versionsbereiche in der Regel fortlaufend sind, können sich aber auch
überschneiden.
Beispiele für Verzeichnisnamen, die eine Reihe gezielter R8-Regeln darstellen, sind r8, r8-upto-8.0.0, r8-from-8.0.0-upto-8.2.0 und
r8-from-8.2.0. Die Regeln im Verzeichnis r8 können von allen R8-Versionen verwendet werden. Die Regeln unter
dem r8-from-8.0.0-upto-8.2.0 Verzeichnis können von R8 ab Version
8.0.0 bis einschließlich nicht einschließlich Version 8.2.0 verwendet werden.
Das Android-Gradle-Plug-in verwendet diese Informationen, um alle Regeln auszuwählen, die von der aktuellen R8-Version verwendet werden können. Wenn in einer Bibliothek keine gezielten R8
Regeln angegeben sind, wählt das Android-Gradle-Plug-in die Regeln aus den Legacy-Speicherorten
(proguard.txt für ein AAR oder META-INF/proguard/<ProGuard-rule-files> für ein
JAR).