Google berkomitmen untuk mendorong terwujudnya keadilan ras bagi komunitas Kulit Hitam. Lihat caranya.

Mengaktifkan multidex untuk aplikasi dengan lebih dari 6K metode

Ketika aplikasi Anda beserta library yang direferensikannya melebihi 65.536 metode, akan muncul error build yang menunjukkan bahwa aplikasi 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, K, memiliki jumlah 1.024 (atau 2^10). Karena 65.536 sama dengan 64 X 1024, batas ini disebut sebagai 'batas referensi 64K'.

Dukungan multidex sebelum Android 5.0

Versi platform sebelum Android 5.0 (API level 21) menggunakan waktu proses Dalvik untuk mengeksekusi kode aplikasi. Secara default, Dalvik membatasi aplikasi ke satu file bytecode classes.dex per APK. Untuk mengatasi batasan ini, tambahkan support library multidex ke project Anda:

dependencies {
    def multidex_version = "2.0.1"
    implementation 'androidx.multidex:multidex:$multidex_version'
}
   

Untuk melihat versi sekarang library ini, lihat informasi tentang Multidex pada halaman versi.

Jika tidak menggunakan AndroidX, tambahkan dependensi library dukungan berikut ini sebagai gantinya:

dependencies {
  implementation 'com.android.support:multidex:1.0.3'
}

Library ini menjadi bagian dari file DEX utama aplikasi Anda, lalu mengelola akses ke file DEX tambahan dan kode yang dimuatnya. Detail selengkapnya ada di bawah ini, di bagian cara mengonfigurasi aplikasi Anda untuk multidex.

Dukungan multidex untuk Android 5.0 dan versi lebih tinggi

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

Untuk mengetahui informasi selengkapnya tentang waktu proses Android 5.0, baca 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 versi Android 5.0 dan yang lebih baru. 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, Anda harus melakukan langkah-langkah untuk mengurangi jumlah referensi yang dipanggil oleh kode aplikasi, termasuk metode yang didefinisikan oleh kode aplikasi atau library yang disertakan. Strategi berikut ini dapat membantu Anda agar tidak mencapai batas referensi DEX:

  • Tinjau dependensi langsung dan transitif aplikasi - Pastikan setiap dependensi library berukuran besar yang disertakan dalam aplikasi Anda digunakan sedemikian rupa sehingga nilai gunanya melebihi jumlah kode yang ditambahkan ke aplikasi. Antipola umum adalah menyertakan library yang sangat besar karena beberapa metode utilitas sebelumnya berguna. Mengurangi dependensi kode aplikasi sering kali dapat membantu Anda menghindari batas referensi DEX.
  • Hapus kode yang tidak digunakan dengan R8 - Aktifkan penyingkatan kode untuk menjalankan R8 bagi build rilis Anda. Mengaktifkan penyingkatan akan memastikan Anda tidak mengirimkan kode yang tidak digunakan dengan APK Anda.

Penggunaan teknik-teknik ini dapat membantu Anda menghindari keharusan mengaktifkan multidex dalam aplikasi sekaligus mengurangi ukuran keseluruhan APK.

Mengonfigurasi aplikasi untuk multidex

Jika minSdkVersion Anda ditetapkan ke versi 21 atau lebih tinggi, multidex akan diaktifkan secara default dan Anda tidak memerlukan support library multidex.

Namun, jika minSdkVersion Anda ditetapkan ke versi 20 atau lebih rendah, Anda harus menggunakan support library multidex dan melakukan modifikasi berikut untuk project aplikasi Anda:

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

    android {
        defaultConfig {
            ...
            minSdkVersion 15
            targetSdkVersion 28
            multiDexEnabled true
        }
        ...
    }
    
    dependencies {
      implementation 'com.android.support:multidex:1.0.3'
    }
    
  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 Anda untuk menetapkan android:name dalam 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="android.support.multidex.MultiDexApplication" >
              ...
          </application>
      </manifest>
      
    • Jika Anda mengganti class Application, ubah class tersebut untuk memperluas MultiDexApplication (jika memungkinkan) seperti berikut:

      Kotlin

      class MyApplication : MultiDexApplication() {...}
      

      Java

      public class MyApplication extends MultiDexApplication { ... }
      
    • Jika mengganti class Application tetapi tidak memungkinkan untuk mengubah class dasarnya, Anda dapat mengganti metode attachBaseContext() dan memanggil 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 antara 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.

Pada waktu proses, API multidex menggunakan loader class khusus untuk menelusuri semua file DEX yang tersedia bagi metode Anda (tidak hanya menelusuri dalam file classes.dex utama).

Batasan support library multidex

Support library multidex memiliki beberapa batasan umum yang harus Anda ketahui dan uji ketika Anda memasukkannya ke konfigurasi build aplikasi:

  • 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 78035). Batas ini dinaikkan di Android 4.0 (API level 14), tetapi masalah tidak teratasi sepenuhnya. Dan 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.

Hal ini tidak akan terjadi untuk kode yang diakses secara langsung dari kode aplikasi karena alat build mengenali lokasi kode tersebut, tetapi ini dapat terjadi jika jalur kode kurang jelas, misalnya saat library yang digunakan 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.

Jadi, jika Anda menerima java.lang.NoClassDefFoundError, Anda harus menentukan class tambahan berikut ini secara manual seperti yang diperlukan dalam file DEX utama dengan mendeklarasikannya menggunakan properti multiDexKeepFile atau multiDexKeepProguard dalam jenis build Anda. Jika suatu class dicocokkan dalam file multiDexKeepFile atau multiDexKeepProguard, class tersebut akan ditambahkan ke file DEX utama.

Properti multiDexKeepFile

File yang ditentukan dalam multiDexKeepFile harus berisi satu class per baris, dalam format com/example/MyClass.class. Misalnya, Anda dapat membuat file bernama multidex-config.txt yang terlihat seperti ini:

com/example/MyClass.class
com/example/MyOtherClass.class

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

android {
    buildTypes {
        release {
            multiDexKeepFile file('multidex-config.txt')
            ...
        }
    }
}

Perlu diingat bahwa Gradle akan membaca jalur secara relatif terhadap file build.gradle, sehingga contoh di atas akan berfungsi jika multidex-config.txt berada dalam direktori yang sama dengan file build.gradle.

Properti multiDexKeepProguard

File multiDexKeepProguard akan menggunakan format yang sama dengan Proguard dan mendukung seluruh gramatika Proguard. Untuk mengetahui informasi selengkapnya tentang format dan tata bahasa Proguard, lihat bagian Keep Options pada panduan Proguard.

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 bisa mendeklarasikan file tersebut untuk jenis build seperti berikut:

android {
    buildTypes {
        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 kembali menggunakan output multidex antar-build. Pre-dexing mengandalkan format ART yang hanya tersedia di Android 5.0 (API level 21) dan yang lebih tinggi. Jika Anda menggunakan Android Studio 2.3 dan yang lebih tinggi, IDE akan otomatis menggunakan fitur ini saat men-deploy aplikasi Anda ke perangkat yang menjalankan Android 5.0 (API level 21) atau yang lebih tinggi.

Tips: plugin Android untuk Gradle 3.0.0 dan yang lebih tinggi menyertakan peningkatan lebih lanjut untuk mengoptimalkan kecepatan build, seperti dexing per class (sehingga hanya class yang diubah yang di-dex ulang). Secara umum, untuk mendapatkan pengalaman pengembangan terbaik, sebaiknya Anda selalu mengupgrade ke versi terbaru Android Studio dan plugin Android.

Namun, jika menjalankan build Gradle dari command line, Anda perlu menetapkan minSdkVersion ke 21 atau yang lebih tinggi untuk mengaktifkan pre-dexing. Strategi yang dapat membantu mempertahankan setelan untuk build produksi adalah dengan membuat dua versi aplikasi menggunakan ragam produk: ragam pengembangan dan ragam rilis dengan nilai yang berbeda untuk minSdkVersion, seperti yang ditunjukkan di bawah ini.

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 what you set for
            // 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 'com.android.support:multidex:1.0.3'
}

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: Sekarang, setelah memiliki varian build yang berbeda untuk kebutuhan multidex yang berbeda, Anda juga dapat menyediakan file manifes yang berbeda untuk setiap varian (jadi hanya varian untuk API level 20 dan yang lebih rendah yang mengubah nama tag <application>), atau membuat subclass Application yang berbeda untuk setiap varian (jadi hanya varian 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);
  ...
}