DEX (Dalvik Executable) è un formato di bytecode in cui viene compilato il codice sorgente delle applicazioni Android in Java e Kotlin. I file DEX vengono eseguiti dalla macchina virtuale Dalvik (fino ad Android 4.4) o da Android Runtime (ART, a partire da Android 5.0). Secondo Android Open Source Project, 2026, il formato DEX fornisce in media una rappresentazione del codice 30% più compatta rispetto al bytecode Java standard della JVM.
Punti chiave
DEX (Dalvik Executable) è un formato di bytecode progettato specificamente per i dispositivi mobili Android. A differenza del bytecode Java standard (file .class), DEX è ottimizzato per risorse limitate: meno memoria, dimensioni ridotte e caricamento delle classi più veloce.
Il codice sorgente in Java o Kotlin viene compilato da javac/kotlinc in file .class standard (bytecode Java). Successivamente, lo strumento d8 (o in precedenza dx) converte i .class in uno o più file DEX. Questa conversione non è un semplice reimpacchettamento — d8 esegue ottimizzazioni: unisce i pool di costanti, riscrive le istruzioni in architettura a registri e rimuove i dati duplicati.
DEX utilizza un'architettura basata su registri (a differenza della JVM basata su stack). Ogni metodo ha un numero fisso di registri (fino a 65536). Le istruzioni DEX sono più corte — in media 2 byte contro 1–4 byte nella JVM. Questo produce codice più compatto: un'applicazione tipica si riduce da 10–15 MB di .class a 4–6 MB di .dex.
Un file DEX ha una struttura binaria rigorosamente definita. Ogni file inizia con un'intestazione e contiene diverse sezioni che si riferiscono l'una all'altra tramite offset.
| Sezione | Scopo |
|---|---|
| header | Intestazione: magic, checksum, firma, dimensioni e offset delle sezioni |
| string_ids | Tabella delle stringhe: nomi di classi, metodi, campi |
| type_ids | Tipi: riferimenti agli identificatori di stringa dei tipi |
| proto_ids | Prototipi di metodi: tipo restituito e parametri |
| field_ids | Campi delle classi: classe, tipo, nome |
| method_ids | Metodi: classe, prototipo, nome |
| class_defs | Definizioni di classi: flag, superclasse, interfacce, offset dei dati |
| data | Dati effettivi: codice dei metodi, annotazioni, informazioni di debug |
Il numero magico di DEX è `dex\n035\0` (versione 035). Altre versioni: 036, 037, 038 (per Android 8.0+). L'intestazione è di 0x70 byte e contiene un checksum SHA-1 e gli offset di tutte le sezioni. La validazione dell'intestazione è il primo passo quando la macchina virtuale carica un DEX.
string_ids, type_ids, proto_ids, field_ids, method_ids — sono tabelle indicizzate. Invece di memorizzare i nomi completi nel codice del metodo, viene utilizzato un indice di 4 byte. Questa è un'ottimizzazione chiave: se una classe viene menzionata 100 volte, il suo nome viene memorizzato una volta in string_ids. dex2oat ottimizza ulteriormente queste tabelle durante la compilazione ART.
Il processo di conversione del codice sorgente in DEX consiste in diverse fasi. La catena di strumenti moderna utilizza il compilatore D8, che ha sostituito DX nel 2018 con Android Gradle Plugin 3.2.
javac (per Java) o kotlinc (per Kotlin) compila il codice sorgente in file .class. Ogni classe è un file .class separato in bytecode Java. In questa fase vengono eseguiti il controllo dei tipi, la generazione di metodi bridge e l'inlining delle costanti.
D8 prende tutti i file .class e li trasforma in bytecode DEX. D8 esegue diverse ottimizzazioni: rimuove argomenti di metodi inutilizzati, unisce i pool di costanti da diversi file .class in un unico pool DEX globale e converte le istruzioni stack JVM in istruzioni registro Dalvik.
// Codice sorgente Kotlin
data class User(
val name: String,
val email: String
)
fun greet(user: User): String {
return "Hello, ${user.name}!"
}
Dopo la compilazione D8, questo codice si trasforma in istruzioni DEX compatte: const-string per caricare stringhe, iget-object per accedere al campo di un oggetto, invoke-virtual per chiamare StringBuilder.append.
D8 è 2–3 volte più veloce di DX, genera DEX più compatto (5–10% più piccolo) e ottimizza meglio le costruzioni specifiche di Kotlin (funzioni inline, lambda). DX è stato dichiarato deprecato nel 2018 e rimosso da Android Gradle Plugin 8.0.
L'esecuzione del codice DEX in Android ha attraversato due fasi: la macchina virtuale Dalvik originale (Android 2.2–4.4) e Android Runtime ART (Android 5.0+). La differenza nell'approccio di compilazione è fondamentale.
Dalvik utilizzava la compilazione Just-In-Time (JIT): il bytecode DEX veniva interpretato e i metodi chiamati frequentemente venivano compilati in codice nativo al volo. Vantaggio — installazione rapida. Svantaggio — avvio più lento e consumo costante di CPU per JIT.
ART (Android Runtime) compila DEX in codice nativo durante l'installazione dell'applicazione tramite dex2oat. Questo è un approccio Ahead-Of-Time (AOT): l'installazione è più lunga, ma l'avvio è più veloce e il consumo energetico è inferiore. Da Android 7.0, ART utilizza un approccio ibrido — AOT + JIT + ottimizzazione guidata dal profilo.
Lo strumento dex2oat viene eseguito durante l'installazione o l'aggiornamento di un'applicazione. Compila DEX in un file ELF con codice nativo per l'architettura del dispositivo. Il risultato — file .oat e .art nella directory /data/dalvik-cache/. Google migliora continuamente dex2oat: su Android 14 sono state aggiunte ottimizzazioni per i dispositivi pieghevoli.
Il limite di 65536 metodi per file DEX è un'eredità dell'architettura Dalvik. Il campo method_ids nell'intestazione DEX occupa 4 byte, dando un massimo di 2^16 = 65536 riferimenti univoci. Le applicazioni moderne con Google Play Services, Firebase e altri SDK superano facilmente questo limite.
Multidex è un meccanismo per dividere il codice in più file DEX. Il classes.dex principale contiene i punti di ingresso (classe Application, Activity principale), gli altri sono classes2.dex, classes3.dex e così via. All'avvio, le classi dai DEX aggiuntivi vengono caricate tramite DexClassLoader.
// build.gradle.kts — abilitazione multidex
android {
defaultConfig {
multiDexEnabled = true
}
}
// Classe Application con supporto multidex
class MyApp : Application() {
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base)
MultiDex.install(this)
}
}
Il caricamento di DEX aggiuntivi durante l'avvio dell'applicazione può causare ANR (Application Not Responding) sui dispositivi con Android precedente al 5.0. Raccomandazione — utilizzare multidex solo quando necessario e minimizzare le dipendenze per non superare il limite.
L'ottimizzazione di DEX è un passaggio standard nella creazione di un'applicazione Android in versione release. Gli strumenti R8 e ProGuard riducono le dimensioni del DEX, offuscano il codice e rimuovono le classi inutilizzate.
R8 è il successore di ProGuard, integrato in Android Gradle Plugin dal 2019. R8 esegue minimizzazione, offuscamento e ottimizzazione in un unico passaggio, mentre ProGuard richiedeva due fasi: ProGuard → D8. ProGuard è ancora supportato, ma Google raccomanda R8 per i nuovi progetti.
R8 rimuove classi, metodi e campi inutilizzati, li rinomina con nomi brevi (a, b, c), incorpora funzioni inline ed elimina il codice morto. Il risultato — il DEX viene ridotto del 20–40% senza perdita di funzionalità.
La configurazione di R8 viene specificata nel file proguard-rules.pro. Lo sviluppatore può indicare quali classi non possono essere rinominare (ad esempio, per riflessione o serializzazione Gson). Firebase e altri SDK forniscono le proprie regole nelle loro dipendenze.
DEX può essere decompilato in codice Java. Questa è una questione di sicurezza fondamentale per le applicazioni Android: senza offuscamento, il codice viene ripristinato a un livello vicino all'originale.
JADX è il decompilatore da DEX a Java più popolare. Ripristina nomi di classi, metodi, campi e la maggior parte della logica. apktool decompila DEX in codice smali (assembler Dalvik) — una rappresentazione di basso livello vicina alle istruzioni originali. Bytecode Viewer combina più decompilatori in un'unica interfaccia.
L'offuscamento con R8/ProGuard è la prima linea di difesa: i nomi di classi e metodi diventano illeggibili. DexGuard è uno strumento commerciale con metodi aggiuntivi: crittografia delle stringhe, verifica dell'integrità, anti-manomissione. L'offuscamento del flusso di controllo (O-LLVM) modifica la struttura del codice mantenendone la funzionalità, rendendo l'analisi molto più difficile.
Domande frequenti
DEX utilizza un'architettura basata su registri invece della JVM basata su stack, ha un formato più compatto (30% più piccolo), unisce tutti i .class in un unico file con un pool di costanti unificato e utilizza indici a 16 bit invece di 8 bit.
Smali è un assembler per bytecode DEX. Ogni istruzione DEX ha una rappresentazione testuale in formato smali. Lo strumento baksmali converte DEX in smali (disassemblaggio) e smali assembla smali di nuovo in DEX.
Il task Gradle countMethods o il plugin dex-method-counts mostrano il numero di metodi in ogni file DEX. Il comando adb shell con dumpsys mostra anche le statistiche dei DEX caricati per le applicazioni installate.
Sì, sui dispositivi con Android precedente all'8.0, più file DEX rallentano l'avvio dell'applicazione perché ogni file aggiuntivo viene caricato separatamente. Su ART con Android 8.0+, la differenza è minima grazie alla compilazione dex2oat in un singolo file .oat.
Sì, esistono progetti come dexplorer e implementazioni JVM compatibili con Android che possono eseguire bytecode DEX al di fuori di Android. Tuttavia, la maggior parte dei file DEX utilizza l'API Android, rendendoli inadatti all'esecuzione su una JVM standard.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche