Wenn Ihre App eine minSdk
von API 20 oder niedriger hat und Ihre App und die Bibliotheken, auf die sie verweist, 65.536 Methoden überschreiten, tritt der folgende Build-Fehler auf. Dieser weist darauf hin, 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 Fehlerbedingungen wird eine allgemeine Zahl angezeigt: 65536. Diese Zahl stellt die Gesamtzahl der Verweise dar, die vom Code in einer einzelnen Dalvik Executable-Datei (DEX) aufgerufen werden können. Auf dieser Seite wird erläutert, wie Sie diese Einschränkung umgehen, indem Sie die als Multidex bezeichnete Anwendungskonfiguration aktivieren, mit der Ihre Anwendung mehrere DEX-Dateien erstellen und lesen kann.
Referenzlimit von 64.000
Android-App-Dateien (APK-Dateien) enthalten ausführbare Bytecode-Dateien in Form von Dalvik Executable (DEX)-Dateien, die den kompilierten Code zum Ausführen Ihrer App enthalten. Die Dalvik Executable-Spezifikation beschränkt 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 Ihrer App.
Im Kontext der Informatik steht der Begriff Kilo oder K für 1.024 (oder 2^10). Da 65.536 64 × 1024 entspricht, wird dieses Limit als _64K-Referenzlimit_ bezeichnet.Unterstützung von Multidex vor Android 5.0
Plattformversionen vor Android 5.0 (API-Level 21) verwenden die Dalvik-Laufzeit zum Ausführen von App-Code. Standardmäßig beschränkt Dalvik Apps auf eine einzelne classes.dex
-Bytecodedatei pro APK. Fügen Sie die Multidex-Bibliothek in die Datei build.gradle
oder build.gradle.kts
auf Modulebene ein, um diese Einschränkung zu umgehen:
Cool
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 Anwendung und verwaltet dann den Zugriff auf die zusätzlichen DEX-Dateien und den darin enthaltenen Code. Die aktuellen Versionen für diese 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 mit dem Namen ART, die nativ das Laden mehrerer DEX-Dateien aus APK-Dateien unterstützt. ART führt bei der App-Installation eine Vorkompilierung durch, sucht nach classesN.dex
-Dateien und kompiliert sie in eine einzige OAT-Datei, um sie auf dem Android-Gerät auszuführen. Wenn Ihre minSdkVersion
also 21 oder höher ist, ist Multidex standardmäßig aktiviert und Sie benötigen die Multidex-Bibliothek nicht.
Weitere Informationen zur Android 5.0-Laufzeit 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 die App bereitstellen. Dazu gehört auch das Aktivieren 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 möglicherweise trotzdem den Release-Build für Multidex konfigurieren, um das Limit von 64.000 KB zu vermeiden.
Das Limit von 64.000 KB vermeiden
Bevor Sie Ihre Anwendung für die Verwendung von 64.000 oder mehr Methodenreferenzen konfigurieren, sollten Sie die Gesamtzahl der Referenzen reduzieren, die von Ihrem Anwendungscode aufgerufen werden, einschließlich der Methoden, die durch Ihren Anwendungscode oder enthaltene Bibliotheken definiert wurden.
Die folgenden Strategien können Ihnen helfen, das Erreichen des DEX-Referenzlimits zu vermeiden:
- Direkte und transitive Abhängigkeiten Ihrer App überprüfen
- Überlegen Sie, ob der Wert einer großen Bibliotheksabhängigkeit, die Sie in Ihre App aufnehmen, die Menge an Code überwiegt, der der App hinzugefügt wird. Ein häufiges, aber problematisches Muster ist die Einbindung einer sehr großen Bibliothek, da einige Dienstprogrammmethoden nützlich waren. Durch das Reduzieren der Anwendungscodeabhängigkeiten können Sie häufig vermeiden, dass das DEX-Referenzlimit erreicht wird.
- Ungenutzten Code mit R8 entfernen
- Aktivieren Sie Codekomprimierung, um R8 für Ihre Release-Builds auszuführen. Aktiviere die Verkleinerung, damit du keinen ungenutzten Code mit deinen APKs sendest. Wenn die Codeverkleinerung richtig konfiguriert ist, kann sie auch nicht verwendeten Code und nicht verwendete Ressourcen aus Ihren Abhängigkeiten entfernen.
Mit diesen Techniken kannst du die Gesamtgröße deines APK verringern und die Notwendigkeit von 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 minSdkVersion
auf 20 oder niedriger festgelegt ist, müssen Sie die Multidex-Bibliothek verwenden und die folgenden Änderungen am App-Projekt vornehmen:
-
Ändern Sie die Datei
build.gradle
auf Modulebene, um Multidex zu aktivieren, und fügen Sie die Multidex-Bibliothek als Abhängigkeit hinzu, wie hier gezeigt:Cool
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 Klasse
Application
nicht überschreiben, bearbeiten Sie die Manifestdatei so, dassandroid:name
im<application>
-Tag festgelegt wird:<?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, dassMultiDexApplication
erweitert wird:Kotlin
class MyApplication : MultiDexApplication() {...}
Java
public class MyApplication extends MultiDexApplication { ... }
Wenn Sie die Klasse
Application
überschreiben, die Basisklasse jedoch 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 Reflexion oder JNI aus, bevorMultiDex.install()
abgeschlossen ist. Das Multidex-Tracing verfolgt diese Aufrufe nicht und verursachtClassNotFoundException
oder verifiziert Fehler aufgrund einer fehlerhaften Klassenpartitionierung zwischen DEX-Dateien.
Wenn Sie jetzt Ihre App erstellen, erstellen die Android-Build-Tools eine primäre DEX-Datei (classes.dex
) und unterstützende DEX-Dateien (classes2.dex
, classes3.dex
usw.) nach Bedarf.
Das Build-System verpackt dann alle DEX-Dateien in dein APK.
Zur Laufzeit suchen die Multidex-APIs nicht nur in der classes.dex
-Hauptdatei, sondern mit einem speziellen Klassenladeprogramm, um alle verfügbaren DEX-Dateien für Ihre Methoden zu durchsuchen.
Einschränkungen der Multidex-Bibliothek
Für die multidex-Bibliothek gelten einige bekannte Einschränkungen. Wenn Sie die Bibliothek in die Build-Konfiguration Ihrer App einbinden, beachten Sie Folgendes:
- Die Installation von DEX-Dateien während des Starts auf der Datenpartition eines Geräts ist komplex und kann zu ANR-Fehlern (Application Not Responding) führen, wenn die sekundären DEX-Dateien groß sind. Um dieses Problem zu vermeiden, aktivieren Sie das Verkleinern von Code, um die Größe von DEX-Dateien zu minimieren und nicht verwendete Codeabschnitte zu entfernen.
- Bei der Ausführung auf 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 dadurch wurde das Problem nicht vollständig behoben.
Bei Versionen unter Android 4.0 erreichen Sie möglicherweise das Limit für linearalloc-Daten vor dem DEX-Indexlimit. Wenn Sie API-Levels unter 14 anstreben, sollten Sie diese Versionen der Plattform gründlich testen, da Ihre App beim Start oder beim Laden bestimmter Gruppen von Klassen Probleme haben kann.
Durch das Verkleinern des Codes lassen sich diese Probleme reduzieren oder möglicherweise beseitigen.
Deklarieren Sie die erforderlichen Klassen in der primären DEX-Datei
Beim Erstellen der einzelnen DEX-Dateien für eine Multidex-Anwendung führen die Build-Tools eine komplexe Entscheidungsfindung durch, um festzustellen, welche Klassen in der primären DEX-Datei erforderlich sind, damit Ihre Anwendung erfolgreich gestartet werden kann. Wenn eine beim Start erforderliche Klasse nicht in der primären DEX-Datei angegeben 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 die Selbstprüfung oder den Aufruf von Java-Methoden aus nativem Code verwendet, werden diese Klassen in der primären DEX-Datei möglicherweise nicht als erforderlich 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. Dazu deklarieren Sie sie mit dem Attribut multiDexKeepProguard
in Ihrem Build-Typ. Stimmt eine Klasse in der Datei multiDexKeepProguard
überein, wird diese Klasse der primären DEX-Datei hinzugefügt.
multiDexKeepProguard-Property
Die Datei multiDexKeepProguard
hat dasselbe Format wie ProGuard und unterstützt die gesamte ProGuard-Grammatik. Weitere Informationen zum Anpassen, was in Ihrer App aufbewahrt werden soll, finden Sie unter Festlegen, welcher Code beibehalten werden soll.
Die Datei, die Sie in multiDexKeepProguard
angeben, sollte -keep
-Optionen in einer beliebigen 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
So deklarieren Sie diese Datei für einen Build-Typ:
Cool
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 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 Ihren Entwicklungsprozess verlangsamen können.
Verwenden Sie Pre-Dexing, um die Multidex-Ausgabe zwischen Builds wiederzuverwenden und so längere inkrementelle Build-Zeiten abzumildern.
Das Predexing basiert auf einem ART-Format, das nur unter Android 5.0 (API-Level 21) und höher verfügbar ist. Wenn du Android Studio verwendest, verwendet die IDE automatisch Predexing, wenn deine App auf einem Gerät mit Android 5.0 (API-Level 21) oder höher bereitgestellt wird.
Wenn Sie Gradle-Builds jedoch über die Befehlszeile ausführen, müssen Sie minSdkVersion
auf 21 oder höher festlegen, um das Predexing zu aktivieren.
minSdkVersion
, wie hier gezeigt:
Cool
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 über Android Studio oder 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 bereitstellen, sodass nur die Datei für API-Level 20 und niedriger den <application>
-Tag-Namen ändert. Sie können auch für jede Variante eine andere abgeleitete Application
-Klasse erstellen, sodass nur die abgeleitete Klasse 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-Anwendungen schreiben, ist keine zusätzliche Konfiguration erforderlich, wenn Sie eine
MonitoringInstrumentation
- oder
AndroidJUnitRunner
-Instrumentierung verwenden. Wenn Sie einen anderen Instrumentation
verwenden, müssen Sie dessen 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); ... }