Bibliotheken mit Bedacht auswählen

Wenn Sie die App-Optimierung aktivieren möchten, müssen Sie Bibliotheken verwenden, die mit der Android-Optimierung kompatibel sind. Wenn eine Bibliothek nicht für die Android-Optimierung konfiguriert ist, z. B. wenn sie Reflection ohne zugehörige Keep-Regeln verwendet, ist sie möglicherweise nicht für eine Android-App geeignet. Auf dieser Seite wird erläutert, warum einige Bibliotheken besser für die App-Optimierung geeignet sind, und es werden allgemeine Tipps zur Auswahl gegeben.

Allgemeine Tipps zur Auswahl von Bibliotheken

Mit diesen Tipps können Sie dafür sorgen, dass Ihre Bibliotheken mit der App-Optimierung kompatibel sind.

Codegenerierung gegenüber Reflexion bevorzugen

Wählen Sie Bibliotheken aus, die Codegenerierung (codegen) anstelle von Reflexion verwenden. Mit der Codegenerierung kann der Optimierer bestimmen, welcher Code tatsächlich zur Laufzeit verwendet wird und welcher Code entfernt werden kann. Es kann schwierig sein, festzustellen, ob eine Bibliothek Codegenerierung oder Reflexion verwendet. Es gibt jedoch einige Anzeichen – siehe Tipps.

Weitere Informationen zu Codegenerierung im Vergleich zu Reflexion finden Sie unter Optimierung für Bibliotheksautoren.

Auf Verwendung von Reflexion prüfen (erweitert)

Ob eine Bibliothek Reflection verwendet, können Sie anhand des Codes feststellen. Wenn die Bibliothek Reflexion verwendet, prüfen Sie, ob sie entsprechende Beibehaltungsregeln enthält. Eine Bibliothek verwendet wahrscheinlich Reflexion, wenn sie Folgendes tut:

  • Verwendet Klassen oder Methoden aus den Paketen kotlin.reflect oder java.lang.reflect.
  • Verwendet die Funktionen Class.forName oder classLoader.getClass.
  • Liest Annotationen zur Laufzeit, z. B. wenn ein Annotationswert mit val value = myClass.getAnnotation() oder val value = myMethod.getAnnotation() gespeichert und dann etwas mit value gemacht wird.
  • Ruft Methoden mit dem Methodennamen als String auf, wie im folgenden Beispiel:

    // Calls the private `processData` API with reflection
    myObject.javaClass.getMethod("processData", DataType::class.java)
    ?.invoke(myObject, data)
    

Auf Optimierungsprobleme prüfen

Wenn Sie eine neue Bibliothek in Betracht ziehen, sollten Sie sich den Issue Tracker und die Onlinediskussionen der Bibliothek ansehen, um zu prüfen, ob es Probleme im Zusammenhang mit der Reduzierung oder der Konfiguration der App-Optimierung gibt. Wenn ja, sollten Sie nach Alternativen zu dieser Bibliothek suchen. Beachten Sie dabei Folgendes:

  • Die AndroidX-Bibliotheken und Bibliotheken wie Hilt eignen sich gut für die App-Optimierung, da sie meist Codegenerierung anstelle von Reflexion verwenden. Wenn sie Reflexion verwenden, stellen sie minimale Beibehaltungsregeln bereit, um nur den erforderlichen Code beizubehalten.
  • Serialisierungsbibliotheken verwenden häufig Reflexion, um Boilerplate-Code beim Instanziieren oder Serialisieren von Objekten zu vermeiden. Anstatt reflexionsbasierte Ansätze (wie Gson für JSON) zu verwenden, sollten Sie nach Bibliotheken suchen, die Codegenerierung verwenden, um diese Probleme zu vermeiden, z. B. Kotlin Serialization oder Moshi mit Codegenerierung.
  • Vermeiden Sie nach Möglichkeit Bibliotheken, die paketweite Beibehaltungsregeln enthalten. Paketweite Beibehaltungsregeln können helfen, Fehler zu beheben. Breite Beibehaltungsregeln sollten jedoch letztendlich so verfeinert werden, dass nur der benötigte Code beibehalten wird. Weitere Informationen finden Sie unter Optimierungen schrittweise übernehmen.
  • Bevor Sie eine App veröffentlichen, die eine Drittanbieterbibliothek verwendet, sollten Sie die bereitgestellten Keep-Regeln mit dem R8 Configuration Analyzer prüfen. Anhand des Berichts können Sie prüfen, ob die Beibehaltungsregeln der Bibliothek zu weit gefasst sind und R8 daran hindern, wichtige Optimierungen an Ihrer Codebasis vorzunehmen. So wird sichergestellt, dass die ausgewählten Bibliotheken den Leistungszielen Ihrer App entsprechen und keine unnötige Konfigurationsüberlastung verursachen.
  • Bibliotheken sollten nicht erfordern, dass Sie Beibehaltungsregeln aus der Dokumentation in eine Datei in Ihrem Projekt kopieren und einfügen, insbesondere keine paketweiten Beibehaltungsregeln. Diese Regeln stellen langfristig eine Wartungslast für den App-Entwickler dar und sind im Laufe der Zeit nur schwer zu optimieren und zu ändern.

Optimierung nach dem Hinzufügen einer neuen Bibliothek aktivieren

Wenn Sie eine neue Bibliothek hinzufügen, aktivieren Sie anschließend die Optimierung und prüfen Sie, ob Fehler auftreten. Wenn Fehler auftreten, suchen Sie nach Alternativen zu dieser Bibliothek oder schreiben Sie Beibehaltungsregeln. Wenn eine Bibliothek nicht mit der Optimierung kompatibel ist, melden Sie einen Fehler für diese Bibliothek.

Fehlerhafte Aufbewahrungsregeln herausfiltern (erweitert)

Aufbewahrungsregeln werden miteinander kombiniert. Das bedeutet, dass bestimmte Regeln, die eine Bibliotheksabhängigkeit enthält, nicht entfernt werden können und sich auf die Kompilierung anderer Teile Ihrer App auswirken können. Wenn eine Bibliothek beispielsweise eine Regel zum Deaktivieren von Codeoptimierungen enthält, werden durch diese Regel die Optimierungen für Ihr gesamtes Projekt deaktiviert.

Sie sollten Bibliotheken mit Beibehaltungsregeln vermeiden, die Code beibehalten, der eigentlich entfernt werden sollte. Wenn Sie sie jedoch verwenden müssen, können Sie die Regeln wie im folgenden Code herausfiltern:

// If you're using AGP 8.4 and higher
buildTypes {
    release {
        optimization.keepRules {
          it.ignoreFrom("com.somelibrary:somelibrary")
        }
    }
}

// If you're using AGP 7.3-8.3
buildTypes {
    release {
        optimization.keepRules {
          it.ignoreExternalDependencies("com.somelibrary:somelibrary")
        }
    }
}

Empfohlener Workflow für allgemeine Bibliotheksregeln

Wenn der R8 Configuration Analyzer zeigt, dass eine Bibliothek zu weit gefasste Keep-Regeln enthält, filtern Sie diese Regeln nur in lokalen Builds, nicht in Produktions-Builds. So testen Sie, wie stark sich die Regeln auf Ihre App auswirken:

  1. Bibliothek aktualisieren: Verwenden Sie die aktuelle Version der Bibliothek, die vom R8 Configuration Analyzer gemeldet wurde, da neuere Versionen möglicherweise engere Consumer-Keep-Regeln enthalten. Die aktuelle Version finden Sie in der Dokumentation der Bibliothek oder im Maven-Repository. Android Studio kennzeichnet auch neuere Versionen vieler Abhängigkeiten. Führen Sie nach der Aktualisierung den R8 Configuration Analyzer noch einmal aus, um zu prüfen, ob die allgemeinen Keep-Regeln weiterhin vorhanden sind.
  2. Mögliche Verbesserung lokal prüfen: Kopieren Sie die gebündelten Nutzerregeln der Bibliothek in Ihre lokale Datei mit Beibehaltungsregeln, filtern Sie die gebündelten Regeln wie unter Fehlerhafte Beibehaltungsregeln herausfiltern beschrieben heraus und grenzen Sie die kopierten Regeln ein. Führen Sie den R8 Configuration Analyzer vor und nach dem Einschränken der Regeln aus, um die Werte für die Verkleinerung, Optimierung und Verschleierung Ihrer App zu vergleichen.
  3. Ergebnisse melden: Reichen Sie beim Verwalter der Bibliothek einen Fehlerbericht mit den empirischen Daten aus Ihrem R8 Configuration Analyzer-Bericht ein. Zeigen Sie beispielsweise die genaue Anzahl der nicht referenzierten Klassen oder Methoden, die durch die Platzhalterregel beibehalten werden.

Fallstudie: Warum Gson bei Optimierungen fehlschlägt

Gson ist eine Serialisierungsbibliothek, die häufig Probleme bei der App-Optimierung verursacht, da sie stark auf Reflexion setzt. Das folgende Code-Snippet zeigt, wie Gson normalerweise verwendet wird, was zu Abstürzen zur Laufzeit führen kann. Beachten Sie, dass Sie beim Abrufen einer Liste von User-Objekten mit Gson weder den Konstruktor aufrufen noch eine Factory an die Funktion fromJson() übergeben. Das Erstellen oder Verwenden von App-definierten Klassen ohne eine der folgenden Optionen ist ein Zeichen dafür, dass eine Bibliothek möglicherweise eine offene Reflexion verwendet:

  • App-Klasse, die eine Bibliothek, eine Standardschnittstelle oder eine Standardklasse implementiert
  • Plug-in zur Codegenerierung wie KSP
class User(val name: String)
class UserList(val users: List<User>)

// This code runs in debug mode, but crashes when optimizations are enabled
Gson().fromJson("""[{"name":"myname"}]""", User::class.java).toString()

Informationen zur Funktionsweise von R8 in Gson finden Sie in den Gson-Nutzerregeln. Wenn R8 diesen Code analysiert und nicht sieht, dass UserList oder User irgendwo instanziiert wird, kann es Felder umbenennen oder Konstruktoren entfernen, die nicht verwendet zu werden scheinen. Dies kann zum Absturz Ihrer App führen. Wenn Sie andere Bibliotheken auf ähnliche Weise verwenden, sollten Sie prüfen, ob sie die App-Optimierung beeinträchtigen. Falls ja, sollten Sie sie vermeiden.

@SerializedName mit neueren Gson-Versionen verwenden

Wenn Sie Ihre Datenmodelle so definieren möchten, dass sie mit den Gson-Nutzerregeln kompatibel sind, annotieren Sie Ihre Felder mit @SerializedName, wie im folgenden Snippet gezeigt:

import com.google.gson.annotations.SerializedName

class User(@SerializedName("name") val name: String)
class UserList(@SerializedName("users") val users: List<User>)

Wenn Sie die Annotation @SerializedName verwenden, kann R8 Ihre Modellklassen mit den Keep-Regeln abgleichen, die in Gson Version 2.11.0 und höher enthalten sind. R8 behält die annotierten Felder und erforderlichen Konstruktoren automatisch bei. Sie können sich also auf die gebündelten Regeln der Bibliothek verlassen, ohne manuelle ProGuard-Konfigurationen in Ihrem Projekt verwalten zu müssen.

Beachten Sie, dass Room, Hilt und Moshi mit Codegenerierung App-definierte Typen erstellen, aber die Codegenerierung verwenden, um die Notwendigkeit von Reflexion zu vermeiden.