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 Reflexion verwendet, ohne zugehörige Keep-Regeln zu bündeln, 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.

codegen gegenüber Reflection bevorzugen

Im Allgemeinen sollten Sie Bibliotheken auswählen, die Codegenerierung (codegen) anstelle von Reflection verwenden. Mit codegen kann der Optimierer leichter feststellen, welcher Code zur Laufzeit tatsächlich verwendet wird und welcher Code entfernt werden kann. Es kann schwierig sein, zu erkennen, ob eine Bibliothek codegen oder Reflection verwendet. Es gibt jedoch einige Anzeichen. Weitere Informationen finden Sie in den Tipps.

Weitere Informationen zu codegen im Vergleich zur Reflection finden Sie unter Optimierung für Bibliotheksautoren.

Allgemeine Tipps zur Auswahl von Bibliotheken

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

Auf Optimierungsprobleme prüfen

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

  • Die AndroidX-Bibliotheken und Bibliotheken wie Hilt eignen sich gut für die App-Optimierung, da sie Codegen anstelle von Reflection verwenden. Wenn sie Reflection verwenden, legen sie minimale Regeln für das Behalten fest, um nur den Code zu behalten, der benötigt wird.
  • In Serialization-Bibliotheken wird häufig Reflection verwendet, um Boilerplate-Code beim Instanziieren oder Serialisieren von Objekten zu vermeiden. Anstatt reflexionsbasierte Ansätze (z. B. Gson für JSON) zu verwenden, sollten Sie nach Bibliotheken suchen, die diese Probleme mithilfe von Codegen vermeiden. Verwenden Sie stattdessen beispielsweise Kotlin Serialization.
  • Bibliotheken, die paketweite Regeln zum Beibehalten von Dateien enthalten, sollten nach Möglichkeit vermieden werden. Paketweite Beibehaltungsregeln können helfen, Fehler zu beheben. Breite Beibehaltungsregeln sollten jedoch nach und nach so angepasst werden, dass nur der Code beibehalten wird, der benötigt wird. Weitere Informationen finden Sie unter Optimierungen nach und nach einführen.

Optimierung nach dem Hinzufügen einer neuen Bibliothek aktivieren

Wenn Sie eine neue Bibliothek hinzufügen, aktivieren Sie danach die Optimierung und prüfen Sie, ob Fehler auftreten. Wenn Fehler auftreten, suchen Sie nach Alternativen zu dieser Bibliothek oder schreiben Sie Regeln für das Behalten von Daten. Wenn eine Bibliothek nicht mit der Optimierung kompatibel ist, melden Sie einen Fehler für diese Bibliothek.

Regeln sind additiv

Mehrere Regeln für das Behalten von Inhalten werden miteinander kombiniert. Das bedeutet, dass bestimmte Regeln, die eine Bibliothek abhängig machen, 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.

Verwendung von Reflexion prüfen (erweitert)

Sie können möglicherweise anhand des Codes einer Bibliothek erkennen, ob sie Reflection verwendet. Wenn die Bibliothek Reflection verwendet, prüfen Sie, ob zugehörige Regeln für das Behalten von Daten vorhanden sind. Eine Bibliothek verwendet wahrscheinlich Reflection, wenn Folgendes zutrifft:

  • Klassen oder Methoden aus den Paketen kotlin.reflect oder java.lang.reflect werden verwendet.
  • Die Funktionen Class.forName oder classLoader.getClass werden verwendet.
  • Liest Anmerkungen zur Laufzeit, z. B. wenn ein Anmerkungswert mit val value = myClass.getAnnotation() oder val value = myMethod.getAnnotation() gespeichert und dann etwas mit value ausgeführt wird
  • Ruft Methoden mit dem Methodennamen als String auf, z. B.:

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

Ungültige Regeln für das Behalten von Inhalten herausfiltern (fortgeschrittene Nutzer)

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 herausfiltern, wie im folgenden Code gezeigt:

// 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")
        }
    }
}

Fallstudie: Warum Gson bei Optimierungen bricht

Gson ist eine Serialization-Bibliothek, die häufig Probleme bei der App-Optimierung verursacht, da sie stark auf Reflection setzt. Das folgende Code-Snippet zeigt, wie Gson normalerweise verwendet wird, was leicht zu Abstürzen zur Laufzeit führen kann. Wenn Sie Gson verwenden, um eine Liste von Nutzerobjekten abzurufen, rufen Sie den Konstruktor nicht auf und übergeben der fromJson()-Funktion keine Fabrik. Wenn appdefinierte Klassen ohne eines der folgenden Elemente erstellt oder verwendet werden, ist das ein Hinweis darauf, dass in einer Bibliothek die offene Reflexion verwendet wird:

  • App-Klasse, die eine Bibliothek oder Standardschnittstelle oder -klasse implementiert
  • Codegenerierungs-Plug-in 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()

Wenn R8 diesen Code analysiert und UserList oder User nirgends instanziiert findet, kann es Felder umbenennen oder Konstruktoren entfernen, die anscheinend nicht verwendet werden, was zu einem Absturz Ihrer App führt. 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.

Beachten Sie, dass sowohl Room als auch Hilt appdefinierte Typen erstellen, aber codegen verwenden, um die Notwendigkeit von Reflection zu vermeiden.