Multidex für Anwendungen mit über 64.000 Methoden aktivieren

Wenn Ihre App ein minSdk mit API-Level 20 oder niedriger hat und Ihre App und die Bibliotheken, auf die sie verweist, mehr als 65.536 Methoden enthalten, wird der folgende Build-Fehler 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.

In älteren Versionen des Build-Systems wird ein anderer Fehler gemeldet, der auf dasselbe Problem hinweist:

Conversion to Dalvik format failed:
Unable to execute dex: method ID not in [0, 0xffff]: 65536

Bei diesen Fehlerbedingungen wird die gemeinsame Nummer 65536 angezeigt. Diese Zahl gibt die Gesamtzahl der Referenzen an, die vom Code in einer einzelnen DEX-Bytecode-Datei (Dalvik Executable) aufgerufen werden können. Auf dieser Seite wird erläutert, wie Sie diese Einschränkung umgehen können, indem Sie eine App-Konfiguration namens Multidex aktivieren. Dadurch kann Ihre App mehrere DEX-Dateien erstellen und lesen.

Referenzlimit von 64.000 Tokens

Android-App-Dateien (APKs) enthalten ausführbare Bytecode-Dateien in Form von Dalvik Executable (DEX)-Dateien, die den kompilierten Code enthalten, der zum Ausführen Ihrer App verwendet wird. Die Dalvik Executable-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.

In der Informatik steht der Begriff Kilo oder K für 1024 (oder 2^10). Da 65.536 = 64 × 1.024 ist, wird dieses Limit als _64K-Referenzlimit_ bezeichnet.

Multidex-Unterstützung vor Android 5.0

In Versionen der Plattform vor Android 5.0 (API-Level 21) wird die Dalvik-Laufzeit zum Ausführen von App-Code verwendet. Standardmäßig ist für Apps in Dalvik nur eine classes.dex-Bytecode-Datei pro APK zulässig. 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

In Android 5.0 (API-Ebene 21) und höher wird eine Laufzeit namens ART verwendet, die das native Laden mehrerer DEX-Dateien aus APK-Dateien unterstützt. ART führt die Vorabkompilierung bei der Installation der App durch. Dabei werden classesN.dex-Dateien gescannt und in einer einzigen OAT-Datei für die Ausführung auf dem Android-Gerät kompiliert. Wenn Ihr 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 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 weiterhin Ihren Release-Build für Multidex konfigurieren, um das 64‑KB-Limit zu vermeiden.

64.000-Zeichen-Limit umgehen

Bevor Sie Ihre App so konfigurieren, dass 64.000 oder mehr Methodenreferenzen verwendet werden können, sollten Sie die Gesamtzahl der Referenzen reduzieren, die von Ihrem App-Code aufgerufen werden, einschließlich der Methoden, die von Ihrem App-Code oder den enthaltenen 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 an Code überwiegt, die der App hinzugefügt wird. Ein häufiges, aber problematisches Muster ist, eine sehr große Bibliothek einzubinden, 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
Codekomprimierung aktivieren, um R8 für Ihre Release-Builds auszuführen. Aktivieren Sie die Reduzierung, um sicherzustellen, dass Sie keinen ungenutzten Code mit Ihren APKs ausliefern. 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 APK verringern und vermeiden, dass Sie multidex in Ihrer App benötigen.

App für Multidex konfigurieren

Hinweis: Wenn minSdkVersion 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 weniger festgelegt ist, müssen Sie die Multidex-Bibliothek verwenden und die folgenden Änderungen an Ihrem App-Projekt vornehmen:

  1. Ä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")
    }
  2. 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, um android:name im Tag <application> 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 sie MultiDexApplication erweitert:

      Kotlin

      class MyApplication : MultiDexApplication() {...}

      Java

      public class MyApplication extends MultiDexApplication { ... }
    • Wenn Sie die Klasse Application überschreiben, die Basisklasse jedoch nicht ändern können, überschreiben Sie stattdessen die Methode attachBaseContext() und rufen Sie MultiDex.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, bevor MultiDex.install() abgeschlossen ist. Multidex-Tracing folgt diesen Aufrufen nicht, was zu ClassNotFoundException- oder Bestätigungsfehlern aufgrund einer fehlerhaften Klassenpartitionierung zwischen DEX-Dateien führt.

Wenn Sie Ihre App jetzt erstellen, erstellen die Android-Build-Tools bei Bedarf eine primäre DEX-Datei (classes.dex) und unterstützende DEX-Dateien (classes2.dex, classes3.dex usw.). Das Build-System packt dann alle DEX-Dateien in Ihr APK.

Zur Laufzeit wird nicht nur in der classes.dex-Hauptdatei gesucht, sondern die Multidex-APIs verwenden einen speziellen Klassenlader, um alle verfügbaren DEX-Dateien nach Ihren Methoden zu durchsuchen.

Einschränkungen der Multidex-Bibliothek

Die Multidex-Bibliothek hat 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“ / „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 unter Android 4.0 kann das „linearalloc“-Limit erreicht werden, bevor das DEX-Indexlimit erreicht wird. Wenn Sie also auf API-Ebenen unter 14 abzielen, sollten Sie Ihre App auf diesen Versionen der Plattform gründlich testen, da beim Start oder beim Laden bestimmter Klassengruppen Probleme auftreten können.

    Durch Code-Shrinking können diese Probleme reduziert oder möglicherweise ganz behoben werden.

Erforderliche Klassen in der primären DEX-Datei deklarieren

Beim Erstellen jeder DEX-Datei 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 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 im Code beispielsweise Introspection oder der Aufruf von Java-Methoden aus nativem Code verwendet wird, 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, indem Sie sie mit dem Attribut multiDexKeepProguard in Ihrem Build-Typ deklarieren. Wenn eine Klasse in der Datei multiDexKeepProguard gefunden wird, 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 dazu, wie Sie anpassen können, was in Ihrer App beibehalten wird, finden Sie unter Anpassen, welcher Code beibehalten wird.

Die in multiDexKeepProguard angegebene Datei 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

Anschließend können Sie die 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 können. 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 Pre-Dexing verwenden, um die Multidex-Ausgabe zwischen Builds wiederzuverwenden. Für die Vordexing-Funktion ist ein ART-Format erforderlich, das nur unter Android 5.0 (API-Level 21) und höher verfügbar ist. Wenn Sie Android Studio verwenden, wird beim Bereitstellen Ihrer App auf einem Gerät mit Android 5.0 (API‑Level 21) oder höher automatisch die Vordexing-Funktion verwendet. Wenn Sie jedoch Gradle-Builds über die Befehlszeile ausführen, müssen Sie minSdkVersion auf 21 oder höher festlegen, um Pre-Dexing zu aktivieren.

Wenn Sie die Einstellungen für Ihren Produktions-Build beibehalten möchten, können Sie zwei Versionen Ihrer App mit Produktvarianten erstellen: eine Version mit einer Entwicklungs- und eine Version mit einer Release-Variante. Diese haben unterschiedliche Werte für minSdkVersion, wie unten 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 ü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 angeben. So wird nur der Dateiname für API-Level 20 und niedriger geändert.<application> 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 eine AndroidJUnitRunner-Instrumentierung verwenden. Wenn Sie ein anderes Instrumentation verwenden, müssen Sie 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);
  ...
}