Wenn Ihre App eine minSdk
von API 20 oder niedriger hat und Ihre App und die Bibliotheken, auf die sie verweist, mehr als 65.536 Methoden enthalten, wird der folgende Buildfehler angezeigt, 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
Für diese Fehler wird die gemeinsame Zahl 65.536 angezeigt. Diese Zahl entspricht der Gesamtzahl der Verweise, die vom Code in einer einzelnen DEX-Bytecodedatei (Dalvik Executable) aufgerufen werden können. Auf dieser Seite wird erläutert, wie Sie diese Einschränkung umgehen, indem Sie die App-Konfiguration MultiDex aktivieren. Dadurch kann Ihre App mehrere DEX-Dateien erstellen und lesen.
64K-Referenzlimit
Android-App-Dateien (APK) enthalten ausführbare Bytecodedateien in Form von Dalvik-Ausführbaren (DEX), die den kompilierten Code enthalten, mit dem Ihre App ausgeführt wird. Die Dalvik-Ausführbare-Spezifikation begrenzt die Gesamtzahl der Methoden, auf die in einer einzelnen DEX-Datei verwiesen werden kann, auf 65.536 – einschließlich Android-Framework-Methoden, Bibliotheksmethoden und Methoden in Ihrem eigenen Code.
Im Kontext der Informatik steht der Begriff Kilo oder K für 1.024 (oder 2 hoch 10). Da 65.536 64 × 1.024 entspricht, wird dieses Limit als 64K-Referenzlimit bezeichnet.Multidex-Unterstützung vor Android 5.0
Bei Versionen der Plattform vor Android 5.0 (API-Level 21) wird die Dalvik-Laufzeit zum Ausführen von App-Code verwendet. Standardmäßig beschränkt Dalvik Apps auf eine einzelne 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 zum Konfigurieren Ihrer App für Multidex.Multidex-Unterstützung für Android 5.0 und höher
Android 5.0 (API-Ebene 21) und höher verwenden eine Laufzeit namens ART, die das Laden mehrerer DEX-Dateien aus APK-Dateien nativ unterstützt. ART führt bei der App-Installation eine Vorkompilierung durch, sucht nach classesN.dex
-Dateien und kompiliert sie zu einer einzelnen OAT-Datei, die vom Android-Gerät ausgeführt wird. Wenn Ihre minSdkVersion
21 oder höher ist, ist MultiDex standardmäßig aktiviert und Sie benötigen keine MultiDex-Bibliothek.
Weitere Informationen zur Laufzeit von Android 5.0 finden Sie 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 sie 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 bei der Bereitstellung Ihrer App mit Android Studio angewendet wird, müssen Sie Ihren Release-Build möglicherweise für Multidex konfigurieren, um das Limit von 64 KB zu vermeiden.
64 KB-Limit vermeiden
Bevor Sie Ihre App so konfigurieren, dass die Verwendung von 64.000 Methodenreferenzen oder mehr möglich ist, sollten Sie die Gesamtzahl der vom App-Code aufgerufenen Referenzen reduzieren, einschließlich der Methoden, die im App-Code oder in den enthaltenen Bibliotheken definiert sind.
Mit den folgenden Strategien können Sie das DEX-Referenzlimit vermeiden:
- Direkte und transitive Abhängigkeiten Ihrer App prüfen
- Überlegen Sie, ob der Wert einer großen Bibliotheksabhängigkeit, die Sie in Ihre App aufnehmen, den Codeumfang überwiegt, der der App hinzugefügt wird. Ein häufiges, aber problematisches Muster ist die Aufnahme einer sehr großen Bibliothek, weil einige Dienstmethoden nützlich waren. Wenn Sie die Abhängigkeiten Ihres App-Codes reduzieren, können Sie das DEX-Referenzlimit häufig vermeiden.
- Ungenutzten Code mit R8 entfernen
- Aktivieren Sie die Codekomprimierung, um R8 für Ihre Release-Builds auszuführen. Aktivieren Sie die Komprimierung, damit nicht verwendeter Code nicht mit Ihren APKs ausgeliefert wird. Wenn die Codekomprimierung richtig konfiguriert ist, können auch nicht verwendete Codeelemente und Ressourcen aus Ihren Abhängigkeiten entfernt werden.
Mit diesen Techniken können Sie die Gesamtgröße Ihres APKs verringern und die Notwendigkeit von Multidex in Ihrer 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 minSdkVersion
auf 20 oder niedriger festgelegt ist, müssen Sie die Multidex-Bibliothek verwenden und die folgenden Änderungen an Ihrem App-Projekt vornehmen:
-
Ändern Sie die
build.gradle
-Datei auf Modulebene, um MultiDex zu aktivieren und die MultiDex-Bibliothek als Abhängigkeit hinzuzufügen, wie hier gezeigt:Groovy
android { defaultConfig { ... minSdkVersion 15 targetSdkVersion 33 multiDexEnabled true } ... } dependencies { implementation "androidx.multidex:multidex:2.0.1" }
Kotlin
android { defaultConfig { ... minSdk = 15 targetSdk = 33 multiDexEnabled = true } ... } dependencies { implementation("androidx.multidex:multidex:2.0.1") }
- Führen Sie je nachdem, ob Sie die Klasse
Application
überschreiben, einen der folgenden Schritte aus:Wenn Sie die
Application
-Klasse nicht überschreiben, bearbeiten Sie die Manifestdatei so, dassandroid:name
im<application>
-Tag wie unten angegeben festgelegt ist:<?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 sieMultiDexApplication
erweitert:Kotlin
class MyApplication : MultiDexApplication() {...}
Java
public class MyApplication extends MultiDexApplication { ... }
Wenn Sie die
Application
-Klasse überschreiben, die Basisklasse aber nicht geändert werden kann, ü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ühren Sie
MultiDex.install()
oder anderen Code nicht über Reflection oder JNI aus, bevorMultiDex.install()
abgeschlossen ist. Bei der Multidex-Analyse werden diese Aufrufe nicht berücksichtigt, was aufgrund einer fehlerhaften Klassenpartition zwischen DEX-Dateien zuClassNotFoundException
- oder Überprüfungsfehlern führt.
Wenn Sie Ihre App jetzt erstellen, erstellen die Android-Build-Tools eine primäre DEX-Datei (classes.dex
) und nach Bedarf unterstützende DEX-Dateien (classes2.dex
, classes3.dex
usw.).
Das Build-System verpackt dann alle DEX-Dateien in Ihrem APK.
Anstatt nur in der Hauptdatei classes.dex
zu suchen, verwenden die Multidex-APIs zur Laufzeit einen speziellen Klassenloader, um in allen verfügbaren DEX-Dateien nach Ihren Methoden zu suchen.
Einschränkungen der Multidex-Bibliothek
Die Multidex-Bibliothek hat einige bekannte Einschränkungen. Beachten Sie Folgendes, wenn Sie die Bibliothek in die Build-Konfiguration Ihrer App einbinden:
- Die Installation von DEX-Dateien beim Starten 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 der DEX-Dateien zu minimieren und nicht verwendete Codeteile zu entfernen.
- Bei der Ausführung auf Versionen vor Android 5.0 (API-Level 21) reicht die Verwendung von Multidex nicht aus, um das Limit für linearalloc zu umgehen (Problem 37008143). Dieses Limit wurde in Android 4.0 (API-Level 14) erhöht, das Problem wurde dadurch aber nicht vollständig behoben.
Bei Versionen niedriger als Android 4.0 wird möglicherweise das Limit für linearalloc erreicht, bevor das Limit für den DEX-Index erreicht wird. Wenn Sie also API-Ebenen unter 14 ansteuern, sollten Sie diese Versionen der Plattform gründlich testen, da bei Ihrer App beim Start oder beim Laden bestimmter Klassengruppen Probleme auftreten können.
Durch Code-Shrinking können diese Probleme reduziert oder möglicherweise beseitigt werden.
Klassen in der primären DEX-Datei deklarieren, die erforderlich sind
Beim Erstellen jeder DEX-Datei für eine Multidex-App treffen die Build-Tools komplexe Entscheidungen, um festzustellen, welche Klassen in der primären DEX-Datei benötigt werden, damit Ihre App gestartet werden kann. Wenn eine Klasse, die zum Starten erforderlich ist, nicht in der primären DEX-Datei enthalten ist, stürzt Ihre App mit dem Fehler java.lang.NoClassDefFoundError
ab.
Die Build-Tools erkennen die Codepfade für Code, auf den direkt über Ihren App-Code 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 die Inspektion oder Aufrufe von Java-Methoden aus nativem Code verwendet, werden diese Klassen möglicherweise nicht als erforderlich in der primären DEX-Datei erkannt.
Wenn Sie java.lang.NoClassDefFoundError
erhalten, müssen Sie die zusätzlichen Klassen, die in der primären DEX-Datei erforderlich sind, manuell angeben. Deklarieren Sie sie dazu mit dem Attribut multiDexKeepProguard
in Ihrem Build-Typ. Wenn eine Klasse in der multiDexKeepProguard
-Datei gefunden wird, wird sie der primären DEX-Datei hinzugefügt.
Eigenschaft „multiDexKeepProguard“
Die Datei multiDexKeepProguard
verwendet dasselbe Format wie ProGuard und unterstützt die gesamte ProGuard-Grammatik. Weitere Informationen dazu, wie Sie anpassen können, was in Ihrer App beibehalten werden soll, finden Sie unter Code beibehalten.
Die in multiDexKeepProguard
angegebene Datei sollte -keep
-Optionen in einer gültigen ProGuard-Syntax enthalten. Beispiel: -keep com.example.MyClass.class
. Erstellen Sie eine Datei namens multidex-config.pro
, 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 Buildtyp deklarieren:
Groovy
android { buildTypes { release { multiDexKeepProguard file('multidex-config.pro') ... } } }
Kotlin
android { buildTypes { getByName("release") { multiDexKeepProguard = file("multidex-config.pro") ... } } }
Multidex in Entwicklungsbuilds optimieren
Eine Multidex-Konfiguration erfordert eine deutlich längere Build-Verarbeitungszeit, da das Build-System komplexe Entscheidungen darüber treffen muss, welche Klassen in der primären DEX-Datei enthalten sein müssen und welche Klassen in sekundären DEX-Dateien enthalten sein können. Das bedeutet, dass inkrementelle Builds mit Multidex in der Regel länger dauern und den Entwicklungsvorgang möglicherweise verlangsamen können.
Um längere inkrementelle Buildzeiten zu vermeiden, können Sie Pre-Dexing verwenden, um die Multidex-Ausgabe zwischen den Builds wiederzuverwenden.
Das Vorab-Dexing verwendet ein ART-Format, das nur auf Android 5.0 (API-Level 21) und höher verfügbar ist. Wenn Sie Android Studio verwenden, wird in der IDE automatisch die Vorab-Dex-Optimierung verwendet, wenn Sie Ihre App auf einem Gerät mit Android 5.0 (API-Level 21) oder höher bereitstellen.
Wenn Sie Gradle-Builds jedoch über die Befehlszeile ausführen, müssen Sie minSdkVersion
auf 21 oder höher setzen, um das Vorab-Dexing zu aktivieren.
minSdkVersion
, wie hier dargestellt:
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 Buildvarianten finden Sie unter Buildvarianten konfigurieren.
Tipp:Wenn Sie verschiedene Buildvarianten für unterschiedliche Multidex-Anforderungen haben, können Sie für jede Variante eine andere Manifestdatei angeben, sodass sich der Name des <application>
-Tags nur in der Datei für API-Level 20 und niedriger ändert. Sie können auch für jede Variante eine andere Application
-Unterklasse erstellen, sodass nur die Unterklasse für API-Ebene 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 du eine andere Instrumentation
verwendest, musst du die onCreate()
-Methode 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); ... }