Multidex für Anwendungen mit über 64.000 Methoden aktivieren

Wenn deine App eine minSdk mit API 20 oder niedriger hat und deine App und die Bibliotheken, auf die sie verweist, 65.536 Methoden überschreiten, tritt der folgende Build-Fehler auf, der darauf hinweist, dass deine 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, was ein Hinweis auf dasselbe Problem ist:

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

Für diese Fehlerbedingungen wird eine gängige Zahl angezeigt: 65536. Diese Zahl stellt die Gesamtzahl der Referenzen dar, 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 Anwendungskonfiguration namens Multidex aktivieren, mit der Ihre Anwendung mehrere DEX-Dateien erstellen und lesen kann.

Informationen zum Referenzlimit von 64 K

Android-App-Dateien (APK-Dateien) enthalten ausführbare Bytecode-Dateien in Form von Dalvik Executable-Dateien (DEX), die den kompilierten Code zum Ausführen Ihrer App enthalten. 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 Ihren Android-Framework-Methoden, Bibliotheksmethoden und Methoden.

In der Informatik bezeichnet der Begriff Kilo oder K 1024 (oder 2^10). Da 65.536 gleich 64 x 1.024 ist, wird dieses Limit als _64.000 Referenzlimit_ bezeichnet.

Multidex-Unterstützung vor Android 5.0

Versionen der Plattform 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 einzige classes.dex-Bytecode-Datei 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:

Groovig

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. Informationen zu den aktuellen Versionen dieser Bibliothek finden Sie unter Multidex-Versionen.

Weitere Informationen finden Sie im Abschnitt zum Konfigurieren der Anwendung für Multidex.

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, sucht nach classesN.dex-Dateien und kompiliert sie zu einer einzigen OAT-Datei, die auf dem Android-Gerät ausgeführt wird. Wenn 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 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 sie bereitstellen. Dazu gehört auch die Aktivierung von Multidex, wenn auf den Zielgeräten Android 5.0 und höher ausgeführt wird. Da diese Optimierung nur beim Bereitstellen Ihrer App mit Android Studio angewendet wird, müssen Sie möglicherweise trotzdem Ihren Release-Build für Multidex konfigurieren, um das Limit von 64.000 $ zu vermeiden.

64.000 Dateien vermeiden

Bevor du deine App so konfigurierst, dass die Verwendung von mindestens 64.000 Methodenverweisen aktiviert wird, musst du die Gesamtzahl der Verweise reduzieren, die von deinem App-Code aufgerufen werden. Dazu gehören auch Methoden, die durch deinen App-Code oder eingeschlossene Bibliotheken definiert wurden.

Die folgenden Strategien können 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 gängiges, aber problematisches Muster ist das Einbeziehen einer sehr großen Bibliothek, da einige Dienstprogrammmethoden nützlich waren. Durch das Reduzieren der Abhängigkeiten im Anwendungscode können Sie das DEX-Referenzlimit häufig umgehen.
Ungenutzten Code mit R8 entfernen
Aktivieren Sie die Codekomprimierung, um R8 für Ihre Release-Builds auszuführen. Aktiviere die Verkleinerung, damit du nicht verwendeten Code mit deinen APKs versenden kannst. Wenn die Codekomprimierung richtig konfiguriert ist, können auch nicht verwendete Code und Ressourcen aus den Abhängigkeiten entfernt werden.

Mit diesen Methoden kannst du die Gesamtgröße deines APKs verringern und die Notwendigkeit von Multidex in deiner App vermeiden.

Anwendung 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 Anwendungsprojekt vornehmen:

  1. Ä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:

    Groovig

    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. Je nachdem, ob Sie die Klasse Application überschreiben, führen Sie einen der folgenden Schritte aus:
    • Wenn Sie die Klasse Application nicht überschreiben, bearbeiten Sie Ihre Manifestdatei, um android:name im <application>-Tag 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 MultiDexApplication 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 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 Reflexion oder JNI aus, bevor MultiDex.install() abgeschlossen ist. Das Multidex-Tracing folgt diesen Aufrufen nicht, was zu ClassNotFoundException führt oder Fehler aufgrund einer fehlerhaften Klassenpartition zwischen DEX-Dateien überprüft.

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

Während der Laufzeit suchen die Multidex-APIs nicht nur in der classes.dex-Hauptdatei, sondern verwenden ein spezielles Klassenladeprogramm, um alle verfügbaren DEX-Dateien nach Ihren Methoden zu durchsuchen.

Einschränkungen der Multidex-Bibliothek

Die Multidex-Bibliothek hat einige bekannte Einschränkungen. Berücksichtigen Sie Folgendes, wenn Sie die Bibliothek in Ihre App-Build-Konfiguration einbinden:

  • Die Installation von DEX-Dateien beim Start 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 des Codes, um die Größe der 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 Limit für die lineare Zuordnung zu umgehen (Problem 37008143). Dieses Limit wurde in Android 4.0 (API-Level 14) erhöht, das Problem wurde dadurch jedoch nicht vollständig behoben.

    Bei Versionen vor Android 4.0 wird das Limit der linearen Zuordnung möglicherweise vor dem DEX-Indexlimit erreicht. Wenn Ihre App auf API-Level unter 14 ausgerichtet ist, sollten Sie sie gründlich mit diesen Versionen der Plattform testen, da Ihre App beim Start oder beim Laden bestimmter Gruppen von Klassen Probleme haben kann.

    Durch eine Codereduzierung können diese Probleme reduziert oder möglicherweise sogar beseitigt werden.

Erforderliche Deklarationsklassen in der primären DEX-Datei

Beim Erstellen jeder DEX-Datei für eine Multidex-Anwendung führen die Build-Tools eine komplexe Entscheidungsfindung durch, um zu bestimmen, 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 Ihre Anwendung mit dem Fehler java.lang.NoClassDefFoundError ab.

Die Build-Tools erkennen die Codepfade für Code, auf den direkt über den 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 eine Introspektion 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 in Ihrem Build-Typ mit dem Attribut multiDexKeepProguard. Wenn eine Klasse in der Datei multiDexKeepProguard abgeglichen wird, wird diese Klasse der primären DEX-Datei hinzugefügt.

multiDexKeepProguard-Property

Die Datei multiDexKeepProguard verwendet dasselbe Format wie ProGuard und unterstützt die gesamte ProGuard-Grammatik. Weitere Informationen zum Anpassen des Inhalts Ihrer App finden Sie unter Anpassen, welchen Code Sie behalten möchten.

Die Datei, die Sie in multiDexKeepProguard angeben, muss -keep-Optionen in jeder gültigen ProGuard-Syntax enthalten. Beispiel: -keep com.example.MyClass.class. Sie können eine Datei namens 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 wie folgt für einen Build-Typ deklarieren:

Groovig

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 der primären DEX-Datei enthalten sein müssen und welche Klassen in sekundäre DEX-Dateien aufgenommen werden können. Dies bedeutet, dass inkrementelle Builds mit Multidex in der Regel länger dauern und Ihren Entwicklungsprozess möglicherweise verlangsamen können.

Verwenden Sie Pre-Dexing, um die Multidex-Ausgabe zwischen Builds wiederzuverwenden und so längere inkrementelle Build-Zeiten abzufangen. Pre-Dexing 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, nutzt die IDE automatisch Pre-Dexing, wenn deine App auf einem Gerät mit Android 5.0 (API-Level 21) oder höher bereitgestellt wird. Wenn Sie jedoch Gradle-Builds über die Befehlszeile ausführen, müssen Sie minSdkVersion auf 21 oder höher setzen, um das Pre-Dexing zu aktivieren.

Um die Einstellungen für Ihren Produktions-Build beizubehalten, können Sie zwei Versionen Ihrer App mit Produktvarianten erstellen – eine mit einer Entwicklungs- und eine mit einer Release-Variante – mit unterschiedlichen Werten für minSdkVersion, wie hier gezeigt:

Groovig

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 unterschiedliche Build-Varianten für unterschiedliche Multidex-Anforderungen haben, können Sie für jede Variante eine andere Manifestdatei angeben. So ändert nur die Datei für API-Level 20 und niedriger den <application>-Tag-Namen. Sie können auch für jede Variante eine andere abgeleitete Klasse Application erstellen, sodass nur die abgeleitete Klasse für API-Level 20 und niedriger die Klasse MultiDexApplication 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 eine andere Instrumentation verwenden, müssen Sie deren 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);
  ...
}