Wenn Ihre App ein minSdk von API 20 oder niedriger hat und Ihre App und die Bibliotheken, auf die sie verweist, mehr als 65.536 Methoden enthalten, tritt der folgende Build-Fehler auf, der darauf hinweist, dass Ihre App das Limit der Android-Build-Architektur erreicht hat:
trouble writing output: Too many field references: 131000; max is 65536. You may try using --multi-dex option.
Ältere Versionen des Build-Systems melden einen anderen Fehler, der auf dasselbe Problem hinweist:
Conversion to Dalvik format failed: Unable to execute dex: method ID not in [0, 0xffff]: 65536
Diese Fehlerbedingungen weisen eine gemeinsame Nummer auf: 65536. Diese Zahl entspricht der Gesamtzahl der Verweise, die vom Code innerhalb einer einzelnen Dalvik Executable-Bytecodedatei (DEX) aufgerufen werden können. Auf dieser Seite wird beschrieben, wie Sie diese Einschränkung umgehen können, indem Sie eine App-Konfiguration namens Multidex aktivieren, mit der Ihre App mehrere DEX-Dateien erstellen und lesen kann.
Informationen zum Referenzlimit von 64.000
Android-App-Dateien (APK) enthalten ausführbare Bytecode-Dateien in Form von Dalvik Executable-Dateien (DEX). Diese enthalten den kompilierten Code, der zum Ausführen der App verwendet wird. Die Dalvik Executable-Spezifikation beschränkt die Gesamtzahl der Methoden,auf die in einer einzelnen DEX-Datei verwiesen werden kann, auf 65.536. Dazu gehören Android-Framework-Methoden, Bibliotheksmethoden und Methoden in Ihrem eigenen Code.
Im Kontext der Informatik bezeichnet der Begriff Kilo oder K 1.024 (oder 2^10). Da 65.536 gleich 64 × 1.024 ist, wird dieses Limit als _64K-Referenzlimit_ bezeichnet.Multidex-Unterstützung vor Android 5.0
Plattformversionen vor Android 5.0 (API-Level 21) verwenden die Dalvik-Laufzeit, um App-Code auszuführen. Standardmäßig beschränkt Dalvik Apps auf eine einzige classes.dex-Bytecodedatei pro APK. Um diese Einschränkung zu umgehen, fügen Sie die Multidex-Bibliothek der Datei build.gradle oder build.gradle.kts auf Modulebene hinzu:
Groovy
dependencies { def multidex_version = "2.0.1" implementation "androidx.multidex:multidex:$multidex_version" }
Kotlin
dependencies { val multidex_version = "2.0.1" implementation("androidx.multidex:multidex:$multidex_version") }
Diese Bibliothek wird Teil der primären DEX-Datei Ihrer App und verwaltet dann den Zugriff auf die zusätzlichen DEX-Dateien und den darin enthaltenen Code. Die aktuellen Versionen dieser Bibliothek finden Sie unter Multidex-Versionen.
Weitere Informationen finden Sie im Abschnitt App für Multidex konfigurieren.Multidex-Unterstützung für Android 5.0 und höher
Android 5.0 (API-Level 21) und höher verwendet eine Laufzeit namens ART, die das Laden mehrerer DEX-Dateien aus APK-Dateien nativ unterstützt. ART führt bei der Installation der App eine Vorkompilierung durch. Dabei werden classesN.dex-Dateien gesucht und in einer einzigen OAT-Datei kompiliert, die vom Android-Gerät ausgeführt werden kann. Wenn deine minSdkVersion 21 oder höher ist, ist Multidex standardmäßig aktiviert und du benötigst die Multidex-Bibliothek nicht.
Weitere Informationen zur Android 5.0-Laufzeit findest du unter Android Runtime (ART) und Dalvik.
Hinweis:Wenn Sie Ihre App mit Android Studio ausführen, wird der Build für die Zielgeräte optimiert, auf denen Sie die App bereitstellen. Dazu gehört auch die Aktivierung von Multidex, wenn auf den Zielgeräten Android 5.0 oder höher ausgeführt wird. Da diese Optimierung nur angewendet wird, wenn Sie Ihre App mit Android Studio bereitstellen, müssen Sie möglicherweise trotzdem Ihren Release-Build für Multidex konfigurieren, um das 64K-Limit zu vermeiden.
64K-Limit vermeiden
Bevor du deine App so konfigurierst, dass mehr als 64.000 Methodenreferenzen verwendet werden können, solltest du die Gesamtzahl der Referenzen reduzieren, die von deinem App-Code aufgerufen werden. Dazu gehören auch Methoden, die durch deinen App-Code oder enthaltene Bibliotheken definiert werden.
Mit den folgenden Strategien können Sie vermeiden, dass das DEX-Referenzlimit erreicht wird:
- Direkte und transitive Abhängigkeiten Ihrer App prüfen
- Überlegen Sie, ob der Wert einer großen Bibliotheksabhängigkeit, die Sie in Ihre App einbinden, die Menge des Codes, der der App hinzugefügt wird, überwiegt. Ein gängiges, aber problematisches Muster ist das Einbinden einer sehr großen Bibliothek, weil einige Hilfsmethoden nützlich waren. Wenn Sie die Abhängigkeiten Ihres App-Codes reduzieren, können Sie das DEX-Referenzlimit oft vermeiden.
- Nicht verwendeten Code mit R8 entfernen
- Aktivieren Sie die Codekomprimierung, um R8 für Ihre Release-Builds auszuführen. Aktivieren Sie die Code-Verkleinerung, um sicherzustellen, dass Sie keinen ungenutzten Code mit Ihren APKs versenden. Wenn die Codekomprimierung richtig konfiguriert ist, können auch nicht verwendeter Code und nicht verwendete Ressourcen aus Ihren Abhängigkeiten entfernt werden.
Mit diesen Techniken kannst du die Gesamtgröße deines APKs verringern und Multidex in deiner App vermeiden.
App für Multidex konfigurieren
Hinweis: WennminSdkVersion auf 21 oder höher festgelegt ist, ist Multidex standardmäßig aktiviert und Sie benötigen die Multidex-Bibliothek nicht.
Wenn Ihr minSdkVersion auf 20 oder niedriger festgelegt ist, müssen Sie die Multidex-Bibliothek verwenden und die folgenden Änderungen an Ihrem Anwendungsprojekt vornehmen:
-
Ändern Sie die Datei
build.gradleauf Modulebene, um Multidex zu aktivieren, und fügen Sie die Multidex-Bibliothek als Abhängigkeit hinzu, wie hier gezeigt:Groovy
android { defaultConfig { ... minSdkVersion 15 targetSdkVersion 36 multiDexEnabled true } ... } dependencies { implementation "androidx.multidex:multidex:2.0.1" }
Kotlin
android { defaultConfig { ... minSdk = 15 targetSdk = 36 multiDexEnabled = true } ... } dependencies { implementation("androidx.multidex:multidex:2.0.1") }
- Je nachdem, ob Sie die Klasse
Applicationüberschreiben, führen Sie einen der folgenden Schritte aus:Wenn Sie die Klasse
Applicationnicht überschreiben, bearbeiten Sie die Manifestdatei, umandroid:nameim Tag<application>so festzulegen:<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapp"> <application android:name="androidx.multidex.MultiDexApplication" > ... </application> </manifest>
Wenn Sie die Klasse
Applicationüberschreiben, ändern Sie sie so, dass sieMultiDexApplicationerweitert:Kotlin
class MyApplication : MultiDexApplication() {...}
Java
public class MyApplication extends MultiDexApplication { ... }
Wenn Sie die Klasse
Applicationüberschreiben, es aber nicht möglich ist, die Basisklasse zu ändern, überschreiben Sie stattdessen die MethodeattachBaseContext()und rufen SieMultiDex.install(this)auf, um Multidex zu aktivieren:Kotlin
class MyApplication : SomeOtherApplication() { override fun attachBaseContext(base: Context) { super.attachBaseContext(base) MultiDex.install(this) } }
Java
public class MyApplication extends SomeOtherApplication { @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); MultiDex.install(this); } }
Achtung:Führe
MultiDex.install()oder anderen Code nicht über Reflection oder JNI aus, bevorMultiDex.install()abgeschlossen ist. Multidex-Tracing folgt diesen Aufrufen nicht, was aufgrund einer fehlerhaften Klassenpartition zwischen DEX-Dateien zuClassNotFoundException- oder Verifizierungsfehlern führt.
Wenn Sie jetzt Ihre App erstellen, erstellen die Android-Build-Tools eine primäre DEX-Datei (classes.dex) und bei Bedarf unterstützende DEX-Dateien (classes2.dex, classes3.dex usw.).
Das Build-System verpackt dann alle DEX-Dateien in Ihr APK.
Anstatt zur Laufzeit nur in der Hauptdatei classes.dex zu suchen, verwenden die Multidex APIs einen speziellen ClassLoader, um alle verfügbaren DEX-Dateien nach Ihren Methoden zu durchsuchen.
Einschränkungen der Multidex-Bibliothek
Die Multidex-Bibliothek hat einige bekannte Einschränkungen. Beachten Sie beim Einbinden der Bibliothek in die Build-Konfiguration Ihrer App Folgendes:
- Die Installation von DEX-Dateien während des Starts auf der Datenpartition eines Geräts ist komplex und kann zu ANR-Fehlern (App antwortet nicht) führen, wenn die sekundären DEX-Dateien groß sind. Um dieses Problem zu vermeiden, aktivieren Sie die Codekomprimierung, um die Größe von DEX-Dateien zu minimieren und nicht verwendete Codeabschnitte zu entfernen.
- Bei Versionen vor Android 5.0 (API-Level 21) reicht die Verwendung von Multidex nicht aus, um das LinearAlloc-Limit zu umgehen (Problem 37008143). Dieses Limit wurde in Android 4.0 (API-Level 14) erhöht, aber das Problem wurde dadurch nicht vollständig behoben.
Bei Versionen vor Android 4.0 wird das LinearAlloc-Limit möglicherweise vor dem DEX-Indexlimit erreicht. Wenn Sie also auf API-Level unter 14 abzielen, sollten Sie Ihre App auf diesen Versionen der Plattform gründlich testen, da es beim Start oder beim Laden bestimmter Klassengruppen zu Problemen kommen kann.
Durch die Codekomprimierung können diese Probleme reduziert oder möglicherweise beseitigt werden.
In der primären DEX-Datei erforderliche Klassen deklarieren
Beim Erstellen der einzelnen DEX-Dateien für eine Multidex-App treffen die Build-Tools komplexe Entscheidungen, um zu bestimmen, welche Klassen in der primären DEX-Datei benötigt werden, damit Ihre App erfolgreich gestartet werden kann. Wenn eine Klasse, die beim Start erforderlich ist, nicht in der primären DEX-Datei enthalten ist, stürzt die App mit dem Fehler java.lang.NoClassDefFoundError ab.
Die Build-Tools erkennen die Codepfade für Code, auf den direkt über Ihren Anwendungscode zugegriffen wird. Dieses Problem kann jedoch auftreten, wenn die Codepfade weniger sichtbar sind, z. B. wenn eine von Ihnen verwendete Bibliothek komplexe Abhängigkeiten hat. Wenn der Code beispielsweise Introspektion oder den Aufruf von Java-Methoden aus nativem Code verwendet, werden diese Klassen möglicherweise nicht als erforderlich in der primären DEX-Datei erkannt.
Wenn du java.lang.NoClassDefFoundError erhältst, musst du die zusätzlichen Klassen, die in der primären DEX-Datei erforderlich sind, manuell angeben. Deklariere sie dazu mit dem Attribut multiDexKeepProguard in deinem Build-Typ. Wenn eine Klasse in der Datei multiDexKeepProguard übereinstimmt, wird sie der primären DEX-Datei hinzugefügt.
multiDexKeepProguard-Attribut
Die Datei multiDexKeepProguard verwendet dasselbe Format wie ProGuard und unterstützt die gesamte ProGuard-Grammatik. Weitere Informationen zum Anpassen der in Ihrer App beibehaltenen Elemente finden Sie unter Anpassen, welcher Code beibehalten werden soll.
Die in multiDexKeepProguard angegebene Datei sollte -keep-Optionen in einer gültigen ProGuard-Syntax enthalten. Beispiel: -keep com.example.MyClass.class Sie können eine Datei mit dem Namen multidex-config.pro erstellen, die so aussieht:
-keep class com.example.MyClass -keep class com.example.MyClassToo
Wenn Sie alle Klassen in einem Paket angeben möchten, sieht die Datei so aus:
-keep class com.example.** { *; } // All classes in the com.example package
Anschließend können Sie diese Datei für einen Build-Typ deklarieren:
Groovy
android { buildTypes { release { multiDexKeepProguard file('multidex-config.pro') ... } } }
Kotlin
android { buildTypes { getByName("release") { multiDexKeepProguard = file("multidex-config.pro") ... } } }
Multidex in Entwicklungs-Builds optimieren
Eine Multidex-Konfiguration erfordert eine deutlich längere Build-Verarbeitungszeit, da das Build-System komplexe Entscheidungen darüber treffen muss, welche Klassen in die primäre DEX-Datei und welche in sekundäre DEX-Dateien aufgenommen werden müssen. Das bedeutet, dass inkrementelle Builds mit Multidex in der Regel länger dauern und den Entwicklungsprozess verlangsamen können.
Um längere inkrementelle Build-Zeiten zu vermeiden, können Sie Predexing verwenden, um die Multidex-Ausgabe zwischen Builds wiederzuverwenden.
Das Predexing basiert auf einem ART-Format, das nur unter Android 5.0 (API-Level 21) und höher verfügbar ist. Wenn Sie Android Studio verwenden, nutzt die IDE automatisch Predexing, wenn Sie Ihre App auf einem Gerät mit Android 5.0 (API-Level 21) oder höher bereitstellen.
Wenn Sie Gradle-Builds über die Befehlszeile ausführen, müssen Sie minSdkVersion auf 21 oder höher festlegen, um Predexing zu aktivieren.
minSdkVersion, wie hier gezeigt:
Groovy
android { defaultConfig { ... multiDexEnabled true // The default minimum API level you want to support. minSdkVersion 15 } productFlavors { // Includes settings you want to keep only while developing your app. dev { // Enables pre-dexing for command-line builds. When using // Android Studio 2.3 or higher, the IDE enables pre-dexing // when deploying your app to a device running Android 5.0 // (API level 21) or higher, regardless of minSdkVersion. minSdkVersion 21 } prod { // If you've configured the defaultConfig block for the production version of // your app, you can leave this block empty and Gradle uses configurations in // the defaultConfig block instead. You still need to include this flavor. // Otherwise, all variants use the "dev" flavor configurations. } } buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { implementation "androidx.multidex:multidex:2.0.1" }
Kotlin
android { defaultConfig { ... multiDexEnabled = true // The default minimum API level you want to support. minSdk = 15 } productFlavors { // Includes settings you want to keep only while developing your app. create("dev") { // Enables pre-dexing for command-line builds. When using // Android Studio 2.3 or higher, the IDE enables pre-dexing // when deploying your app to a device running Android 5.0 // (API level 21) or higher, regardless of minSdkVersion. minSdk = 21 } create("prod") { // If you've configured the defaultConfig block for the production version of // your app, you can leave this block empty and Gradle uses configurations in // the defaultConfig block instead. You still need to include this flavor. // Otherwise, all variants use the "dev" flavor configurations. } } buildTypes { getByName("release") { isMinifyEnabled = true proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro") } } } dependencies { implementation("androidx.multidex:multidex:2.0.1") }
Weitere Strategien zur Verbesserung der Build-Geschwindigkeit in Android Studio oder über die Befehlszeile finden Sie unter Build-Geschwindigkeit optimieren. Weitere Informationen zur Verwendung von Build-Varianten finden Sie unter Build-Varianten konfigurieren.
Tipp:Wenn Sie verschiedene Build-Varianten für unterschiedliche Multidex-Anforderungen haben, können Sie für jede Variante eine andere Manifestdatei angeben, sodass nur die Datei für API-Level 20 und niedriger den Tag-Namen <application> ändert. Du kannst auch für jede Variante eine andere Application-Unterklasse erstellen, sodass nur die Unterklasse für API-Level 20 und niedriger die MultiDexApplication-Klasse erweitert oder MultiDex.install(this) aufruft.
Multidex-Apps testen
Wenn Sie Instrumentierungstests für Multidex-Apps schreiben, ist keine zusätzliche Konfiguration erforderlich, wenn Sie eine
MonitoringInstrumentation- oder
AndroidJUnitRunner-Instrumentierung verwenden. Wenn Sie eine andere Instrumentation verwenden, müssen Sie die Methode onCreate() mit dem folgenden Code überschreiben:
Kotlin
fun onCreate(arguments: Bundle) { MultiDex.install(targetContext) super.onCreate(arguments) ... }
Java
public void onCreate(Bundle arguments) { MultiDex.install(getTargetContext()); super.onCreate(arguments); ... }