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 performa dan kelancaran platform dan aplikasi Android. Anda dapat menemukan informasi selengkapnya tentang fitur baru ART dalam Memperkenalkan ART.

Akan tetapi, beberapa teknik yang berfungsi di Dalvik tidak berfungsi di ART. Dokumen ini memberi tahu Anda tentang hal-hal yang harus diperhatikan saat memigrasikan aplikasi yang ada agar kompatibel dengan ART. Sebagian besar aplikasi akan berfungsi begitu saja saat dijalankan bersama ART.

Menangani Masalah Pengumpulan Sampah (GC)

Pada Dalvik, aplikasi sering kali perlu memanggil System.gc() secara eksplisit untuk meminta pembersihan sampah memori (GC). Hal ini akan menjadi jauh lebih tidak diperlukan dengan ART, terutama jika Anda memanggil pembersihan sampah memori untuk mencegah kemunculan jenis GC_FOR_ALLOC atau untuk mengurangi fragmentasi. Anda dapat memverifikasi runtime mana yang sedang digunakan dengan memanggil System.getProperty("java.vm.version"). Jika ART sedang digunakan, nilai properti adalah "2.0.0" atau lebih tinggi.

ART menggunakan kolektor Concurrent Menyalin (CC) yang secara serentak memadatkan heap Java. Karena itu, Anda harus menghindari penggunaan teknik yang tidak kompatibel dengan GC pemadatan (seperti menyimpan pointer ke data instance objek). Hal ini sangat penting bagi aplikasi yang menggunakan Java Native Interface (JNI). Untuk mengetahui informasi selengkapnya, lihat Mencegah Masalah JNI.

Mencegah Masalah JNI

JNI ART agak lebih ketat daripada JNI Dalvik. Menggunakan mode CheckJNI lebih baik 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

Kolektor Concurrent Menyalin (CC) dapat memindahkan objek dalam memori untuk pemadatan. Jika Anda menggunakan kode C/C++, jangan lakukan operasi yang tidak kompatibel dengan GC pemadatan. Kami telah meningkatkan CheckJNI untuk mengidentifikasi beberapa potensi masalah (seperti yang dijelaskan dalam Perubahan Referensi Lokal JNI di ICS).

Satu area yang harus diperhatikan secara khusus adalah penggunaan fungsi Get...ArrayElements() dan Release...ArrayElements(). Pada runtime dengan GC non-pemadatan, fungsi Get...ArrayElements() biasanya menampilkan referensi ke memori sebenarnya yang mendukung objek array. Jika Anda melakukan perubahan pada salah satu elemen array yang ditampilkan, objek array itu sendiri akan berubah (dan argumen ke Release...ArrayElements() biasanya diabaikan). Namun, jika GC pemadatan digunakan, fungsi Get...ArrayElements() dapat menampilkan salinan memori. Jika Anda menyalahgunakan referensi saat GC pemadatan sedang digunakan, hal ini dapat menyebabkan kerusakan memori atau masalah lainnya. Contoh:

  • Jika membuat perubahan pada elemen array yang ditampilkan, Anda harus memanggil fungsi Release...ArrayElements() yang sesuai setelah selesai, untuk memastikan perubahan yang Anda buat telah disalin kembali dengan benar ke objek array yang mendasarinya.
  • Saat merilis elemen array memori, Anda harus menggunakan mode yang sesuai, bergantung pada perubahan yang Anda buat:
    • Jika Anda tidak membuat perubahan apa pun pada elemen array, gunakan mode JNI_ABORT, yang merilis memori tanpa menyalin perubahan kembali ke objek array yang mendasarinya.
    • Jika Anda membuat perubahan pada array, dan tidak memerlukan referensi lagi, gunakan kode 0 (yang memperbarui objek array dan membebaskan salinan memori).
    • Jika Anda membuat perubahan pada array yang ingin di-commit, dan ingin menyimpan salinan array, gunakan JNI_COMMIT (yang mengupdate objek array yang mendasarinya dan mempertahankan salinannya).
  • Saat Anda memanggil Release...ArrayElements(), tampilkan pointer yang sama dengan yang awalnya ditampilkan oleh Get...ArrayElements(). Misalnya, tidak aman untuk menambah pointer asli (untuk memindai melalui elemen array yang ditampilkan), lalu meneruskan pointer yang bertambah ke Release...ArrayElements(). Meneruskan pointer yang telah dimodifikasi ini dapat menyebabkan salah membebaskan memori, sehingga mengakibatkan kerusakan memori.

Penanganan error

JNI ART melempar error dalam sejumlah kasus, sedangkan Dalvik tidak demikian. (Sekali lagi, Anda dapat menangkap banyak kasus demikian dengan mengujinya menggunakan CheckJNI.)

Misalnya, jika RegisterNatives dipanggil dengan metode yang tidak ada (mungkin karena metode tersebut telah dihapus oleh alat seperti ProGuard), ART kini akan menampilkan 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 error (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 menampilkan NoSuchFieldError dengan benar, bukan hanya menampilkan null. Demikian pula, GetMethodID() dan GetStaticMethodID() kini akan menampilkan NoSuchMethodError dengan benar. Hal ini dapat menyebabkan CheckJNI gagal karena pengecualian yang tidak tertangani atau pengecualian ditampilkan ke pemanggil Java kode native. Hal ini membuatnya sangat penting untuk menguji aplikasi yang kompatibel dengan ART menggunakan mode CheckJNI.

ART mengharapkan pengguna metode CallNonvirtual...Method() JNI (seperti CallNonvirtualVoidMethod()) menggunakan class deklarasi metode, bukan subclass, seperti yang diwajibkan oleh spesifikasi JNI.

Mencegah masalah ukuran tumpukan

Dalvik memiliki stack terpisah untuk kode native dan Java, dengan ukuran stack Java default sebesar 32 KB dan ukuran stack native default sebesar 1 MB. ART memiliki tumpukan terpadu untuk lokalitas yang lebih baik. Biasanya, ukuran stack Thread ART kurang lebih sama dengan Dalvik. Namun, jika Anda secara eksplisit menetapkan ukuran tumpukan, Anda mungkin perlu meninjau kembali nilai tersebut untuk aplikasi yang berjalan di ART.

  • Di Java, tinjau panggilan ke konstruktor Thread yang menentukan ukuran stack eksplisit. Misalnya, Anda perlu meningkatkan ukuran jika StackOverflowError terjadi.
  • Pada C/C++, tinjau penggunaan pthread_attr_setstack() dan pthread_attr_setstacksize() untuk thread yang juga menjalankan kode Java melalui JNI. Berikut adalah contoh error yang dicatat ke dalam log saat aplikasi mencoba 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 ingin mengganti metode class dalam paket lain, deklarasikan metode sebagai public atau protected.

Object kini memiliki kolom pribadi. Aplikasi yang mencerminkan kolom dalam hierarki class-nya harus berhati-hati agar tidak mencoba melihat kolom Object. Misalnya, jika Anda melakukan iterasi hierarki class sebagai bagian dari framework serialisasi, hentikan saat

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

bukan melanjutkan hingga metode menampilkan null.

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

Memperbaiki masalah kompilasi AOT

Kompilasi Java Ahead-Of-Time (AOT) di ART seharusnya berfungsi untuk semua kode Java standar. Kompilasi dilakukan oleh alat dex2oat ART. Jika Anda mengalami masalah terkait dex2oat pada waktu penginstalan, beri tahu kami (lihat Melaporkan Masalah) agar kami dapat 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, beberapa fitur pascapemrosesan (terutama fitur yang melakukan obfuscation) dapat menghasilkan file tidak valid yang ditoleransi oleh Dalvik tetapi ditolak oleh ART. Kami telah bekerja sama dengan vendor alat untuk menemukan dan memperbaiki masalah tersebut. Dalam banyak kasus, mendapatkan versi terbaru alat dan membuat ulang file DEX dapat memperbaiki masalah ini.
  • Beberapa masalah umum yang ditandai oleh pemverifikasi ART meliputi:
    • alur kontrol tidak valid
    • monitorenter/monitorexit tidak seimbang
    • ukuran daftar tipe parameter dengan panjang 0
  • Beberapa aplikasi memiliki dependensi pada format file .odex yang diinstal di /system/framework, /data/dalvik-cache, atau di direktori output yang dioptimalkan DexClassLoader. Semua file ini sekarang adalah file ELF dan bukan bentuk perluasan dari file DEX. Meskipun ART mencoba mengikuti penamaan dan aturan penguncian yang sama dengan Dalvik, aplikasi tidak boleh bergantung pada format file; format dapat berubah tanpa pemberitahuan.

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

Melaporkan masalah

Jika Anda mengalami masalah yang bukan karena masalah JNI aplikasi, laporkan masalah tersebut melalui Issue Tracker Project Open Source Android di https://code.google.com/p/android/issues/list. Sertakan "adb bugreport" dan link ke aplikasi di Google Play Store jika tersedia. Jika tidak, jika memungkinkan, lampirkan APK yang mereproduksi masalah. Perlu diperhatikan bahwa masalah (termasuk lampiran) dapat dilihat oleh publik.