Memverifikasi perilaku aplikasi pada Android runtime (ART)

Android Runtime (ART) adalah runtime default untuk perangkat yang menjalankan Android 5.0 (API level 21) dan yang lebih tinggi. Runtime ini menawarkan sejumlah fitur yang meningkatkan kinerja dan kelancaran aplikasi serta platform Android. Anda bisa menemukan informasi selengkapnya tentang fitur baru ART dalam Memperkenalkan ART.

Akan tetapi, beberapa teknik yang berfungsi di Dalvik tidak berfungsi di ART. Dokumen ini memungkinkan Anda mengetahui tentang hal-hal yang harus diperhatikan saat migrasi aplikasi yang ada agar kompatibel dengan ART. Kebanyakan aplikasi akan berfungsi begitu saja saat dijalankan bersama ART.

Menangani Masalah Pengumpulan Sampah (GC)

Pada Dalvik, aplikasi sering kali akan merasakan gunanya memanggil System.gc() secara eksplisit untuk mengonfirmasi pengumpulan sampah (GC). Hal ini akan menjadi kurang begitu dibutuhkan pada ART, khususnya jika Anda memanggil pengumpulan sampah untuk mencegah terjadinya GC bertipe GC_FOR_ALLOC atau untuk mengurangi fragmentasi. Anda bisa memverifikasi runtime mana yang digunakan dengan memanggil System.getProperty("java.vm.version"). Jika ART sedang digunakan, nilai properti adalah "2.0.0" atau yang lebih tinggi.

Lebih jauh, pengumpul sampah pemadatan sedang dalam tahap pengembangan di Android Open-Source Project (AOSP) untuk meningkatkan manajemen memori. Karenanya, Anda harus menghindari penggunaan teknik-teknik yang tidak kompatibel dengan GC pemadatan (misalnya menyimpan pointer ke data instance objek). Ini khususnya penting bagi aplikasi yang menggunakan Java Native Interface (JNI). Untuk informasi selengkapnya, lihat Mencegah Masalah JNI.

Mencegah Masalah JNI

JNI ART kadang-kadang lebih ketat daripada JNI Dalvik. Ini adalah ide yang bagus sekali untuk menggunakan mode CheckJNI untuk menangkap masalah umum. Jika aplikasi Anda menggunakan kode C/C++, Anda harus meninjau artikel berikut:

Men-debug Android JNI dengan CheckJNI

Memeriksa kode JNI untuk masalah pengumpulan sampah

ART memiliki pengumpul sampah pemadatan yang sedang dalam tahap pengembangan di Android Open Source Project (AOSP). Jika pengumpul sampah pemadatan digunakan, objek mungkin akan dipindah di memori. Jika Anda menggunakan kode C/C++, jangan lakukan operasi yang tidak kompatibel dengan GC pemadatan. Kami telah menyempurnakan CheckJNI untuk mengidentifikasi beberapa masalah potensial (seperti yang dijelaskan dalam Perubahan Referensi Lokal JNI dalam ICS).

Satu area yang harus diperhatikan secara khusus adalah penggunaan fungsi Get...ArrayElements() dan Release...ArrayElements(). Pada runtime yang memiliki GC non-pemadatan, fungsi Get...ArrayElements() biasanya mengembalikan referensi ke memori sesungguhnya yang mendukung objek larik. Jika Anda membuat perubahan pada salah satu elemen larik yang dikembalikan, objek larik tersebut dengan sendirinya berubah (dan argumen ke Release...ArrayElements() biasanya diabaikan). Akan tetapi, jika GC pemadatan digunakan, fungsi Get...ArrayElements() mungkin akan mengembalikan salinan memori. Jika Anda menyalahgunakan referensi saat GC pemadatan sedang digunakan, ini bisa menyebabkan korupsi memori atau masalah lainnya. Sebagai contoh:

  • Jika Anda membuat perubahan pada elemen larik yang dikembalikan, Anda harus memanggil fungsi Release...ArrayElements() yang sesuai jika Anda sudah selesai, demi memastikan perubahan yang Anda buat telah disalin kembali dengan benar ke objek larik sumbernya.
  • Jika Anda merilis elemen larik memori, Anda harus menggunakan mode yang sesuai, bergantung pada perubahan yang Anda buat:
    • Jika Anda tidak membuat perubahan apa pun pada elemen larik, gunakan mode JNI_ABORT, yang merilis memori tanpa menyalin perubahan kembali ke objek larik sumbernya.
    • Jika Anda membuat perubahan pada larik, dan tidak memerlukan referensi lagi, gunakan kode 0 (yang memperbarui objek larik dan membebaskan salinan memori).
    • Jika Anda membuat perubahan pada larik yang ingin Anda ikat, dan ingin mempertahankan salinan larik, gunakan JNI_COMMIT (yang memperbarui objek larik sumbernya dan mempertahankan salinannya).
  • Jika Anda memanggil Release...ArrayElements(), akan mengembalikan pointer yang sama dengan yang semula dikembalikan oleh Get...ArrayElements(). Misalnya, tidak aman menambah pointer asal (untuk memindai melalui elemen larik yang dikembalikan) kemudian meneruskan pointer yang telah ditambahi ke Release...ArrayElements(). Meneruskan pointer yang telah dimodifikasi ini bisa menyebabkan salah membebaskan memori, sehingga mengakibatkan korupsi memori.

Penanganan kesalahan

JNI ART akan melontarkan kesalahan dalam sejumlah kasus sedangkan Dalvik tidak demikian. (Sekali lagi, Anda bisa menangkap banyak kasus demikian dengan mengujinya menggunakan CheckJNI.)

Misalnya, jika RegisterNatives dipanggil dengan metode yang tidak ada (mungkin karena metode tersebut telah dihapus oleh fitur seperti ProGuard), ART kini akan melontarkan NoSuchMethodError dengan benar:

08-12 17:09:41.082 13823 13823 E AndroidRuntime: FATAL EXCEPTION: main
08-12 17:09:41.082 13823 13823 E AndroidRuntime: java.lang.NoSuchMethodError:
    no static or non-static method
    "Lcom/foo/Bar;.native_frob(Ljava/lang/String;)I"
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.Runtime.nativeLoad(Native Method)
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.Runtime.doLoad(Runtime.java:421)
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.Runtime.loadLibrary(Runtime.java:362)
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.System.loadLibrary(System.java:526)

ART juga akan mencatat kesalahan (terlihat di logcat) jika RegisterNatives dipanggil tanpa metode:

W/art     ( 1234): JNI RegisterNativeMethods: attempt to register 0 native
methods for <classname>

Selain itu, fungsi JNI GetFieldID() dan GetStaticFieldID() kini melontarkan NoSuchFieldError dengan benar, alih-alih mengembalikan null. Demikian pula, GetMethodID() dan GetStaticMethodID() kini melontarkan NoSuchMethodError dengan benar. Ini bisa menyebabkan CheckJNI gagal karena pengecualian yang tidak tertangani atau pengecualian dilontarkan ke pemanggil Java kode asli. Ini membuatnya sangat penting untuk menguji aplikasi yang kompatibel dengan ART menggunakan mode CheckJNI.

ART mengharapkan pengguna metode CallNonvirtual...Method() JNI (misalnya CallNonvirtualVoidMethod()) untuk menggunakan kelas pendeklarasi metode, bukan subkelas, sebagaimana diharuskan oleh spesifikasi JNI.

Mencegah masalah ukuran tumpukan

Dalvik memiliki tumpukan terpisah untuk kode asli dan Java, dengan ukuran tumpukan Java default 32 KB dan ukuran tumpukan asli default 1 MB. ART memiliki tumpukan terpadu demi lokalitas yang lebih baik. Umumnya, ukuran tumpukan Thread ART kurang lebih sama dengan Dalvik. Namun, jika Anda secara eksplisit menyetel ukuran tumpukan, Anda mungkin perlu mempertimbangkan kembali nilai-nilai tersebut untuk aplikasi yang berjalan di ART.

  • Di Java, tinjau panggilan ke konstruktor Thread yang menetapkan ukuran tumpukan eksplisit. Misalnya, Anda nanti perlu menambah ukuran jika StackOverflowError terjadi.
  • Di C/C++, tinjau penggunaan pthread_attr_setstack() dan pthread_attr_setstacksize() untuk thread yang juga menjalankan kode Java melalui JNI. Inilah contoh kesalahan yang tercatat dalam log bila aplikasi berusaha memanggil JNI AttachCurrentThread() jika ukuran pthread terlalu kecil:
    F/art: art/runtime/thread.cc:435]
        Attempt to attach a thread with a too-small stack (16384 bytes)

Perubahan model objek

Dalvik salah mengizinkan subclass untuk menimpa metode pribadi paket. ART mengeluarkan peringatan dalam beberapa kasus seperti:

Before Android 4.1, method void com.foo.Bar.quux()
would have incorrectly overridden the package-private method in
com.quux.Quux

Jika Anda bermaksud mengganti metode class dalam paket berbeda, deklarasikan metode sebagai public atau protected.

Object kini memiliki kolom pribadi. Aplikasi yang mencerminkan kolom dalam hierarki kelasnya harus berhati-hati agar tidak berusaha melihat kolom Object. Misalnya, jika Anda mengulang hierarki kelas sebagai bagian framework serialisasi, hentikan jika

Class.getSuperclass() == java.lang.Object.class

sebagai ganti melanjutkannya hingga metode tersebut mengembalikan null.

Proxy InvocationHandler.invoke() kini menerima null jika tidak ada argumen sebagai ganti larik kosong. Perilaku ini sebelumnya telah didokumentasikan namun tidak ditangani dengan benar di Dalvik. Mockito versi sebelumnya mengalami kesulitan dengan hal ini, jadi gunakan versi Mockito yang telah diperbarui saat menguji dengan ART.

Memperbaiki masalah kompilasi AOT

Kompilasi Java Ahead-Of-Time (AOT) di ART seharusnya berhasil untuk semua kode Java standar. Kompilasi dilakukan oleh fitur dex2oat ART; jika Anda mengalami masalah yang berkaitan dengan dex2oat pada waktu penginstalan, beri tahu kami (lihat Melaporkan Masalah) jadi kami bisa memperbaikinya secepat mungkin. Dua masalah yang harus diperhatikan:

  • ART melakukan verifikasi bytecode pada waktu pemasangan yang lebih ketat daripada Dalvik. Kode yang dihasilkan oleh alat pembangun Android seharusnya berhasil. Namun, sebagian fitur pascapemrosesan (khususnya fitur yang melakukan pengaburan) mungkin menghasilkan file tidak valid yang ditoleransi oleh Dalvik namun ditolak oleh ART. Kami bekerja sama dengan vendor fitur untuk menemukan dan memperbaiki masalah seperti itu. Di banyak kasus, mendapatkan versi terbaru fitur Anda dan membuat kembali file DEX bisa memperbaiki masalah-masalah ini.
  • Sebagian masalah umum yang telah ditandai oleh pemverifikasi ART antara lain:
    • alur kontrol tidak valid
    • moniterenter/moniterexit yang tidak seimbang
    • ukuran daftar tipe parameter dengan panjang 0
  • Sebagian aplikasi memiliki dependensi pada format file .odex yang terinstal di /system/framework, /data/dalvik-cache, atau dalam direktori keluaran yang dioptimalkan DexClassLoader. Semua file ini sekarang adalah file ELF dan bukan bentuk perluasan dari file DEX. Selagi ART mencoba mengikuti penamaan dan aturan penguncian yang sama dengan Dalvik, aplikasi tidak boleh bergantung pada format file; format tersebut dapat berubah tanpa pemberitahuan.

    Catatan: Di Android 8.0 (API level 26) dan yang lebih tinggi, direktori keluaran yang dioptimalkan DexClassLoader tidak digunakan lagi. Untuk informasi selengkapnya, lihat dokumentasi untuk konstruktor DexClassLoader().

Melaporkan masalah

Jika Anda mengalami masalah yang bukan diakibatkan oleh masalah JNI aplikasi, laporkan masalah tersebut melalui Android Open Source Project Issue Tracker di https://code.google.com/p/android/issues/list. Sertakan "adb bugreport" dan tautkan ke aplikasi di Google Play Store jika tersedia. Atau, jika memungkinkan, lampirkan APK yang mereproduksi masalah tersebut. Perhatikan masalahnya (termasuk lampiran) yang bersifat bisa dilihat secara umum.