App verkleinern, verschleiern und optimieren

Damit Ihre App so klein und schnell wie möglich ist, sollten Sie sie optimieren und reduzieren. Ihren Release-Build mit isMinifyEnabled = true erstellen.

Dadurch wird die Verkleinerung aktiviert, wodurch nicht verwendeter Code und nicht verwendete Ressourcen entfernt werden. Verschleierung, mit der die Namen der Klassen und Mitglieder Ihrer App gekürzt werden und Optimierung, mit der aggressivere Strategien angewendet werden, um und die Leistung Ihrer App zu verbessern. Auf dieser Seite wird beschrieben, wie R8 diese Aufgaben zur Kompilierungszeit für Ihr Projekt ausführt .

Wenn Sie Ihr Projekt mit Android-Gradle-Plug-in 3.4.0 oder höher, Das Plug-in verwendet ProGuard nicht mehr, um den Code für die Kompilierungszeit zu optimieren. Das Plug-in verwendet stattdessen den R8-Compiler, um Folgendes zu verarbeiten: Kompilierungszeit-Aufgaben:

  • Codeverkleinerung (oder Baumschütteln): Erkennt und entfernt ungenutzte Klassen, Felder, Methoden und Attribute aus Ihrer App und ihrer Bibliothek Abhängigkeiten (und ist somit ein wertvolles Tool zur Umgehung des Referenzlimit von 64.000). Wenn Sie beispielsweise nur wenige APIs einer Bibliotheksabhängigkeit vorliegen, kann durch Verkleinern der Bibliothekscode identifiziert werden. dass Ihre App nicht verwendet, und entfernen Sie nur diesen Code aus der App. Bis Weitere Informationen finden Sie im Abschnitt Code verkleinern.
  • Ressourcenverkleinerung:Entfernt nicht verwendete Ressourcen aus der gepackten Anwendung. nicht verwendete Ressourcen in die Bibliotheksabhängigkeiten Ihrer App. Funktioniert in zusammen mit der Codekomprimierung, sodass nach dem Entfernen nicht verwendeter Code Ressourcen, auf die nicht mehr verwiesen wird, können ebenfalls entfernt werden. Weitere Informationen finden Sie im Abschnitt verringern Sie Ihre Ressourcen.
  • Optimierung:Überprüfung und Neuschreibung des Codes zur Verbesserung der Laufzeit und die Größe der DEX-Dateien Ihrer App weiter reduzieren. Dieses verbessert die Laufzeitleistung des Codes um bis zu 30 % und verbessert Start- und Frame-Timings. Wenn R8 beispielsweise erkennt, dass die else {} für eine bestimmte if/else-Anweisung nie verwendet wird, entfernt R8 den Code für Zweig else {}. Weitere Informationen finden Sie im Abschnitt Code-Optimierung.
  • Verschleierung (oder Reduzierung von Kennungen): kürzt den Namen von Klassen. und Mitglieder, wodurch sich die Größe der DEX-Dateien verringert. Weitere Informationen findest du unter im Abschnitt zur Verschleierung von Code.

Beim Erstellen der Release-Version Ihrer App kann R8 so konfiguriert werden, dass die oben beschriebenen Aufgaben zur Kompilierungszeit. Sie können auch bestimmte oder das Verhalten von R8 mithilfe von ProGuard-Regeldateien anpassen. R8 funktioniert mit allen Ihren vorhandenen ProGuard-Regeldateien. Wenn Sie das Android-Gradle-Plug-in für R8 aktualisieren, sollten Sie keine Änderung vornehmen müssen Ihre bestehenden Regeln.

Verkleinerung, Verschleierung und Optimierung aktivieren

Wenn Sie Android Studio 3.4 oder das Android-Gradle-Plug-in 3.4.0 und höher verwenden, ist R8 den Standard-Compiler, der den Java-Bytecode Ihres Projekts in den DEX konvertiert das auf der Android-Plattform ausgeführt wird. Wenn Sie jedoch ein neues Projekt Verkleinern, Verschleierung und Codeoptimierung ist standardmäßig aktiviert. Das liegt daran, dass diese Optimierungen bei der Kompilierungszeit die Build-Dauer Ihres Projekts und können zu Fehlern führen, wenn Sie festlegen, welcher Code beibehalten werden soll.

Daher ist es am besten, diese Aufgaben während der Kompilierung zu aktivieren, wenn Sie die endgültige Version die Sie vor der Veröffentlichung testen. Um Verkleinerung, Verschleierung Optimierung: Nehmen Sie Folgendes in Ihr Build-Skript auf Projektebene auf:

Kotlin

android {
    buildTypes {
        getByName("release") {
            // Enables code shrinking, obfuscation, and optimization for only
            // your project's release build type. Make sure to use a build
            // variant with `isDebuggable=false`.
            isMinifyEnabled = true

            // Enables resource shrinking, which is performed by the
            // Android Gradle plugin.
            isShrinkResources = true

            proguardFiles(
                // Includes the default ProGuard rules files that are packaged with
                // the Android Gradle plugin. To learn more, go to the section about
                // R8 configuration files.
                getDefaultProguardFile("proguard-android-optimize.txt"),

                // Includes a local, custom Proguard rules file
                "proguard-rules.pro"
            )
        }
    }
    ...
}

Cool

android {
    buildTypes {
        release {
            // Enables code shrinking, obfuscation, and optimization for only
            // your project's release build type. Make sure to use a build
            // variant with `debuggable false`.
            minifyEnabled true

            // Enables resource shrinking, which is performed by the
            // Android Gradle plugin.
            shrinkResources true

            // Includes the default ProGuard rules files that are packaged with
            // the Android Gradle plugin. To learn more, go to the section about
            // R8 configuration files.
            proguardFiles getDefaultProguardFile(
                    'proguard-android-optimize.txt'),
                    'proguard-rules.pro'
        }
    }
    ...
}

R8-Konfigurationsdateien

R8 verwendet ProGuard-Regeldateien, um das Standardverhalten zu ändern die Struktur Ihrer App zu verstehen, z. B. die Klassen, die als Einstiegspunkte dienen in den Code Ihrer App einfügen. Sie können zwar einige dieser Regeldateien ändern, können automatisch von Tools zur Kompilierungszeit wie AAPT2 oder aus den Bibliotheksabhängigkeiten Ihrer App übernommen. In der folgenden Tabelle werden die Quellen von ProGuard-Regeldateien, die R8 verwendet.

Quelle Standort Beschreibung
Android Studio <module-dir>/proguard-rules.pro Wenn Sie ein neues Modul mit Android Studio erstellen, erstellt die IDE ein proguard-rules.pro im Stammverzeichnis dieses Moduls.

Standardmäßig werden auf diese Datei keine Regeln angewendet. Fügen Sie also Ihre eigenen ProGuard-Regeln hier an, z. B. Ihre benutzerdefiniert Regeln beibehalten.

Android-Gradle-Plug-in Wird vom Android-Gradle-Plug-in bei der Kompilierung generiert. Das Android-Gradle-Plug-in generiert proguard-android-optimize.txt mit Regeln, die ist für die meisten Android-Projekte nützlich und ermöglicht @Keep* Anmerkungen.

Wenn Sie mit Android Studio ein neues Modul erstellen, wird die Modulebene Das Build-Skript enthält diese Regeldatei in Ihrem Release-Build für Sie.

Hinweis:Das Android-Gradle-Plug-in enthält zusätzliche vordefinierte ProGuard-Funktionen. Regeldateien. Es wird jedoch empfohlen, proguard-android-optimize.txt

Bibliotheksabhängigkeiten AAE-Bibliotheken: <library-dir>/proguard.txt

JAR-Bibliotheken: <library-dir>/META-INF/proguard/

Wenn eine AAR-Bibliothek mit einer eigenen ProGuard-Regeldatei veröffentlicht wird und Sie AAR als Abhängigkeit zur Kompilierungszeit enthält, wendet R8 seine wenn Sie Ihr Projekt kompilieren.

Die Verwendung von Regeldateien, die mit AAR-Bibliotheken gepackt sind, ist nützlich, wenn bestimmte die Regeln erforderlich sind, damit die Bibliothek ordnungsgemäß funktioniert, d. h. die Bibliothek hat der Entwickler die Schritte zur Fehlerbehebung für Sie durchgeführt.

Da ProGuard-Regeln additiv sind, können bestimmte Regeln, die eine Abhängigkeit von einer AAR-Bibliothek enthalten, nicht entfernt werden. kann sich auf die Zusammenstellung anderer Teile Ihrer App auswirken. Wenn zum Beispiel ein Bibliothek eine Regel zum Deaktivieren von Codeoptimierungen enthält. Diese Regel deaktiviert Optimierungen für Ihr gesamtes Projekt.

Android Asset Package Tool 2 (AAPT2) Nachdem Sie Ihr Projekt mit minifyEnabled true erstellt haben, gehen Sie so vor: <module-dir>/build/intermediates/proguard-rules/debug/aapt_rules.txt AAPT2 erstellt Keep-Regeln basierend auf Verweisen auf Klassen in den Manifest, Layouts und anderen App-Ressourcen. Zum Beispiel enthält AAPT2 für jede Aktivität, die du im Manifest deiner App als Einstiegspunkts hinzufügen.
Benutzerdefinierte Konfigurationsdateien Wenn Sie ein neues Modul mit Android Studio erstellen, erstellt <module-dir>/proguard-rules.pro für Sie, damit Sie Ihre eigenen hinzufügen können Regeln. Sie können zusätzliche Konfigurationen, und R8 wendet sie zur Kompilierungszeit an.

Wenn Sie das Attribut minifyEnabled auf true setzen, kombiniert R8 Regeln aus allen den oben aufgeführten verfügbaren Quellen. Das sollten Sie sich merken, wenn Sie Fehlerbehebung mit R8, da andere Abhängigkeiten bei der Kompilierungszeit wie Bibliotheksabhängigkeiten, kann zu Änderungen im R8-Verhalten führen, die Sie noch nicht kennen.

Um einen vollständigen Bericht mit allen Regeln zu erstellen, die R8 beim Erstellen Ihres Projekt enthalten, fügen Sie Folgendes in die proguard-rules.pro-Datei Ihres Moduls ein:

// You can specify any path and filename.
-printconfiguration ~/tmp/full-r8-config.txt

Zusätzliche Konfigurationen hinzufügen

Wenn Sie ein neues Projekt oder Modul mit Android Studio erstellen, erstellt die IDE ein <module-dir>/proguard-rules.pro-Datei, in die Sie Ihre eigenen Regeln einfügen können. Ich können auch zusätzliche Regeln aus anderen Dateien enthalten, indem Sie diese proguardFiles im Build-Skript des Moduls ein.

So können Sie z. B. Regeln hinzufügen, die für jede Build-Variante spezifisch sind, indem Sie weitere proguardFiles-Property im entsprechenden productFlavor-Block. Die über die Gradle-Datei, dass flavor2-rules.pro dem Produkt-Flavor flavor2 hinzugefügt wird. flavor2 verwendet jetzt alle drei ProGuard-Regeln, weil die von der release angewendet.

Außerdem können Sie die Eigenschaft testProguardFiles hinzufügen, die ein Liste der ProGuard-Dateien, die nur im Test-APK enthalten sind:

Kotlin

android {
    ...
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                // List additional ProGuard rules for the given build type here. By default,
                // Android Studio creates and includes an empty rules file for you (located
                // at the root directory of each module).
                "proguard-rules.pro"
            )
            testProguardFiles(
                // The proguard files listed here are included in the
                // test APK only.
                "test-proguard-rules.pro"
            )
        }
    }
    flavorDimensions.add("version")
    productFlavors {
        create("flavor1") {
            ...
        }
        create("flavor2") {
            proguardFile("flavor2-rules.pro")
        }
    }
}

Cool

android {
    ...
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles
                getDefaultProguardFile('proguard-android-optimize.txt'),
                // List additional ProGuard rules for the given build type here. By default,
                // Android Studio creates and includes an empty rules file for you (located
                // at the root directory of each module).
                'proguard-rules.pro'
            testProguardFiles
                // The proguard files listed here are included in the
                // test APK only.
                'test-proguard-rules.pro'
        }
    }
    flavorDimensions "version"
    productFlavors {
        flavor1 {
            ...
        }
        flavor2 {
            proguardFile 'flavor2-rules.pro'
        }
    }
}

Code verkleinern

Das Verkleinern von Code mit R8 ist standardmäßig aktiviert, wenn Sie den minifyEnabled festlegen zu true.

Code Shrinking (auch Tree Shaking) ist der Vorgang, bei dem Code entfernt wird. die R8 zur Laufzeit nicht benötigt. Dieser Prozess kann die wenn Ihre App viele Bibliotheksabhängigkeiten enthält, aber und nutzt nur einen kleinen Teil ihrer Funktionalität.

Um den Code Ihrer App zu verkürzen, bestimmt R8 zunächst alle Einstiegspunkte in die basierend auf dem kombinierten Satz von Konfigurationsdateien. Zu diesen Einstiegspunkten gehören alle Klassen, die die Android-Plattform zum Öffnen von die Aktivitäten oder Dienste Ihrer App. Ausgehend von jedem Einstiegspunkt prüft R8 um eine Grafik mit allen Methoden, Mitgliedsvariablen und anderen Klassen, auf die Ihre App zur Laufzeit zugreifen kann. Code, der nicht mit wird dieses Diagramm als nicht erreichbar angesehen und möglicherweise aus der App entfernt.

Abbildung 1 zeigt eine Anwendung mit einer Laufzeitbibliothekabhängigkeit. Bei der Untersuchung der App-Code ermittelt R8, dass die Methoden foo(), faz() und bar() ist vom Einstiegspunkt MainActivity.class aus erreichbar. Die Klasse OkayApi.class oder seine Methode baz() wird von deiner App niemals zur Laufzeit verwendet und R8 entfernt diesen Code beim Verkleinern Ihrer App.

Abbildung 1: Zur Kompilierungszeit erstellt R8 eine basierend auf den kombinierten Keep-Regeln Ihres Projekts, um nicht erreichbaren Code zu ermitteln.

R8 bestimmt Einstiegspunkte über -keep-Regeln in der R8-Konfigurationsdateien. Das heißt, dass Sie Regeln Klassen, die R8 beim Verkleinern der App nicht verwerfen sollte und die R8 berücksichtigt als mögliche Einstiegspunkte in Ihre App. Das Android-Gradle-Plug-in und AAPT2 generieren automatisch Notizen, die von den meisten Apps Projekte für Sie, z. B. Aktivitäten, Aufrufe und Dienste Ihrer App. Sie können jedoch Wenn Sie dieses Standardverhalten mit zusätzlichen Regeln zum Beibehalten anpassen müssen, lesen Sie finden Sie im Abschnitt Festlegen, welcher Code beibehalten werden soll.

Wenn Sie nur die Ressourcen Ihrer App reduzieren möchten, Fahren Sie mit dem Abschnitt zum Reduzieren von Ressourcen fort.

Wenn ein Bibliotheksprojekt verkleinert ist, wird eine App, die auf dieser Bibliothek basiert, umfasst verkleinerte Bibliotheksklassen. Möglicherweise müssen Sie die Regeln für die Bibliotheksbeibehaltung anpassen, wenn Im Bibliotheks-APK fehlen Klassen. Wenn Sie ein Projekt für eine Bibliothek im AAR-Format haben, sind lokale JAR-Dateien, von denen Ihre Bibliothek abhängig ist, nicht in der AAR-Datei kleiner werden.

Festlegen, welcher Code beibehalten werden soll

In den meisten Fällen reicht die Standarddatei von ProGuard (proguard-android- optimize.txt) für R8 aus, um nur den nicht verwendeten Code zu entfernen. Sie können jedoch einige Situationen sind für R8 schwer zu analysieren. den Ihre App wirklich braucht. Beispiele für Fälle, in denen Google Fotos fälschlicherweise Code enthalten:

  • Wenn Ihre App eine Methode über die Java Native Interface (JNI) aufruft
  • Wenn Ihre App zur Laufzeit Code sucht (z. B. bei Reflexion)

Beim Testen deiner App sollten alle Fehler aufgedeckt werden, die durch unangemessene Entfernungen verursacht werden. Sie können aber auch überprüfen, welcher Code einen Bericht über entfernten Code generieren.

Um Fehler zu beheben und R8 zu erzwingen, bestimmten Code beizubehalten, fügen Sie -keep in der ProGuard-Regeldatei. Beispiel:

-keep public class MyClass

Alternativ können Sie den @Keep-Annotation zum Code hinzufügen, behalten möchten. Wenn Sie einem Kurs @Keep hinzufügen, bleibt der gesamte Kurs unverändert. Wenn Sie es einer Methode oder einem Feld hinzufügen, bleiben auch die Methode bzw. das Feld (und sein Name) erhalten. da der Klassenname unverändert bleibt. Diese Anmerkung ist nur verfügbar, wenn die AndroidX-Anmerkungsbibliothek Wenn Sie die ProGuard-Regeldatei hinzufügen, Gradle-Plug-in, wie im Abschnitt zur Aktivieren Sie die Verkleinerung.

Bei der Verwendung der Option -keep sollten Sie eine Reihe von Überlegungen berücksichtigen: für Weitere Informationen zum Anpassen der Regeldatei finden Sie in der ProGuard-Bedienungsanleitung Die Fehlerbehebung beschreibt andere häufige Probleme, die auftreten können, wenn Ihr Code entfernt.

Native Bibliotheken entfernen

Standardmäßig werden native Codebibliotheken in Release-Builds Ihrer App entfernt. Bei diesem Entfernen werden die Symboltabelle und Debugging-Informationen entfernt. in nativen Bibliotheken enthalten, die von Ihrer App verwendet werden. Nativen Code entfernen führen zu erheblichen Größeneinsparungen; aber es ist unmöglich, in der Google Play Console aufgrund fehlender Informationen (z. B. Klassen- und Funktionsnamen).

Native Unterstützung für Abstürze

Die Google Play Console meldet native Abstürze unter Android Vitals Mit ein paar können Sie eine native Debugging-Symboldatei für Ihre App generieren und hochladen. Diese Datei aktiviert symbolisch dargestellte native Absturz-Stacktraces (die Klasse und Funktionsnamen) in Android Vitals, damit du Fehler in deiner App in der Produktionsphase beheben kannst. Diese Schritte variieren je nach Version des Android-Gradle-Plug-ins, das in und die Build-Ausgabe Ihres Projekts.

Android-Gradle-Plug-in ab Version 4.1

Wenn Sie für Ihr Projekt ein Android App Bundle erstellen, können Sie das native Debugging-Symboldatei enthält. Um diese Datei in Release-Builds aufzunehmen, füge den folgende URL in die Datei build.gradle.kts Ihrer App ein:

android.buildTypes.release.ndk.debugSymbolLevel = { SYMBOL_TABLE | FULL }

Wählen Sie die Symbolebene für die Fehlerbehebung aus:

  • Mit SYMBOL_TABLE kannst du Funktionsnamen in den symbolisch dargestellten Stacktraces der Play Console abrufen. Dieses Level unterstützt Tombstones.
  • Du kannst FULL verwenden, um Funktionsnamen, Dateien und Zeilennummern im symbolisch dargestellte Stacktraces.

Wenn in deinem Projekt ein APK erstellt wird, verwende die angezeigte Build-Einstellung build.gradle.kts. um die Datei mit Symbolen zum Debuggen separat zu generieren. Manuell Datei mit Symbolen zum Debuggen von nativem Code hochladen in der Google Play Console. Im Rahmen des Build-Prozesses Das Plug-in gibt diese Datei am folgenden Projektspeicherort aus:

app/build/outputs/native-debug-symbols/variant-name/native-debug-symbols.zip

Android-Gradle-Plug-in Version 4.0 oder niedriger (und andere Build-Systeme)

Im Rahmen des Build-Prozesses speichert das Android-Gradle-Plug-in eine Kopie der in einem Projektverzeichnis enthalten. Diese Verzeichnisstruktur sieht etwa so aus:

app/build/intermediates/cmake/universal/release/obj/
├── armeabi-v7a/
│   ├── libgameengine.so
│   ├── libothercode.so
│   └── libvideocodec.so
├── arm64-v8a/
│   ├── libgameengine.so
│   ├── libothercode.so
│   └── libvideocodec.so
├── x86/
│   ├── libgameengine.so
│   ├── libothercode.so
│   └── libvideocodec.so
└── x86_64/
    ├── libgameengine.so
    ├── libothercode.so
    └── libvideocodec.so
  1. Komprimieren Sie den Inhalt dieses Verzeichnisses:

    cd app/build/intermediates/cmake/universal/release/obj
    zip -r symbols.zip .
    
  2. Manuell laden Sie die Datei symbols.zip hoch in der Google Play Console.

Ressourcen verkleinern

Das Verkleinern von Ressourcen funktioniert nur in Verbindung mit der Codeverkleinerung. Nach dem entfernt den gesamten nicht verwendeten Code, Ressourcen, die die App noch verwendet. Dies gilt insbesondere, wenn Sie Code Bibliotheken, die Ressourcen enthalten, müssen Sie nicht verwendeten Bibliothekscode entfernen, damit der Bibliotheksressourcen werden nicht mehr referenziert und können daher von der Ressource entfernt werden. schrumpft.

Wenn Sie das Verkleinern von Ressourcen aktivieren möchten, legen Sie das Attribut shrinkResources fest in Ihr Build-Skript true (neben minifyEnabled für das Verkleinern von Code). Beispiel:

Kotlin

android {
    ...
    buildTypes {
        getByName("release") {
            isShrinkResources = true
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android.txt"),
                "proguard-rules.pro"
            )
        }
    }
}

Cool

android {
    ...
    buildTypes {
        release {
            shrinkResources true
            minifyEnabled true
            proguardFiles
                getDefaultProguardFile('proguard-android.txt'),
                'proguard-rules.pro'
        }
    }
}

Wenn Sie Ihre App noch nicht mit minifyEnabled für und versuchen Sie dies, bevor Sie shrinkResources aktivieren, da Sie möglicherweise Ihre proguard-rules.pro-Datei bearbeiten müssen, die dynamisch erstellten oder aufgerufenen Klassen oder Methoden beibehalten, beginnen Sie mit dem Entfernen von Ressourcen.

Anpassen, welche Ressourcen beibehalten werden sollen

Wenn Sie bestimmte Ressourcen beibehalten oder verwerfen möchten, erstellen Sie eine XML-Datei mit einem <resources>-Tag in Ihrem Projekt erstellen und jedes Ressource, die im Attribut tools:keep beibehalten werden soll, und jede Ressource, im Attribut tools:discard verwerfen. Beide Attribute akzeptieren einen kommagetrennte Liste von Ressourcennamen. Sie können das Sternchen als ein Platzhalterzeichen.

Beispiel:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"
    tools:discard="@layout/unused2" />

Speichern Sie diese Datei in Ihren Projektressourcen, z. B. unter res/raw/my.package.keep.xml Der Build verpackt diese Datei nicht in Ihr

Hinweis:Verwenden Sie für die Datei keep einen eindeutigen Namen. Wann? verschiedene Bibliotheken verknüpft werden, deren Regeln für Notizen zu Konflikten führen würden Andernfalls kann es zu Problemen mit ignorierten Regeln oder unnötigen Ressourcen.

Die Angabe, welche Ressourcen verworfen werden sollen, mag albern erscheinen, Löschen Sie sie stattdessen. Dies kann jedoch nützlich sein, wenn Sie Build-Varianten verwenden. Für Sie können z. B. alle Ihre Ressourcen im gemeinsamen Projektverzeichnis ablegen, Erstellen Sie dann für jeden Parameter eine eigene my.package.build.variant.keep.xml-Datei Build-Variante, wenn Sie wissen, dass eine bestimmte Ressource im Code verwendet wird (und wird daher nicht vom Verkleinerer entfernt). die für die jeweilige Build-Variante verwendet wird. Es ist auch möglich, dass die Build-Tools eine Ressource fälschlicherweise als erforderlich identifiziert. Dies ist möglich, fügt der Compiler die Ressourcen-IDs inline hinzu. den Unterschied zwischen einer Ressource, auf die eigentlich verwiesen wird, und einem ganzzahligen Wert kennen die zufällig denselben Wert haben.

Strikte Referenzprüfungen aktivieren

Normalerweise kann mit dem Ressourcenverkleinerer genau festgestellt werden, verwendet wird. Wenn Ihr Code jedoch einen Aufruf an Resources.getIdentifier() (oder in einer Ihrer Bibliotheken, dem AppCompat enthält, sucht Ihr Code nach Ressourcennamen, dynamisch generierten Zeichenfolgen. In diesem Fall verhält sich der Ressourcenschrumpfer standardmäßig geschützt und kennzeichnet alle Ressourcen mit einem übereinstimmenden Namensformat als möglicherweise verwendet und kann nicht entfernt werden.

Der folgende Code führt beispielsweise dazu, dass alle Ressourcen mit dem Das Präfix img_, das als verwendet markiert werden soll.

Kotlin

val name = String.format("img_%1d", angle + 1)
val res = resources.getIdentifier(name, "drawable", packageName)

Java

String name = String.format("img_%1d", angle + 1);
res = getResources().getIdentifier(name, "drawable", getPackageName());

Der Ressourcenschrumpfer durchsucht auch alle Stringkonstanten in Ihrem Code sowie verschiedene res/raw/-Ressourcen, die nach Ressourcen suchen, URLs in einem ähnlichen Format wie file:///android_res/drawable//ic_plus_anim_016.png. Wenn Zeichenfolgen wie diese oder andere, die aussehen, als könnten sie zum Erstellen von URLs verwendet werden werden diese nicht entfernt.

Dies sind Beispiele für den sicheren Verkleinerungsmodus, der standardmäßig aktiviert ist. Du kannst die Option „Besser geschützt als Entschuldigung“ aber auch deaktivieren. Umgang mit Ihren Daten damit nur Ressourcen beibehalten werden, die mit Sicherheit verwendet werden. Bis Setzen Sie dazu shrinkMode auf strict in der keep.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:shrinkMode="strict" />

Wenn Sie den strikten Verkleinerungsmodus aktivieren und Ihr Code auch Ressourcen mit dynamisch generierten Zeichenfolgen, wie oben gezeigt, müssen Sie Sie können diese Ressourcen manuell mit dem Attribut tools:keep behalten.

Nicht verwendete alternative Ressourcen entfernen

Der Gradle-Ressourcenverkleinerer entfernt nur Ressourcen, auf die nicht verwiesen wird durch Ihren App-Code. Das bedeutet, dass nicht entfernt wird, alternative Ressourcen für unterschiedliche Gerätekonfigurationen. Bei Bedarf können Sie können Sie mit dem Attribut resConfigs des Android-Gradle-Plug-ins alternative Ressourcendateien entfernen, die Ihre App nicht benötigt.

Wenn Sie z. B. eine Bibliothek mit Sprachressourcen verwenden, wie AppCompat oder Google Play-Dienste, enthält deine App alle übersetzte Sprachzeichenfolgen für die Nachrichten in diesen Bibliotheken der restlichen App in die gleiche Sprache oder nicht übersetzt wird. Wenn Sie nur die Sprachen beizubehalten, die deine App offiziell unterstützt, kannst du diese Sprachen mithilfe des Attributs resConfig. Beliebige Ressourcen für Sprachen, die nicht angegeben sind, werden entfernt.

Das folgende Snippet zeigt, wie Sie Ihre Sprachressourcen auf Englisch und Französisch:

Kotlin

android {
    defaultConfig {
        ...
        resourceConfigurations.addAll(listOf("en", "fr"))
    }
}

Cool

android {
    defaultConfig {
        ...
        resConfigs "en", "fr"
    }
}

Wenn Sie eine App im Android App Bundle-Format veröffentlichen, ist standardmäßig nur Sprachen, die auf dem Gerät eines Nutzers konfiguriert sind, werden bei der Installation der App heruntergeladen. Ebenso können nur Ressourcen, die der Bildschirmdichte des Geräts entsprechen, Bibliotheken, die dem ABI des Geräts entsprechen, sind im Download enthalten. Weitere Informationen finden Sie in den Android-App Bundle-Konfiguration.

Bei älteren Apps, die mit APKs veröffentlicht werden, die vor August 2021 erstellt wurden, können Sie Passen Sie die Bildschirmdichte oder ABI-Ressourcen an, die Sie in Ihr APK aufnehmen möchten, indem Sie Erstellen mehrerer APKs die jeweils auf eine andere Gerätekonfiguration ausgerichtet sind.

Doppelte Ressourcen zusammenführen

Standardmäßig führt Gradle auch Ressourcen mit identischem Namen zusammen, z. B. Drawables mit demselben Namen, die sich in verschiedenen Ressourcenordnern befinden können. Dieses Das Verhalten wird nicht durch das Attribut shrinkResources gesteuert und kann nicht deaktiviert werden, da es notwendig ist, Fehler zu vermeiden, wenn mehrere Ressourcen mit dem Namen übereinstimmen, nach dem Ihr Code sucht.

Ressourcen werden nur zusammengeführt, wenn zwei oder mehr Dateien denselben Ressourcenname, -typ und -qualifizierer. Gradle wählt die Datei aus, die beste Wahl unter den Duplikaten sein (basierend auf der beschriebenen Prioritätsreihenfolge) unten) und übergibt nur diese eine Ressource zur Verteilung in der des finalen Artefakts.

Gradle sucht an den folgenden Speicherorten nach doppelten Ressourcen:

  • Die Hauptressourcen, die mit dem Hauptquellensatz verknüpft sind, sind im Allgemeinen in src/main/res/.
  • Die Varianten-Overlays vom Build-Typ und den Build-Geschmacksrichtungen.
  • Die Abhängigkeiten des Bibliotheksprojekts.

Gradle führt doppelte Ressourcen in der folgenden kaskadierenden Prioritätsreihenfolge zusammen:

Abhängigkeiten → Hauptseite → Build-Flavor → Build-Typ

Wenn beispielsweise eine Ressource doppelt in Ihren Hauptressourcen und einen Build-Flavor enthält, wählt Gradle den im Build-Flavor aus.

Wenn identische Ressourcen im selben Quellsatz vorkommen, kann Gradle nicht zusammengeführt werden. und gibt einen Fehler bei der Zusammenführung von Ressourcen aus. Dies kann passieren, wenn Sie mehrere Quell-Sets im Attribut sourceSet Ihrer build.gradle.kts-Datei, z. B. wenn sowohl src/main/res/ als auch und src/main/res2/ enthalten identische Ressourcen.

Code verschleiern

Der Zweck der Verschleierung besteht darin, die App-Größe zu reduzieren, indem Sie die Namen der die Klassen, Methoden und Felder Ihrer App. Im Folgenden finden Sie ein Beispiel für Verschleierung mit R8:

androidx.appcompat.app.ActionBarDrawerToggle$DelegateProvider -> a.a.a.b:
androidx.appcompat.app.AlertController -> androidx.appcompat.app.AlertController:
    android.content.Context mContext -> a
    int mListItemLayout -> O
    int mViewSpacingRight -> l
    android.widget.Button mButtonNeutral -> w
    int mMultiChoiceItemLayout -> M
    boolean mShowTitle -> P
    int mViewSpacingLeft -> j
    int mButtonPanelSideLayout -> K

Durch die Verschleierung wird zwar kein Code aus Ihrer App entfernt, es gibt jedoch erhebliche Größeneinsparungen ist in Anwendungen mit DEX-Dateien zu sehen, die viele Klassen, Methoden und Felder indexieren. Durch die Verschleierung werden jedoch andere Teile des Codes umbenannt, wie das Prüfen von Stacktraces, sind zusätzliche Tools erforderlich. Um die Leistung Ihrer nach der Verschleierung des Stacktrace finden Sie im Abschnitt einen verschleierten Stacktrace decodieren.

Wenn Ihr Code auf vorhersehbaren Namen für die Methoden Ihrer App basiert, und Klassen. Wenn Sie Reflexion anwenden, sollten Sie diese Signaturen als Einstiegspunkte verwenden und Regeln für die Aufbewahrungsdauer festlegen, wie in den finden Sie im Abschnitt Festlegen, welcher Code beibehalten werden soll. Diese Notizen weisen Sie R8 an, diesen Code nicht nur im endgültigen DEX Ihrer App beizubehalten, die ursprüngliche Bezeichnung.

Verschleierten Stacktrace decodieren

Nach der Verschleierung Ihres Codes mit R8 ist es schwierig, einen Stacktrace zu verstehen weil die Namen von Klassen und Methoden geändert. Um den ursprünglichen Stacktrace zu erhalten, Stacktrace erneut verfolgen.

Codeoptimierung

Um Ihre App noch weiter zu optimieren, untersucht R8 Ihren Code eingehender um nicht verwendeten Code zu entfernen oder Ihren Code nach Möglichkeit so umzuschreiben, weniger ausführlich. Im Folgenden finden Sie einige Beispiele für solche Optimierungen:

  • Wenn Ihr Code nie den else {}-Zweig für eine bestimmte if/else-Anweisung annimmt, R8 entfernt möglicherweise den Code für den Zweig else {}.
  • Wenn Ihr Code eine Methode nur an wenigen Stellen aufruft, entfernt R8 die Methode möglicherweise und es bei einigen Anruf-Websites inline anzeigen.
  • Wenn R8 feststellt, dass eine Klasse nur eine eindeutige Unterklasse hat, und die Klasse wird nicht instanziiert (zum Beispiel eine abstrakte Basisklasse, die nur von einer konkreten Implementierungsklasse), dann kann R8 die beiden Klassen um einen Kurs aus der App zu entfernen.
  • Weitere Informationen finden Sie in der Blogpost zur R8-Optimierung von Jake Wharton

Mit R8 können Sie keine diskreten Optimierungen deaktivieren oder aktivieren oder den einer Optimierung. R8 ignoriert sogar alle ProGuard-Regeln, Standardoptimierungen wie -optimizations und -optimizationpasses. Diese Einschränkung ist wichtig, da R8 und die Aufrechterhaltung eines Standardverhaltens Das Studio-Team kann auftretende Probleme problemlos beheben.

Wenn Sie die Optimierung aktivieren, werden die Stacktraces für Ihre . Durch das Inline-Format werden beispielsweise Stapelframes entfernt. Weitere Informationen finden Sie im Abschnitt retracing um, wie Sie die ursprünglichen Stacktraces abrufen können.

Auswirkungen auf die Laufzeitleistung

Wenn die Verkleinerung, Verschleierung und Optimierung aktiviert sind, verbessert sich R8 Laufzeitleistung des Codes (einschließlich Start- und Frame Time im UI-Thread) um bis zu 30%. Wenn Sie eine dieser Optionen deaktivieren, wird der Satz an Optimierungen drastisch eingeschränkt. R8 verwendet.

Wenn R8 aktiviert ist, sollten Sie auch Start-up-Profile erstellen für eine noch bessere Start-up-Leistung.

Strengere Optimierungen aktivieren

R8 enthält eine Reihe zusätzlicher Optimierungen (als „Full-Modus“ bezeichnet), die anders als bei ProGuard. Diese Optimierungen werden durch Standard seit Android-Gradle-Plug-in Version 8.0.0

Sie können diese zusätzlichen Optimierungen deaktivieren, indem Sie Folgendes in in die gradle.properties-Datei Ihres Projekts ein:

android.enableR8.fullMode=false

Da sich R8 durch die zusätzlichen Optimierungen von ProGuard unterscheidet, Möglicherweise müssen Sie zusätzliche ProGuard-Regeln hinzufügen, um Laufzeiten zu vermeiden. wenn Sie Regeln verwenden, die für ProGuard entwickelt wurden. Nehmen wir zum Beispiel an, -Code verweist über die Java Reflection API auf eine Klasse. Wenn das Gerät nicht verwendet wird "Vollbildmodus" R8 geht davon aus, dass Sie Objekte von auch wenn Ihr Code dies nicht tut, und es wird automatisch behält die Klasse und ihren statischen Initialisierer bei.

Bei Verwendung des Modus „Alle Zugriffe“ geht R8 jedoch nicht von dieser Annahme aus. Wenn R8 dass Ihr Code die Klasse sonst niemals zur Laufzeit verwendet, werden die aus dem finalen DEX Ihrer App. Das heißt, wenn Sie den Kurs und seine statischer Initialisierer müssen Sie eine Keep-Regel in Ihre Regeldatei einfügen,

Wenn beim Verwenden des „Full“-Modus Probleme auftreten, sehen Sie in der R8-FAQ-Seite nach einer möglichen Lösung. Falls Sie das Problem nicht lösen können, Fehler melden

Stacktraces werden zurückgeführt

Von R8 verarbeiteter Code wird auf verschiedene Arten geändert, um Stacktraces zu erstellen sind schwerer zu verstehen, da die Stacktraces nicht genau dem Quellcode entsprechen. Dieses kann bei Änderungen der Zeilennummern der Fall sein, wenn Debugging-Informationen nicht beibehalten werden. Dies kann auf Optimierungen wie Inline- und Outline-Funktionen zurückzuführen sein. Die ist die Verschleierung, bei der sogar die Klassen und Methoden Namen ändern.

Zur Wiederherstellung des ursprünglichen Stacktrace stellt R8 den retrace-Befehlszeilentool. im Paket mit Befehlszeilentools enthalten.

Damit die Stacktraces Ihrer Anwendung zurückgerufen werden können, müssen Sie sicherstellen, Der Build behält genügend Informationen für das Nachverfolgen bei, indem er Folgendes hinzufügt: Regeln für die Datei proguard-rules.pro Ihres Moduls:

-keepattributes LineNumberTable,SourceFile
-renamesourcefileattribute SourceFile

Das Attribut LineNumberTable behält Positionsinformationen bei. damit diese Positionen in Stacktraces gedruckt werden. Das Attribut SourceFile stellt sicher, dass alle potenziellen Laufzeiten die Positionsinformationen tatsächlich ausgeben. Die Anweisung -renamesourcefileattribute legt den Namen der Quelldatei im Stapel fest nur SourceFile. Der tatsächliche Name der ursprünglichen Quelldatei ist nicht erforderlich beim Zurückziehen, da die Zuordnungsdatei die ursprüngliche Quelldatei enthält.

R8 erstellt bei jeder Ausführung eine mapping.txt-Datei, die enthält die Informationen, die erforderlich sind, um Stacktraces dem Original zuzuordnen Stacktraces. Android Studio speichert die Datei im <module-name>/build/outputs/mapping/<build-type>/ -Verzeichnis.

Wenn du deine App bei Google Play veröffentlichst, kannst du die Datei mapping.txt hochladen für jede Version Ihrer App. Bei der Veröffentlichung mit Android App Bundles -Datei wird automatisch in den Inhalt des App-Bundles aufgenommen. Dann Google Play verfolgt eingehende Stacktraces auf Grundlage von vom Nutzer gemeldeten Problemen, können sie sich in der Play Console ansehen. Weitere Informationen findest du in der Hilfe Artikel darüber, wie Sie Offenlegung von Absturz-Stacktraces

Fehlerbehebung mit R8

In diesem Abschnitt werden einige Strategien zur Fehlerbehebung bei der Aktivierung von Verkleinerung, Verschleierung und Optimierung mit R8. Wenn Sie keine Lösung finden zu Ihrem Problem weiter unten, lesen Sie auch R8-FAQ-Seite und Anleitung zur Fehlerbehebung von ProGuard.

Bericht zu entferntem (oder beibehaltenem) Code erstellen

Für die Behebung bestimmter R8-Probleme kann es hilfreich sein, einen Bericht den gesamten Code, den R8 aus Ihrer App entfernt hat. Für jedes Modul, für das Sie Fügen Sie -printusage <output-dir>/usage.txt zu Ihrem benutzerdefinierten Bericht hinzu, um diesen Bericht zu erstellen. Regeldatei. Wenn Sie R8 aktivieren und Ihre App erstellen, gibt R8 einen Bericht mit dem Pfad und Dateinamen, den Sie angegeben haben. Bericht über entfernten Code sieht etwa so aus:

androidx.drawerlayout.R$attr
androidx.vectordrawable.R
androidx.appcompat.app.AppCompatDelegateImpl
    public void setSupportActionBar(androidx.appcompat.widget.Toolbar)
    public boolean hasWindowFeature(int)
    public void setHandleNativeActionModesEnabled(boolean)
    android.view.ViewGroup getSubDecor()
    public void setLocalNightMode(int)
    final androidx.appcompat.app.AppCompatDelegateImpl$AutoNightModeManager getAutoNightModeManager()
    public final androidx.appcompat.app.ActionBarDrawerToggle$Delegate getDrawerToggleDelegate()
    private static final boolean DEBUG
    private static final java.lang.String KEY_LOCAL_NIGHT_MODE
    static final java.lang.String EXCEPTION_HANDLER_MESSAGE_SUFFIX
...

Wenn Sie stattdessen einen Bericht zu den Einstiegspunkten sehen möchten, die R8 die Regeln für die Notizen Ihres Projekts einzuhalten , fügen Sie -printseeds <output-dir>/seeds.txt in Ihr benutzerdefinierte Regeln. Wenn Sie R8 aktivieren und Ihre App erstellen, gibt R8 aus. einen Bericht mit dem Pfad und Dateinamen, den Sie angegeben haben. Bericht über einen aufbewahrten Eintrag sehen in etwa so aus:

com.example.myapplication.MainActivity
androidx.appcompat.R$layout: int abc_action_menu_item_layout
androidx.appcompat.R$attr: int activityChooserViewStyle
androidx.appcompat.R$styleable: int MenuItem_android_id
androidx.appcompat.R$styleable: int[] CoordinatorLayout_Layout
androidx.lifecycle.FullLifecycleObserverAdapter
...

Fehler beim Verkleinern von Ressourcen beheben

Wenn Sie Ressourcen verkleinern, wird der Build zeigt eine Zusammenfassung der Ressourcen, die aus der App entfernt werden. Sie müssen zuerst auf Ansicht umschalten auf der linken Seite des Fensters, um eine detaillierte Textausgabe von Gradle anzuzeigen.) Beispiel:

:android:shrinkDebugResources
Removed unused resources: Binary resource data reduced from 2570KB to 1711KB: Removed 33%
:android:validateDebugSigning

Gradle erstellt außerdem eine Diagnosedatei mit dem Namen resources.txt in <module-name>/build/outputs/mapping/release/ (gleiche Ordner als Ausgabedateien von ProGuard. Diese Datei enthält Angaben dazu, Ressourcen auf andere Ressourcen verweisen und welche Ressourcen verwendet werden oder entfernt.

Um beispielsweise herauszufinden, warum @drawable/ic_plus_anim_016 noch in der App, öffnen Sie die Datei resources.txt und suchen Sie danach Dateinamen. Möglicherweise wird von einer anderen Ressource darauf verwiesen, folgt:

16:25:48.005 [QUIET] [system.out] @drawable/add_schedule_fab_icon_anim : reachable=true
16:25:48.009 [QUIET] [system.out]     @drawable/ic_plus_anim_016

Jetzt wissen Sie, warum @drawable/add_schedule_fab_icon_anim ist erreichbar. Wenn Sie eine höhere Suche durchführen, finden Sie, dass diese Ressource unter „Die erreichbaren Root-Ressourcen sind:“. Es gibt also eine Codereferenz, an add_schedule_fab_icon_anim (d. h., seine R.drawable-ID war Code im erreichbaren Code).

Wenn Sie keine strikte Prüfung verwenden, können Ressourcen-IDs als erreichbar gekennzeichnet werden. ob es Zeichenfolgenkonstanten gibt, die so aussehen, als könnten sie zur Konstruktion Ressourcennamen für dynamisch geladene Ressourcen. Wenn Sie in diesem Fall der Build-Ausgabe für den Ressourcennamen können Sie eine Meldung wie die folgende sehen:

10:32:50.590 [QUIET] [system.out] Marking drawable:ic_plus_anim_016:2130837506
    used because it format-string matches string pool constant ic_plus_anim_%1$d.

Wenn Sie einen dieser Zeichenfolgen sehen und sicher sind, dass der String nicht zum dynamischen Laden der angegebenen Ressource verwendet wird, können Sie den tools:discard, um das Build-System zu informieren, dass es entfernt werden soll. enthalten, wie im Abschnitt zum Anpassen der beizubehaltenden Ressourcen beschrieben.