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 bisa 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 bisa dieksekusi dalam bentuk file Dalvik Executable (DEX), berisi kode kompilasi yang digunakan untuk menjalankan aplikasi Anda. Spesifikasi Dalvik Executable membatasi jumlah total metode yang bisa direferensikan dalam file DEX tunggal sebesar 65.536, termasuk metode framework Android, metode library, dan metode dalam kode Anda sendiri. Dalam konteks ilmu komputer, istilah Kilo, K, memiliki jumlah 1024 (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 menyiasati batasan ini, tambahkan library dukungan multidex ke project Anda:

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

Untuk melihat versi saat ini untuk 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 tentang cara mengonfigurasi aplikasi Anda untuk multidex.

Dukungan multidex untuk Android 5.0 dan versi lebih tinggi

Android 5.0 (API level 21) dan versi lebih tinggi menggunakan waktu proses yang disebut ART, yang secara bawaan mendukung pemuatan beberapa file DEX dari file APK. ART melakukan prakompilasi pada waktu penginstalan aplikasi yang memindai file classesN.dex dan mengompilasinya dalam satu file .oat untuk eksekusi oleh perangkat Android. Oleh karena itu, jika versi minSdkVersion Anda adalah 21 atau lebih tinggi, multidex akan diaktifkan secara default, dan Anda tidak memerlukan library dukungan 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 penerapan. Ini termasuk mengaktifkan multidex saat perangkat target menjalankan versi Android 5.0 dan yang lebih baru. Karena pengoptimalan ini hanya diterapkan saat menerapkan aplikasi menggunakan Android Studio, Anda mungkin masih perlu mengonfigurasi rilis build 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 bisa membantu Anda agar tidak mencapai batas referensi DEX:

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

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 diatur ke versi 21 atau lebih tinggi, multidex akan diaktifkan secara default dan Anda tidak memerlukan library dukungan multidex.

Namun, jika minSdkVersion Anda diatur ke versi 20 atau lebih rendah, Anda harus menggunakan library dukungan 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 ini:

        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 tidak mengganti class Application, edit file manifes Anda agar menyetel 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 agar memperluas MultiDexApplication (jika memungkinkan) seperti berikut:

      Kotlin

          class MyApplication : MultiDexApplication() {...}
          

      Java

          public class MyApplication extends MultiDexApplication { ... }
          
    • Atau, jika Anda mengganti class Application tetapi kondisi saat ini tidak memungkinkan untuk mengubah class dasarnya, Anda bisa 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, mengakibatkan ClassNotFoundException atau error verifikasi karena partisi class yang buruk antara file DEX.

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

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

Batasan library dukungan multidex

Dukungan library multidex memiliki beberapa batasan umum yang harus Anda sadari dan uji ketika Anda memasukannya ke konfigurasi build aplikasi:

  • Penginstalan file DEX selama startup ke partisi data perangkat adalah proses yang kompleks dan bisa mengakibatkan error Aplikasi Tidak Merespons (ANR) jika file DEX sekunder berukuran besar. Dalam hal ini, Anda harus menerapkan penciutan kode dengan ProGuard untuk meminimalkan ukuran file DEX dan menghapus bagian kode yang tidak terpakai.
  • Saat menjalankan 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 level API di bawah 14, lakukan pengujian menyeluruh pada versi platform tersebut karena aplikasi Anda mungkin memiliki masalah saat startup atau ketika grup class tertentu dimuat.

    Penciutan kode bisa mengurangi atau mungkin menghilangkan masalah-masalah potensial ini.

Mendeklarasikan class yang diperlukan dalam file DEX utama

Saat membuat setiap file DEX untuk aplikasi multidex, fitur build akan melakukan pengambilan keputusan yang kompleks guna menentukan class mana yang dibutuhkan dalam file DEX primer sehingga aplikasi Anda bisa berhasil berjalan. Jika class yang dibutuhkan selama startup tidak disediakan dalam file DEX primer, 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 fitur build mengenali lokasi kode tersebut, tetapi ini bisa 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 asli, maka class tersebut mungkin tidak akan dikenali sebagaimana diperlukan dalam file DEX primer.

Jadi, jika Anda menjumpai java.lang.NoClassDefFoundError, tentukan class tambahan 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 file multiDexKeepProguard, class tersebut akan ditambahkan ke file DEX primer.

Properti multiDexKeepFile

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

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

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

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

Ingat bahwa Gradle membaca lokasi secara relatif terhadap file build.gradle, jadi contoh di atas akan berfungsi jika multidex-config.txt berada dalam direktori yang sama dengan file build.gradle.

Properti multiDexKeepProguard

File multiDexKeepProguard menggunakan format yang sama dengan Proguard dan mendukung seluruh gramatika Proguard. Untuk mengetahui informasi selengkapnya tentang format dan gramatika Proguard, lihat bagian Mempertahankan Opsi dalam manual Proguard.

File yang ditentukan dalam multiDexKeepProguard harus berisi opsi -keep dalam setiap sintaksis ProGuard yang valid. Misalnya, -keep com.example.MyClass.class. Anda bisa 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 primer dan class yang bisa disertakan dalam file DEX sekunder. Ini berarti bahwa build bertahap 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 lebih tinggi.

Tips: Android plugin untuk Gradle 3.0.0 dan versi 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, Anda harus selalu meng-upgrade ke versi terbaru Android Studio dan Android plugin.

Namun, jika Anda menjalankan build Gradle dari command line, Anda perlu menyetel minSdkVersion ke versi 21 atau yang lebih tinggi untuk mengaktifkan pre-dexing. Strategi yang bisa membantu mempertahankan setelan untuk build produksi adalah 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 banyak 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, lihat Mengonfigurasi Varian Build.

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

Menguji aplikasi multidex

Saat menulis pengujian instrumentasi untuk aplikasi multidex, tidak ada konfigurasi tambahan yang diperlukan jika Anda menggunakan instrumentasi MonitoringInstrumentation (atau instrumentasi AndroidJUnitRunner). Jika menggunakan Instrumentation lain, Anda harus mengganti metode onCreate() miliknya 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);
      ...
    }