DEX (Dalvik Executable) — це формат байт-коду, в який компілюється вихідний код Android-додатків на Java та Kotlin. Файли DEX виконуються віртуальною машиною Dalvik (до Android 4.4) або Android Runtime (ART, починаючи з Android 5.0). За даними Android Open Source Project, 2026, формат DEX забезпечує в середньому 30% більш компактне представлення коду порівняно зі стандартним Java-байткодом JVM.
Головне
DEX (Dalvik Executable) — це формат байт-коду, розроблений спеціально для мобільних пристроїв Android. На відміну від стандартного Java-байт-коду (.class файли), DEX оптимізований для обмежених ресурсів: менше пам'яті, менший розмір та швидше завантаження класів.
Вихідний код на Java або Kotlin компілюється javac/kotlinc у стандартні .class файли (Java bytecode). Потім інструмент d8 (або раніше dx) конвертує .class в один або кілька DEX-файлів. Ця конвертація не проста перепаковка — d8 виконує оптимізації: об'єднує константні пули, перезаписує інструкції в регістрову архітектуру та видаляє дубльовані дані.
DEX використовує регістрову архітектуру (на відміну від стекової JVM). Кожен метод має фіксовану кількість регістрів (до 65536). Інструкції DEX коротші — в середньому 2 байти проти 1–4 байтів у JVM. Це дає більш компактний код: типовий додаток зменшується з 10–15 МБ .class до 4–6 МБ .dex.
Файл DEX має строго визначену бінарну структуру. Кожен файл починається із заголовка та містить кілька секцій, які посилаються одна на одну через зміщення.
| Секція | Призначення |
|---|---|
| header | Заголовок: magic, контрольна сума, підпис, розміри та зміщення секцій |
| string_ids | Таблиця рядків: імена класів, методів, полів |
| type_ids | Типи: посилання на рядкові ідентифікатори типів |
| proto_ids | Прототипи методів: тип, що повертається, та параметри |
| field_ids | Поля класів: клас, тип, ім'я |
| method_ids | Методи: клас, прототип, ім'я |
| class_defs | Визначення класів: прапорці, superclass, інтерфейси, зміщення даних |
| data | Фактичні дані: код методів, анотації, debug info |
Магічне число DEX — `dex\n035\0` (версія 035). Інші версії: 036, 037, 038 (для Android 8.0+). Заголовок розміром 0x70 байт містить контрольну суму SHA-1 та зміщення всіх секцій. Валідація заголовка — перший крок при завантаженні DEX віртуальною машиною.
string_ids, type_ids, proto_ids, field_ids, method_ids — це індексовані таблиці. Замість зберігання повних імен у коді методу використовується 4-байтовий індекс. Це ключова оптимізація: якщо клас згадується 100 разів, його ім'я зберігається один раз у string_ids. dex2oat при компіляції ART додатково оптимізує ці таблиці.
Процес перетворення вихідного коду в DEX складається з кількох етапів. Сучасний ланцюжок використовує компілятор D8, який прийшов на зміну DX у 2018 році з Android Gradle Plugin 3.2.
javac (для Java) або kotlinc (для Kotlin) компілюють вихідний код у .class файли. Кожен клас — окремий .class файл у Java-байткоді. На цьому етапі виконується перевірка типів, генерація bridge-методів та вбудовування констант.
D8 приймає всі .class файли та перетворює їх на DEX-байт-код. D8 виконує кілька оптимізацій: видаляє невикористовувані аргументи методів, об'єднує константні пули з різних .class в один глобальний пул DEX, конвертує стекові інструкції JVM у регістрові інструкції Dalvik.
// Вихідний код Kotlin
data class User(
val name: String,
val email: String
)
fun greet(user: User): String {
return "Hello, ${user.name}!"
}
Після компіляції D8 цей код перетворюється на компактні інструкції DEX: const-string для завантаження рядка, iget-object для доступу до поля об'єкта, invoke-virtual для виклику StringBuilder.append.
D8 працює швидше DX у 2–3 рази, генерує більш компактний DEX (на 5–10%) та краще оптимізує Kotlin-специфічні конструкції (inline функції, lambda). DX оголошено deprecated з 2018 року та видалено з Android Gradle Plugin 8.0.
Виконання DEX-коду в Android пройшло два етапи: оригінальна віртуальна машина Dalvik (Android 2.2–4.4) та Android Runtime ART (Android 5.0+). Різниця в підході до компіляції кардинальна.
Dalvik використовувала Just-In-Time (JIT) компіляцію: DEX-байт-код інтерпретувався, а часто викликані методи компілювалися в нативний код на льоту. Плюс — швидке встановлення. Мінус — повільніший запуск та постійні витрати CPU на JIT.
ART (Android Runtime) компілює DEX у нативний код при встановленні додатку через dex2oat. Це Ahead-Of-Time (AOT) підхід: встановлення довше, але запуск швидший та споживання енергії менше. З Android 7.0 ART використовує гібридний підхід — AOT + JIT + Profile Guided Optimization.
Інструмент dex2oat запускається при встановленні або оновленні додатку. Він компілює DEX у ELF-файл з нативним кодом під архітектуру пристрою. Результат — файли .oat та .art у директорії /data/dalvik-cache/. Google постійно покращує dex2oat: на Android 14 додано оптимізацію для foldable-пристроїв.
Обмеження в 65536 методів на один DEX-файл — legacy від архітектури Dalvik. Поле method_ids у заголовку DEX займає 4 байти, що дає максимум 2^16 = 65536 унікальних посилань. Сучасні додатки з Google Play Services, Firebase та іншими SDK легко перевищують цей ліміт.
Multidex — це механізм розділення коду на кілька DEX-файлів. Головний classes.dex містить точки входу (Application class, основні Activity), інші — classes2.dex, classes3.dex і так далі. При запуску класи з додаткових DEX завантажуються через DexClassLoader.
// build.gradle.kts — включення multidex
android {
defaultConfig {
multiDexEnabled = true
}
}
// Application клас з підтримкою multidex
class MyApp : Application() {
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base)
MultiDex.install(this)
}
}
Завантаження додаткових DEX на етапі старту додатку може викликати ANR (Application Not Responding) на пристроях з Android до 5.0. Рекомендація — використовувати multidex тільки при необхідності та мінімізувати залежності, щоб не перевищувати ліміт.
Оптимізація DEX — стандартний етап збірки релізного Android-додатку. Інструменти R8 та ProGuard зменшують розмір DEX, обфускують код та видаляють невикористовувані класи.
R8 — наступник ProGuard, вбудований в Android Gradle Plugin з 2019 року. R8 виконує мініфікацію, обфускацію та оптимізацію за один прохід, тоді як ProGuard вимагав двох етапів: ProGuard → D8. ProGuard підтримується досі, але Google рекомендує R8 для нових проєктів.
R8 видаляє невикористовувані класи, методи та поля, перейменовує їх у короткі імена (a, b, c), вбудовує inline-функції та викидає dead-код. Результат — DEX зменшується на 20–40% без втрати функціональності.
Конфігурація R8 задається у файлі proguard-rules.pro. Розробник може вказати, які класи не можна перейменовувати (наприклад, для рефлексії або серіалізації Gson). Firebase та інші SDK постачають власні правила у своїх залежностях.
DEX можна декомпілювати назад у Java-код. Це ключове питання безпеки Android-додатків: без обфускації код відновлюється до рівня, близького до вихідного.
JADX — найпопулярніший декомпілятор DEX у Java. Він відновлює імена класів, методи, поля та більшу частину логіки. apktool декомпілює DEX у smali-код (ассемблер Dalvik) — низькорівневе представлення, близьке до вихідних інструкцій. Bytecode Viewer об'єднує кілька декомпіляторів в одному інтерфейсі.
Обфускація R8/ProGuard — перша лінія захисту: імена класів та методів стають нечитабельними. DexGuard — комерційний інструмент з додатковими методами: шифрування рядків, перевірка цілісності, анти-tamper. Обфускація на рівні Control Flow (O-LLVM) змінює структуру коду, зберігаючи його функціональність, але роблячи аналіз набагато складнішим.
Часто задавані питання
DEX використовує регістрову архітектуру замість стекової JVM, має більш компактний формат (на 30% менше), об'єднує всі .class в один файл з єдиним пулом констант та використовує 16-бітні індекси замість 8-бітних.
Smali — це асемблер DEX-байт-коду. Кожна інструкція DEX має текстове представлення у форматі smali. Інструмент baksmali конвертує DEX у smali (дизасемблювання), а smali збирає smali назад у DEX.
Gradle task countMethods або плагін dex-method-counts показують кількість методів у кожному DEX-файлі. Команда adb shell з dumpsys також відображає статистику завантажених DEX для встановлених додатків.
Так, на пристроях до Android 8.0 багаторазовий DEX уповільнює запуск додатку, оскільки кожен додатковий файл завантажується окремо. На ART з Android 8.0+ різниця мінімальна завдяки компіляції dex2oat у єдиний .oat файл.
Так, існують проєкти, такі як dexplorer та Android-сумісні JVM-реалізації, які можуть виконувати DEX-байт-код поза Android. Однак більшість DEX-файлів використовують Android API, що робить їх непридатними для запуску на звичайній JVM.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також