Mengaktifkan multidex untuk aplikasi dengan lebih dari 64K metode

Jika aplikasi Anda memiliki minSdk API 20 atau yang lebih rendah dan aplikasi Anda serta library yang direferensikannya melebihi 65.536 metode, Anda akan mengalami error build berikut yang menunjukkan bahwa aplikasi Anda telah mencapai batas arsitektur build Android:

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

Versi sistem build yang lebih lama melaporkan error yang berbeda, yang merupakan indikasi dari masalah yang sama:

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

Kedua kondisi error ini menampilkan angka yang sama: 65536. Angka ini menunjukkan jumlah total referensi yang dapat dipanggil oleh kode dalam satu file bytecode Dalvik Executable (DEX). Halaman ini menjelaskan cara melewati batasan ini dengan mengaktifkan konfigurasi aplikasi yang disebut multidex, yang memungkinkan aplikasi Anda membuat dan membaca beberapa file DEX.

Tentang batas referensi 64K

File aplikasi Android (APK) berisi file bytecode yang dapat dieksekusi dalam bentuk file Dalvik Executable (DEX), berisi kode kompilasi yang digunakan untuk menjalankan aplikasi Anda. Spesifikasi Dalvik Executable akan membatasi jumlah total metode yang dapat direferensikan dalam satu file DEX sebesar 65.536, termasuk metode framework Android, metode library, dan metode dalam kode Anda sendiri.

Dalam konteks ilmu komputer, istilah kilo, atau K, memiliki jumlah 1.024 (atau 2^10). Karena 65.536 sama dengan 64x1024, batas ini disebut sebagai 'batas referensi 64K'.

Dukungan multidex sebelum Android 5.0

Versi platform sebelum Android 5.0 (API level 21) menggunakan runtime Dalvik untuk mengeksekusi kode aplikasi. Secara default, Dalvik membatasi aplikasi ke satu file bytecode classes.dex per APK. Untuk mengatasi batasan ini, tambahkan library multidex ke file build.gradle atau build.gradle.kts level modul:

Groovy

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

Library ini menjadi bagian dari file DEX utama aplikasi Anda, lalu mengelola akses ke file DEX tambahan dan kode yang dimuatnya. Untuk melihat versi terbaru library ini, lihat versi multidex.

Untuk detail selengkapnya, lihat bagian tentang cara mengonfigurasi aplikasi untuk multidex.

Dukungan multidex untuk Android 5.0 dan versi lebih tinggi

Android 5.0 (API level 21) dan yang lebih tinggi menggunakan runtime yang disebut ART, yang secara bawaan mendukung pemuatan beberapa file DEX dari file APK. ART melakukan prakompilasi pada waktu penginstalan aplikasi, memindai file classesN.dex dan mengompilasinya ke dalam satu file OAT untuk dieksekusi oleh perangkat Android. Oleh karena itu, jika minSdkVersion Anda adalah versi 21 atau lebih tinggi, multidex akan diaktifkan secara default dan Anda tidak memerlukan library multidex.

Untuk mengetahui informasi selengkapnya tentang runtime Android 5.0, baca Android Runtime (ART) dan Dalvik.

Catatan: Saat menjalankan aplikasi menggunakan Android Studio, build akan dioptimalkan untuk perangkat target yang digunakan untuk deploy. Ini termasuk mengaktifkan multidex saat perangkat target menjalankan Android versi 5.0 dan yang lebih tinggi. Karena pengoptimalan ini hanya diterapkan saat men-deploy aplikasi menggunakan Android Studio, Anda mungkin masih perlu mengonfigurasi build rilis Anda untuk multidex guna menghindari batas 64K.

Menghindari batas 64K

Sebelum mengonfigurasi aplikasi untuk mengaktifkan penggunaan referensi metode 64K atau lebih, lakukan langkah-langkah untuk mengurangi jumlah referensi yang dipanggil oleh kode aplikasi, termasuk metode yang didefinisikan oleh kode aplikasi atau library yang disertakan.

Strategi berikut bisa membantu Anda agar terhindar dari mencapai batas referensi DEX:

Meninjau dependensi langsung dan transitif aplikasi
Pertimbangkan apakah nilai dependensi library besar yang Anda sertakan dalam aplikasi Anda melebihi jumlah kode yang ditambahkan ke aplikasi. Pola yang umum tetapi bermasalah adalah menyertakan library yang sangat besar karena beberapa metode utilitas berguna. Mengurangi dependensi kode aplikasi sering kali dapat membantu Anda menghindari batas referensi DEX.
Menghapus kode yang tidak digunakan dengan R8
Aktifkan penyingkatan kode untuk menjalankan R8 bagi build rilis Anda. Aktifkan penyingkatan untuk membantu memastikan Anda tidak mengirimkan kode yang tidak digunakan dengan APK Anda. Jika penyingkatan kode dikonfigurasi dengan benar, penyingkatan kode juga dapat menghapus kode dan resource yang tidak digunakan dari dependensi Anda.

Penggunaan teknik ini dapat membantu Anda mengurangi ukuran APK secara keseluruhan dan menghindari penggunaan multidex dalam aplikasi.

Mengonfigurasi aplikasi untuk multidex

Catatan: Jika minSdkVersion disetel ke 21 atau yang lebih tinggi, multidex akan diaktifkan secara default dan Anda tidak memerlukan library multidex.

Jika minSdkVersion Anda disetel ke 20 atau lebih rendah, Anda harus menggunakan library multidex dan melakukan modifikasi berikut untuk project aplikasi Anda:

  1. Ubah file build.gradle level modul untuk mengaktifkan multidex dan tambahkan library multidex sebagai dependensi seperti berikut:

    Groovy

    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. Bergantung pada apakah Anda mengganti class Application atau tidak, lakukan salah satu hal berikut ini:
    • Jika Anda tidak mengganti class Application, edit file manifes untuk menetapkan android:name di tag <application> seperti berikut:

      <?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>
    • Jika Anda mengganti class Application, ubah class untuk memperluas MultiDexApplication, seperti berikut:

      Kotlin

      class MyApplication : MultiDexApplication() {...}

      Java

      public class MyApplication extends MultiDexApplication { ... }
    • Jika Anda mengganti class Application tetapi tidak memungkinkan untuk mengubah class dasarnya, gantilah metode attachBaseContext() dan panggil MultiDex.install(this) untuk mengaktifkan 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);
        }
      }

      Perhatian: Jangan jalankan MultiDex.install() atau kode lainnya melalui refleksi atau JNI sebelum MultiDex.install() selesai. Pelacakan multidex tidak akan mengikuti panggilan tersebut, sehingga mengakibatkan ClassNotFoundException atau error verifikasi karena partisi class yang buruk antar-file DEX.

Sekarang, saat Anda membuat aplikasi, alat build Android akan membuat file DEX utama (classes.dex) dan file DEX pendukung (classes2.dex, classes3.dex, dan seterusnya) sesuai kebutuhan. Sistem build kemudian akan memaketkan semua file DEX ke APK Anda.

Saat runtime, alih-alih hanya menelusuri di file classes.dex utama, API multidex menggunakan loader class khusus untuk menelusuri semua file DEX yang tersedia untuk metode Anda.

Batasan library multidex

Library multidex memiliki beberapa batasan umum. Saat Anda menyertakan library ke dalam konfigurasi build aplikasi, pertimbangkan hal berikut:

  • Penginstalan file DEX selama startup ke partisi data perangkat adalah proses yang kompleks dan dapat mengakibatkan error Aplikasi Tidak Merespons (ANR) jika file DEX sekunder berukuran besar. Untuk menghindari masalah ini, aktifkan penyingkatan kode guna meminimalkan ukuran file DEX dan menghapus bagian kode yang tidak terpakai.
  • Jika menjalankan pada versi sebelum Android 5.0 (API level 21), penggunaan multidex tidaklah cukup untuk mengatasi batasan linearalloc (masalah 37008143). Batas ini ditingkatkan di Android 4.0 (API level 14), tetapi masalah tidak teratasi sepenuhnya.

    Pada versi yang lebih rendah dari Android 4.0, Anda mungkin mencapai batas linearalloc sebelum mencapai batas indeks DEX. Jadi, jika Anda menargetkan API level di bawah 14, lakukan pengujian menyeluruh pada versi platform tersebut karena aplikasi Anda mungkin memiliki masalah saat startup atau ketika grup class tertentu dimuat.

    Penyingkatan kode dapat mengurangi atau mungkin menghilangkan masalah-masalah ini.

Mendeklarasikan class yang diperlukan dalam file DEX utama

Saat membuat setiap file DEX untuk aplikasi multidex, alat build akan melakukan pengambilan keputusan yang kompleks guna menentukan class mana yang diperlukan dalam file DEX utama agar aplikasi Anda berhasil dimulai. Jika class yang diperlukan selama startup tidak disediakan dalam file DEX utama, aplikasi akan berhenti bekerja dan menampilkan error java.lang.NoClassDefFoundError.

Alat build mengenali jalur kode untuk kode yang diakses langsung dari kode aplikasi Anda. Namun, masalah ini dapat terjadi saat jalur kode kurang terlihat, misalnya saat library yang Anda gunakan memiliki dependensi yang kompleks. Contohnya, jika kode tersebut menggunakan introspeksi atau pemanggilan metode Java dari kode native, class tersebut mungkin tidak akan dikenali sebagaimana diperlukan dalam file DEX utama.

Jika menerima java.lang.NoClassDefFoundError, Anda harus menentukan secara manual class tambahan yang diperlukan dalam file DEX utama dengan mendeklarasikannya menggunakan properti multiDexKeepProguard dalam jenis build Anda. Jika class cocok dengan file multiDexKeepProguard, class tersebut akan ditambahkan ke file DEX utama.

Properti multiDexKeepProguard

File multiDexKeepProguard menggunakan format yang sama dengan ProGuard dan mendukung seluruh tata bahasa ProGuard. Untuk informasi selengkapnya tentang cara menyesuaikan apa yang dipertahankan di aplikasi Anda, lihat Menyesuaikan kode yang perlu dipertahankan.

File yang ditentukan dalam multiDexKeepProguard harus berisi opsi -keep dalam setiap sintaksis ProGuard yang valid. Misalnya, -keep com.example.MyClass.class. Anda dapat membuat file bernama multidex-config.pro yang terlihat seperti ini:

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

Jika ingin menetapkan semua class dalam satu paket, filenya akan terlihat seperti ini:

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

Kemudian, Anda dapat mendeklarasikan file tersebut untuk jenis build seperti berikut:

Groovy

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

Kotlin

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

Mengoptimalkan multidex dalam build pengembangan

Konfigurasi multidex memerlukan lebih banyak waktu proses build yang signifikan karena sistem build harus membuat keputusan kompleks tentang class yang harus disertakan dalam file DEX utama dan class yang dapat disertakan dalam file DEX sekunder. Ini berarti bahwa build inkremental yang menggunakan multidex biasanya memerlukan waktu lebih lama dan berpotensi memperlambat proses pengembangan Anda.

Untuk mengurangi waktu build tambahan yang lebih lama, gunakan pre-dexing untuk menggunakan kembali output multidex antar-build. Pre-dexing mengandalkan format ART yang hanya tersedia di Android 5.0 (API level 21) dan yang lebih baru. Jika Anda menggunakan Android Studio, IDE akan otomatis menggunakan pre-dexing saat men-deploy aplikasi ke perangkat yang menjalankan Android 5.0 (API level 21) atau yang lebih tinggi. Namun, jika menjalankan build Gradle dari command line, Anda perlu menetapkan minSdkVersion ke 21 atau yang lebih tinggi untuk mengaktifkan pre-dexing.

Untuk mempertahankan setelan untuk build produksi, Anda dapat membuat dua versi aplikasi menggunakan ragam produk—satu versi dengan ragam pengembangan dan satu versi dengan ragam rilis—dengan nilai yang berbeda untuk minSdkVersion, seperti yang ditunjukkan:

Groovy

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

Untuk mempelajari lebih lanjut strategi guna membantu meningkatkan kecepatan build dari Android Studio atau command line, baca Mengoptimalkan kecepatan build Anda. Untuk mengetahui informasi selengkapnya tentang penggunaan varian build, baca Mengonfigurasi varian build.

Tips: Jika memiliki varian build yang berbeda untuk kebutuhan multidex yang berbeda, Anda dapat menyediakan file manifes yang berbeda untuk setiap varian sehingga hanya file untuk API level 20 dan yang lebih rendah yang mengubah nama tag <application>. Anda juga dapat membuat subclass Application yang berbeda untuk setiap varian sehingga hanya subclass untuk API level 20 dan yang lebih rendah yang memperluas class MultiDexApplication atau memanggil MultiDex.install(this).

Menguji aplikasi multidex

Saat menulis uji instrumentasi untuk aplikasi multidex, tidak ada konfigurasi tambahan yang diperlukan jika Anda menggunakan instrumentasi MonitoringInstrumentation atau AndroidJUnitRunner. Jika menggunakan Instrumentation lain, Anda harus mengganti metode onCreate()-nya dengan kode berikut ini:

Kotlin

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

Java

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