Se l'app ha un minSdk di API 20 o versioni precedenti e l'app e le
librerie a cui fa riferimento superano i 65.536 metodi, si verifica il seguente errore di build che
indica che l'app ha raggiunto il limite dell'architettura di 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 build 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 richiamati dal codice all'interno di un singolo file di bytecode Dalvik Executable (DEX). Questa pagina spiega come superare questa limitazione attivando una configurazione dell'app nota come multidex, che consente all'app di creare e leggere più file DEX.
Informazioni sul limite di riferimento di 64.000
I file delle app per Android (APK) contengono file bytecode eseguibili sotto forma di file Dalvik Executable (DEX), che contengono il codice compilato utilizzato per eseguire l'app. La specifica Dalvik Executable limita a 65.536 il numero totale di metodi a cui è possibile fare riferimento all'interno di un singolo file DEX, inclusi i metodi del framework Android, i metodi della libreria e i metodi nel tuo codice.
Nel contesto dell'informatica, il termine kilo o K indica 1024 (o 2^10). Poiché 65.536 è uguale a 64 x 1024, questo limite viene definito _limite di riferimento di 64K_.Supporto multidex prima di Android 5.0
Le versioni della piattaforma precedenti ad Android 5.0 (livello API 21) utilizzano il runtime Dalvik per l'esecuzione del codice dell'app. Per impostazione predefinita, Dalvik limita le app a un singolo file di bytecode classes.dex per APK. Per aggirare questa limitazione, aggiungi la libreria multidex al file build.gradle o build.gradle.kts a livello di modulo:
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 attuali di questa libreria, consulta le versioni multidex.
Per maggiori dettagli, consulta la sezione su come configurare l'app per la configurazione multidex.Supporto multidex per Android 5.0 e versioni successive
Android 5.0 (livello API 21) e versioni successive utilizzano un runtime chiamato ART che supporta in modo nativo il caricamento di più file DEX dai file APK. ART
esegue la precompilazione al momento dell'installazione dell'app, cercando i file
classesN.dex e compilandoli in un unico file
OAT per
l'esecuzione da parte del dispositivo Android. Pertanto, se il tuo minSdkVersion è 21 o superiore, la configurazione multidex è abilitata per impostazione predefinita e non hai bisogno della libreria multidex.
Per saperne di più sul runtime di Android 5.0, leggi Android Runtime (ART) e Dalvik.
Nota: quando esegui l'app utilizzando Android Studio, la build viene ottimizzata per i dispositivi di destinazione su cui esegui il deployment. Ciò include l'attivazione della configurazione multidex quando i dispositivi di destinazione eseguono Android 5.0 e versioni successive. Poiché questa ottimizzazione viene applicata solo quando esegui il deployment della tua app utilizzando Android Studio, potresti comunque dover configurare la build di release per multidex per evitare il limite di 64.000.
Evita il limite di 64K
Prima di configurare l'app per abilitare l'utilizzo di almeno 64.000 riferimenti ai metodi, adotta misure per ridurre il numero totale di riferimenti chiamati dal codice dell'app, inclusi i metodi definiti dal codice dell'app o dalle librerie incluse.
Le seguenti strategie possono aiutarti a evitare di raggiungere il limite di riferimento DEX:
- Esamina le dipendenze dirette e transitive della tua app
- Valuta se il valore di una dipendenza di una libreria di grandi dimensioni che includi nella tua app supera la quantità di codice aggiunta all'app. Un pattern comune ma problematico è l'inclusione di una libreria molto grande perché alcuni metodi di utilità erano utili. La riduzione delle dipendenze del codice dell'app spesso può aiutarti a evitare il limite di riferimento DEX.
- Rimuovi il codice inutilizzato con R8
- Attiva la riduzione del codice per eseguire R8 per le build di release. Attiva la compressione per assicurarti di non includere codice inutilizzato negli APK. Se la riduzione del codice è configurata correttamente, può anche rimuovere il codice e le risorse inutilizzati dalle dipendenze.
L'utilizzo di queste tecniche può aiutarti a ridurre le dimensioni complessive dell'APK ed evitare la necessità di una configurazione multidex nell'app.
Configura la tua app per la configurazione multidex
Nota: seminSdkVersion è impostato su 21 o un valore superiore, la configurazione multidex è abilitata per impostazione predefinita e non è necessaria la libreria multidex.
Se minSdkVersion è impostato su 20 o un valore inferiore, devi utilizzare la libreria multidex e apportare le seguenti modifiche al progetto dell'app:
-
Modifica il file
build.gradlea livello di modulo per attivare la configurazione multidex e aggiungi la libreria multidex come dipendenza, come mostrato di seguito:Alla moda
android { defaultConfig { ... minSdkVersion 15 targetSdkVersion 36 multiDexEnabled true } ... } dependencies { implementation "androidx.multidex:multidex:2.0.1" }
Kotlin
android { defaultConfig { ... minSdk = 15 targetSdk = 36 multiDexEnabled = true } ... } dependencies { implementation("androidx.multidex:multidex:2.0.1") }
- A seconda che tu esegua o meno l'override della classe
Application, esegui una delle seguenti operazioni:Se non esegui l'override della classe
Application, modifica il file manifest per impostareandroid:namenel tag<application>come segue:<?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 esegui l'override della classe
Application, modificala in modo che estendaMultiDexApplication, come segue:Kotlin
class MyApplication : MultiDexApplication() {...}
Java
public class MyApplication extends MultiDexApplication { ... }
Se esegui l'override della classe
Application, ma non è possibile modificare la classe base, esegui l'override del metodoattachBaseContext()e chiamaMultiDex.install(this)per attivare la configurazione 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 altro codice tramite reflection o JNI prima del completamento diMultiDex.install(). Il tracciamento multidex non seguirà queste chiamate, causando erroriClassNotFoundExceptiono di verifica dovuti a una partizione di classe errata tra i file DEX.
Ora, quando crei l'app, gli strumenti di creazione di 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 build pacchettizza quindi tutti i file DEX nell'APK.
In fase di runtime, anziché eseguire la ricerca solo nel file classes.dex principale, le API multidex utilizzano un caricatore di classi speciale per cercare i tuoi metodi in tutti i file DEX disponibili.
Limitazioni della libreria multidex
La libreria multidex presenta alcune limitazioni note. Quando incorpori la libreria nella configurazione di compilazione dell'app, tieni presente quanto segue:
- L'installazione dei file DEX durante l'avvio nella partizione dei dati di un dispositivo è complessa e può causare errori di tipo "L'applicazione non risponde" (ANR) se i file DEX secondari sono di grandi dimensioni. Per evitare questo problema, attiva la riduzione del codice per ridurre al minimo le dimensioni dei file DEX e rimuovere le parti di codice inutilizzate.
- Se l'esecuzione avviene su versioni precedenti ad Android 5.0 (livello API 21), l'utilizzo della configurazione multidex non è sufficiente per aggirare il limite di linearalloc (problema 37008143). Questo limite è stato aumentato in Android 4.0 (livello API 14), ma il problema non è stato risolto completamente.
Nelle versioni precedenti ad Android 4.0, potresti raggiungere il limite di linearalloc prima di raggiungere il limite dell'indice DEX. Pertanto, se scegli come target livelli API inferiori a 14, esegui test approfonditi su queste versioni della piattaforma, perché l'app potrebbe avere problemi all'avvio o quando vengono caricati gruppi di classi particolari.
La riduzione del codice può ridurre o eventualmente eliminare questi problemi.
Dichiara le classi richieste nel file DEX principale
Durante la creazione di ogni file DEX per un'app con configurazione multidex, gli strumenti di build eseguono
un processo decisionale complesso per determinare quali classi sono necessarie nel file DEX principale
in modo che l'app possa avviarsi correttamente. Se una classe richiesta durante l'avvio non viene fornita nel file DEX principale, l'app si arresta in modo anomalo con l'errore java.lang.NoClassDefFoundError.
Gli strumenti di build riconoscono i percorsi del codice a cui si accede direttamente dal codice dell'app. Tuttavia, questo problema può verificarsi quando i percorsi del codice sono meno visibili, ad esempio quando una libreria che utilizzi ha dipendenze complesse. Ad esempio, se il codice utilizza l'introspezione o l'invocazione di metodi Java dal codice nativo, queste classi potrebbero non essere riconosciute come obbligatorie nel file DEX principale.
Se ricevi java.lang.NoClassDefFoundError, devi specificare manualmente le classi aggiuntive richieste nel file DEX principale dichiarandole con la proprietà multiDexKeepProguard nel tipo di build. Se una classe corrisponde nel file multiDexKeepProguard, viene aggiunta al file DEX principale.
Proprietà multiDexKeepProguard
Il file multiDexKeepProguard utilizza lo stesso formato di ProGuard e supporta l'intera grammatica di ProGuard. Per ulteriori informazioni su come personalizzare gli elementi da conservare nella tua app, consulta Personalizza il codice da conservare.
Il file specificato in multiDexKeepProguard deve contenere le opzioni -keep in qualsiasi sintassi ProGuard valida. Ad esempio, -keep com.example.MyClass.class. Puoi creare un file denominato multidex-config.pro simile al seguente:
-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
Puoi quindi dichiarare il 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 la configurazione multidex nelle build di sviluppo
Una configurazione multidex richiede un tempo di elaborazione della build notevolmente maggiore, perché il sistema di build deve prendere decisioni complesse su quali classi devono essere incluse nel file DEX principale e quali possono essere incluse nei file DEX secondari. Ciò significa che le build incrementali che utilizzano la configurazione multidex in genere richiedono più tempo e possono potenzialmente rallentare il processo di sviluppo.
Per ridurre i tempi di build incrementale più lunghi, utilizza il 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 versioni successive. Se utilizzi Android Studio, l'IDE utilizza automaticamente il pre-dexing
quando esegue il deployment dell'app su un dispositivo con Android 5.0 (livello API 21) o versioni successive.
Tuttavia, se esegui build Gradle dalla riga di comando, devi impostare minSdkVersion su 21 o un valore superiore per attivare il pre-dexing.
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") }
Per scoprire altre strategie per migliorare la velocità di build da Android Studio o dalla riga di comando, leggi Ottimizza la velocità di build. Per saperne di più sull'utilizzo delle varianti di compilazione, consulta Configura le varianti di compilazione.
Suggerimento:se hai varianti di compilazione diverse per esigenze multidex diverse, puoi fornire un file manifest diverso per ogni variante, in modo che solo il file per il livello API 20 e versioni precedenti modifichi il nome del tag <application>. Puoi anche creare una sottoclasse Application diversa per ogni variante, in modo che solo la sottoclasse per il livello API 20 e versioni precedenti estenda la classe MultiDexApplication o chiami MultiDex.install(this).
Testa le app con configurazione multidex
Quando scrivi test di instrumentazione per le app multidex, non è necessaria alcuna configurazione aggiuntiva se utilizzi un'instrumentazione
MonitoringInstrumentation o
AndroidJUnitRunner. Se utilizzi un altro Instrumentation, devi eseguire l'override del relativo 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); ... }