Attiva il multidex per le app con più di 64.000 metodi

Se la tua app ha minSdk di API 20 o versioni precedenti e la tua app e librerie a cui fa riferimento superano i 65.536 metodi, si verifica il seguente errore di build indica che la tua app ha raggiunto il limite dell'architettura della build di Android:

trouble writing output:
Too many field references: 131000; max is 65536.
You may try using --multi-dex option.

Le versioni precedenti del sistema di compilazione segnalano un errore diverso, che indica lo stesso problema:

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

Queste condizioni di errore mostrano un numero comune: 65536. Questo numero rappresenta il numero totale di riferimenti che possono essere richiamato dal codice all'interno di un singolo file bytecode Dalvik Executable (DEX). In questa pagina viene spiegato come superare questo limite: attivare una configurazione dell'app nota come multidex, che consente alla tua app per creare e leggere più file DEX.

Informazioni sul limite di 64.000 riferimenti

I file delle app per Android (APK) contengono file bytecode eseguibili in forma di di Dalvik File eseguibili (DEX), che contengono il codice compilato utilizzato per eseguire l'app. La specifica Dalvik Executable limita il numero totale di metodi che all'interno di un singolo file DEX è possibile fare riferimento a 65.536,tra cui Android framework, metodi di libreria e metodi all'interno del tuo codice.

Nella nel contesto dell'informatica, il termine kilo o K indica 1024 (ovvero 2^10). Poiché 65.536 corrisponde a 64 x 1024, questo limite è denominato _Limite di riferimento di 64.000_.

Supporto di Multidex prima di Android 5.0

Le versioni della piattaforma precedenti ad Android 5.0 (livello API 21) utilizzano Dalvik per l'esecuzione del codice dell'app. Per impostazione predefinita, Dalvik limita le app a una singola classes.dex file bytecode per APK. Per evitare limitazione, aggiungi la libreria multidex al livello build.gradle a livello di modulo oppure build.gradle.kts file:

Alla moda

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")
}

Questa libreria diventa parte del file DEX principale della tua app e poi gestisce l'accesso ai file DEX aggiuntivi e al codice che contengono. Per visualizzare le versioni correnti di questa libreria, vedi versioni multidex.

Per ulteriori dettagli, consulta la sezione su come configurare la tua app per multidex.

Supporto di Multidex per Android 5.0 e versioni successive

Android 5.0 (livello API 21) e versioni successive utilizza un runtime chiamato ART che supporta in modo nativo il caricamento di più file DEX dai file APK. ARTE esegue la precompilazione al momento dell'installazione dell'app, analizzando classesN.dex file e la loro compilazione in un'unica file OAT per esecuzione da parte del dispositivo Android. Pertanto, se minSdkVersion è 21 o superiore, il multidex è abilitato per impostazione predefinita e non è necessaria la libreria multidex.

Per ulteriori informazioni su Android 5.0 runtime, leggi Android Runtime (ART) e Dalvik.

Nota: quando esegui l'app utilizzando Android Studio, la build è ottimizzata per i dispositivi di destinazione su cui esegui il deployment. Ciò include l'attivazione del multidex quando i dispositivi di destinazione sono in esecuzione Android 5.0 e versioni successive. Poiché questa ottimizzazione viene applicata solo quando esegui il deployment dell'app utilizzando Android Studio, potrebbe essere comunque necessario configurare la build della release per il multidex per evitare il limite dei 64K.

Evita il limite di 64.000

Prima di configurare l'app in modo da consentire l'uso di almeno 64.000 riferimenti di metodi, segui questi passaggi per ridurre il numero totale di riferimenti chiamati dal codice dell'app, inclusi i metodi definiti del codice dell'app o delle librerie incluse.

Le seguenti strategie possono aiutarti a evitare di raggiungere il limite di riferimenti DEX:

Rivedi le dipendenze dirette e transitive della tua app
Valuta se il valore di qualsiasi dipendenza dalla libreria di grandi dimensioni che includi nella tua app supera la quantità di codice aggiunta all'app. Un metodo comune, ma problematico, è includere una libreria molto grande perché si sono rivelati utili alcuni metodi di utilità. Spesso è utile ridurre le dipendenze del codice dell'app il limite di riferimento DEX.
Rimuovi il codice inutilizzato con R8
Abilita la riduzione del codice per eseguire R8 per le build delle tue release. Attiva la riduzione per assicurarti di non spediscano il codice inutilizzato con gli APK. Se la riduzione del codice è configurata correttamente, puoi anche rimuovere codice e risorse inutilizzati dalle tue dipendenze.

Queste tecniche possono aiutarti a ridurre le dimensioni complessive dell'APK e evitando il multidex nell'app.

Configura la tua app per Multidex

Nota: se il tuo minSdkVersion è impostato su 21 o superiore, il multidex è attivo per impostazione predefinita e non ti serve la libreria multidex.

Se il valore di minSdkVersion è impostato su 20 o su un valore inferiore, deve utilizzare libreria multidex e le seguenti modifiche al progetto dell'app:

  1. Modifica il file build.gradle a livello di modulo in abilita multidex e aggiungi la libreria multidex come dipendenza, come mostrato qui:

    Alla moda

    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. A seconda che tu decida o meno di eseguire l'override di Application esegui una delle seguenti operazioni:
    • Se non esegui l'override di Application modifica il file manifest impostando android:name nel <application> nel seguente modo:

      <?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>
      
    • Se sostituisci Application , modificala per estendere MultiDexApplication, come segue:

      Kotlin

      class MyApplication : MultiDexApplication() {...}
      

      Java

      public class MyApplication extends MultiDexApplication { ... }
      
    • Se sostituisci Application ma non è possibile modificare la classe base, sostituisci il metodo attachBaseContext() e richiama MultiDex.install(this) per attivare multidex:

      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);
        }
      }
      

      Attenzione: non eseguire MultiDex.install() o qualsiasi altro codice tramite riflessione o JNI prima del completamento di MultiDex.install(). Il tracciamento multidex non seguire queste chiamate, causando ClassNotFoundException errori o verificando gli errori a causa di una partizione di classe non valida tra i file DEX.

Ora, quando crei la tua app, gli strumenti di sviluppo Android creano un file DEX principale (classes.dex) e file DEX di supporto (classes2.dex, classes3.dex e così via) in base alle esigenze. Il sistema di compilazione pacchettizza quindi tutti i file DEX nell'APK.

In fase di runtime, anziché cercare solo nella cartella classes.dex, le API multidex usano un caricatore di classi speciale per cercare in tutti i i file DEX disponibili per i tuoi metodi.

Limitazioni della libreria multidex

La libreria multidex presenta alcune limitazioni note. Quando incorpori la libreria nella configurazione della build dell'app, considera quanto segue:

  • L'installazione di file DEX durante l'avvio sulla partizione dati di un dispositivo è complessa e possono causare errori L'applicazione non risponde (ANR) se i file DEX secondari sono di grandi dimensioni. A evita questo problema, attiva la riduzione del codice per ridurre al minimo le dimensioni dei file DEX e rimuovi le parti non utilizzate del codice.
  • Se vengono eseguite su versioni precedenti ad Android 5.0 (livello API 21), utilizzando multidex non è sufficiente per aggirare il limite linearalloc (problema 37008143). Questo limite è stato aumentato in Android 4.0 (livello API 14), ma il problema non è stato completamente risolto.

    Nelle versioni precedenti ad Android 4.0, potresti raggiungere il limite di linearalloc prima raggiungendo il limite dell'indice DEX. Se quindi scegli come target livelli API inferiori a 14, eseguire test approfonditi su queste versioni della piattaforma, perché la tua app potrebbe riscontrano problemi all'avvio o quando vengono caricati gruppi specifici di corsi.

    La riduzione del codice può ridurre o risolvere eventuali problemi.

Dichiara le classi richieste nel file DEX principale

Quando si crea ogni file DEX per un'app multidex, gli strumenti di creazione un processo decisionale complesso per determinare quali classi sono necessarie nel file DEX principale per avviare correttamente l'app. Se sono richiesti corsi non è fornito nel file DEX principale durante l'avvio, l'app ha un arresto anomalo con l'errore java.lang.NoClassDefFoundError.

Gli strumenti di creazione riconoscono i percorsi del codice a cui si accede direttamente dalla tua app le API nel tuo codice. Tuttavia, questo problema può si verificano quando i percorsi del codice sono meno visibili, ad esempio quando una libreria che utilizzi ha di dipendenze complesse. Ad esempio, se il codice utilizza introspezione o chiamata metodi Java da codice nativo, queste classi potrebbero non essere riconosciute come obbligatoria nel file DEX principale.

Se ricevi java.lang.NoClassDefFoundError, devi specificare manualmente le classi aggiuntive richieste nel file DEX principale dichiarandoli con la proprietà multiDexKeepProguard nel tipo di build. Se un corso corrisponde a il file multiDexKeepProguard, poi la classe viene aggiunto al file DEX principale.

proprietà multiDexKeepProguard

Il file multiDexKeepProguard utilizza lo stesso formato di ProGuard e supporta il l'intera grammatica di ProGuard. Per ulteriori informazioni su come personalizzare i contenuti memorizzati nella tua app, vedi Personalizzare il codice da conservare.

Il file specificato in multiDexKeepProguard deve contenere -keep in qualsiasi sintassi ProGuard valida. Ad esempio: -keep com.example.MyClass.class. Puoi creare un file denominato multidex-config.pro con aspetto simile a questo:

-keep class com.example.MyClass
-keep class com.example.MyClassToo

Se vuoi specificare tutte le classi di un pacchetto, il file sarà simile al seguente:

-keep class com.example.** { *; } // All classes in the com.example package

Quindi puoi dichiarare quel file per un tipo di build, come segue:

Alla moda

android {
    buildTypes {
        release {
            multiDexKeepProguard file('multidex-config.pro')
            ...
        }
    }
}

Kotlin

android {
    buildTypes {
        getByName("release") {
            multiDexKeepProguard = file("multidex-config.pro")
            ...
        }
    }
}

Ottimizza multidex nelle build di sviluppo

Una configurazione multidex richiede un'elaborazione delle build notevolmente maggiore tempo perché il sistema di compilazione deve prendere decisioni complesse su quali classi devono essere incluse nel file DEX principale e le classi che possono essere incluse file DEX secondari. Ciò significa che generalmente le build incrementali utilizzano multidex richiedono più tempo e possono rallentare il processo di sviluppo.

Per ridurre i tempi di compilazione incrementali più lunghi, utilizza pre-dexing per riutilizzare l'output multidex tra le build. Il pre-dexing si basa su un formato ART disponibile solo su Android 5.0 (livello API 21) e superiori. Se usi Android Studio, l'IDE utilizza automaticamente il pre-dexing quando esegui il deployment della tua app su un dispositivo con Android 5.0 (livello API 21) o versioni successive. Tuttavia, se esegui build di Gradle dalla riga di comando, devi impostare minSdkVersion su 21 o versioni successive per attivare il pre-dexing.

Per mantenere le impostazioni per di produzione, puoi creare due versioni della tua app utilizzando sapori di prodotto: una versione con una versione di sviluppo e una versione con una versione di release, con valori diversi per minSdkVersion, come mostrato:

Alla moda

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")
}

Scoprire altre strategie utili per migliorare la velocità delle build da Android Studio o dal comando vedi Ottimizzare la velocità di compilazione. Per saperne di più sull'utilizzo delle varianti di build, consulta Configura le varianti di build.

Suggerimento:se hai diverse varianti di build per diverse varianti esigenze multidex, puoi fornire un file manifest diverso per ogni pertanto solo il file per il livello API 20 o inferiore modifica la Nome tag <application>. Puoi anche creare una sottoclasse Application diversa per ogni variante in modo che solo la sottoclasse per il livello API 20 o inferiore estende la classe MultiDexApplication o chiama MultiDex.install(this).

Testa le app multidex

Quando scrivi test di strumentazione per le app multidex, non è richiesta alcuna configurazione aggiuntiva se utilizzi un MonitoringInstrumentation o un AndroidJUnitRunner la strumentazione di lavoro. Se utilizzi un altro Instrumentation, devi sostituire il metodo onCreate() con il seguente codice:

Kotlin

fun onCreate(arguments: Bundle) {
  MultiDex.install(targetContext)
  super.onCreate(arguments)
  ...
}

Java

public void onCreate(Bundle arguments) {
  MultiDex.install(getTargetContext());
  super.onCreate(arguments);
  ...
}