Memverifikasi perilaku aplikasi pada Android runtime (ART)

Android runtime (ART) adalah runtime default untuk perangkat yang menjalankan Android 5.0 (level API 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 di Memperkenalkan ART.

Akan tetapi, beberapa teknik yang berfungsi di Dalvik tidak berfungsi di ART. Ini dokumen memberi tahu Anda tentang hal-hal yang harus diperhatikan saat memigrasikan agar kompatibel dengan ART. Kebanyakan aplikasi akan berfungsi hanya saat dijalankan dengan ART.

Menangani Masalah Pengumpulan Sampah (GC)

Dalam Dalvik, aplikasi sering kali merasa berguna untuk memanggil secara eksplisit System.gc() untuk meminta pembersihan sampah memori (GC). Seharusnya, jauh lebih tidak diperlukan dengan ART, terutama jika Anda memanggil pembersihan sampah memori untuk mencegah jenis GC_FOR_ALLOC kejadian 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 serentak memadatkan heap Java. Karena itu, Anda harus menghindari penggunaan teknik yang tidak kompatibel dengan GC pemadatan (seperti menyimpan pointer ke objek data instance). Hal ini khususnya 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. Ini adalah ide yang bagus menggunakan mode CheckJNI untuk menangkap masalah umum. Jika aplikasi Anda menggunakan C/C++ Anda harus meninjau artikel berikut:

Proses debug JNI Android dengan CheckJNI

Memeriksa kode JNI untuk masalah pengumpulan sampah

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

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

  • Jika Anda membuat perubahan pada elemen array yang ditampilkan, Anda harus memanggil metode fungsi Release...ArrayElements() yang sesuai saat Anda selesai, untuk memastikan perubahan yang Anda buat disalin kembali dengan benar ke objek array.
  • Saat merilis elemen array memori, Anda harus menggunakan metode bergantung pada perubahan yang telah Anda lakukan:
    • Jika Anda tidak membuat perubahan apa pun pada elemen {i>array<i}, gunakan Mode JNI_ABORT, yang melepaskan memori tanpa menyalin berubah kembali ke objek array yang mendasarinya.
    • Jika Anda membuat perubahan pada himpunan, dan tidak memerlukan referensi, selengkapnya, gunakan kode 0 (yang memperbarui objek array dan mengosongkan salinan memori).
    • Jika Anda membuat perubahan pada array yang ingin di-commit, dan Anda ingin untuk menyimpan salinan array, gunakan JNI_COMMIT (yang akan memperbarui objek array yang mendasarinya dan menyimpan salinannya).
  • Saat Anda memanggil Release...ArrayElements(), tampilkan respons yang sama pointer yang awalnya ditampilkan oleh Get...ArrayElements(). Sebagai tidak aman menambah pointer asli (untuk memindai melalui elemen array yang ditampilkan) lalu meneruskan pointer yang bertambah ke Release...ArrayElements(). Meneruskan pointer yang dimodifikasi ini dapat menyebabkan membebaskan memori yang salah, sehingga menyebabkan kerusakan memori.

Penanganan error

JNI ART menampilkan kesalahan dalam beberapa kasus, sedangkan Dalvik tidak demikian. (Sekali 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 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 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() sekarang menampilkan NoSuchFieldError dengan benar alih-alih hanya mengembalikan {i>null<i}. Demikian pula, GetMethodID() dan GetStaticMethodID() sekarang menampilkan NoSuchMethodError dengan benar. Ini bisa menyebabkan kegagalan CheckJNI karena pengecualian yang tidak tertangani atau diterapkan 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()) untuk menggunakan deklarasi metode , bukan subclass, seperti yang diwajibkan oleh spesifikasi JNI.

Mencegah masalah ukuran tumpukan

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

  • Di Java, tinjau panggilan ke konstruktor Thread yang menentukan stack eksplisit ukuran. Misalnya, Anda harus menambah ukuran jika StackOverflowError muncul.
  • Di C/C++, tinjau penggunaan pthread_attr_setstack() dan pthread_attr_setstacksize() untuk thread yang juga menjalankan kode Java melalui untuk JNI. Berikut contoh error yang dicatat 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 kelasnya harus berhati-hati agar tidak mencoba melihat kolom Object. Misalnya, jika Anda melakukan iterasi pada class sebagai bagian dari framework serialisasi, berhenti ketika

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

alih-alih melanjutkan hingga metode menampilkan null.

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

Memperbaiki masalah kompilasi AOT

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

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

Melaporkan masalah

Jika Anda mengalami masalah yang bukan disebabkan oleh masalah JNI aplikasi, laporkan mereka melalui Android Open Source Project Issue Tracker 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 menyelesaikan masalah. Perlu diketahui bahwa masalah (termasuk lampiran) bersifat publik terlihat.