ANR Game Unity Umum

ANR Unity terjadi karena berbagai alasan. ANR paling umum disebabkan oleh penyalahgunaan komponen Android dan Unity serta miskomunikasi di antara keduanya.

WebView

WebView adalah class Android yang menampilkan halaman web. SDK pihak ketiga (seperti iklan) menggunakan WebView untuk menampilkan konten web dinamis dalam aktivitas selain UnityPlayerActivity. ANR terjadi saat SDK pihak ketiga menyalahgunakan WebView.

Pelacakan tumpukan

Stack trace adalah upaya pertama Anda untuk memahami penyebab ANR.

/data/app/~~p-0ksfCD6bF6Sdq6kpVePg==/com.google.android.webview-5YQZOqKbbqp-uoLY6WYnTw==/base.apk!libmonochrome.so
  at J.N.Mhc_M_H$ (Native method)
  at org.chromium.components.viz.service.frame_sinks.ExternalBeginFrameSourceAndroid.doFrame (chromium-TrichromeWebViewGoogle.aab-stable-579013831:60)
  at android.view.Choreographer$CallbackRecord.run (Choreographer.java:1054)
  at android.view.Choreographer.doCallbacks (Choreographer.java:878)
  at android.view.Choreographer.doFrame (Choreographer.java:807)
  at android.view.Choreographer$FrameDisplayEventReceiver.run (Choreographer.java:1041)
  at android.os.Handler.handleCallback (Handler.java:938)
  at android.os.Handler.dispatchMessage (Handler.java:99)
  at android.os.Looper.loop (Looper.java:223)
  at android.app.ActivityThread.main (ActivityThread.java:7721)
  at java.lang.reflect.Method.invoke (Native method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:592)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:952)

Gambar 1.Stack trace ANR yang disebabkan oleh penantian futex.

Penyebab

Sejauh ini, penyebab utama masalah ini belum jelas. Beberapa kemungkinan penyebabnya meliputi:

  • Penerapan iklan yang buruk.
  • Versi WebView yang sudah usang karena pengguna mungkin memilih untuk tidak mengupdate aplikasi secara otomatis.
  • Penggunaan resource sistem (CPU, GPU, dll.) yang tinggi, yang mungkin memerlukan banyak pemrofilan.
  • Error kompilasi shader, yang dapat menunjukkan bahwa konten memiliki shader yang tidak kompatibel atau pengguna telah menginstal versi WebView yang lama.

Solusi

  • Untuk mempersempit jenis konten yang menyebabkan WebView memblokir thread utama, tambahkan log ke game Anda setiap kali halaman web dimuat, ditampilkan, atau ditutup.
    • Anda dapat menggunakan layanan pelaporan Backtrace atau Crashlytics.
    • Kemudian, setelah menganalisis data dan menemukan masalahnya, coba nonaktifkan penyedia iklan yang bermasalah.
    • Sertakan log memori untuk memastikan masalahnya tidak terkait dengan memori.
  • Minta pengguna untuk mengupdate WebView dari Google Play. Mulai dari Android 5.0 (API level 21) dan yang lebih tinggi, WebView telah dipindahkan ke APK. Oleh karena itu, library ini dapat diupdate secara terpisah dari platform Android. Untuk melihat versi WebView yang digunakan di perangkat, buka Setelan > Aplikasi > Android System WebView dan lihat versi di bagian bawah halaman.
Layar info aplikasi yang menampilkan versi WebView.
Gambar 1. Periksa versi WebView.

Jeda Unity

Saat UnityPlayerActivity menerima panggilan onPause(), rangkaian operasi berikut akan dimulai:

  1. UnityPlayerActivity memberi tahu mesin runtime Unity bahwa aktivitas telah dijeda.
  2. Unity memanggil setiap MonoBehaviour yang menerapkan peristiwa OnApplicationPause.
  3. Unity menghentikan komponen dan modulnya, seperti pemutaran suara, rendering, game loop, dan animasi.
  4. Untuk memastikan Unity Android Player (UAP) dan mesin disinkronkan, UAP menunggu 4 detik hingga mesin berhenti.
  5. Jika operasi tersebut memerlukan waktu lebih dari 5 detik, sistem akan memicu ANR.

Pelacakan tumpukan

"main" tid=1 Timed Waiting
jdk.internal.misc.Unsafe.park (Native method)
java.util.concurrent.locks.LockSupport.parkNanos (LockSupport.java:234)
java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedNanos (AbstractQueuedSynchronizer.java:1079)
java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquireSharedNanos (AbstractQueuedSynchronizer.java:1369)
java.util.concurrent.Semaphore.tryAcquire (Semaphore.java:415)
com.unity3d.player.UnityPlayer.pauseUnity (UnityPlayer.java:833)
com.unity3d.player.UnityPlayer.pause (UnityPlayer.java:796)
com.unity3d.player.UnityPlayerActivity.onPause (UnityPlayerActivity.java:117)
android.app.Activity.performPause (Activity.java:8517)
android.app.Instrumentation.callActivityOnPause (Instrumentation.java:1618)
android.app.ActivityThread.performPauseActivityIfNeeded (ActivityThread.java:5061)
android.app.ActivityThread.performPauseActivity (ActivityThread.java:5022)
android.app.ActivityThread.handlePauseActivity (ActivityThread.java:4974)
android.app.servertransaction.PauseActivityItem.execute (PauseActivityItem.java:48)
android.app.servertransaction.ActivityTransactionItem.execute (ActivityTransactionItem.java:45)
android.app.servertransaction.TransactionExecutor.executeLifecycleState (TransactionExecutor.java:179)
android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.java:97)
android.app.ActivityThread$H.handleMessage (ActivityThread.java:2303)
android.os.Handler.dispatchMessage (Handler.java:106)
android.os.Looper.loopOnce (Looper.java:201)
android.os.Looper.loop (Looper.java:288)
android.app.ActivityThread.main (ActivityThread.java:7884)
java.lang.reflect.Method.invoke (Native method)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:548)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:936)

Gambar 3. ANR disebabkan oleh semaphore yang tidak pernah dilepaskan.

Solusi

Pastikan kode game C# Anda tidak memerlukan waktu terlalu lama untuk menyelesaikan eksekusi selama peristiwa jeda atau lanjutkan.

  • Buat profil game Anda dan periksa apakah OnApplicationPause adalah operasi yang mahal. Anda dapat menggunakan Stopwatch.
  • Hindari operasi I/O atau permintaan jaringan sinkron.
  • Pindahkan operasi ke Thread lain menggunakan Task. Unity 2023.1 mendukung model pemrograman asinkron yang disederhanakan menggunakan kata kunci C# async dan await.

UnitySendMessage diblokir

Plugin dan SDK Java Unity mengirim data ke lapisan game C# menggunakan JNI. Namun, komunikasi ini dapat memblokir thread utama karena rutinitas sinkronisasi native seperti mutex, sehingga menyebabkan ANR karena pertentangan kunci.

Pelacakan tumpukan

ANR pada gambar 4 disebabkan oleh operasi yang panjang dalam kode C# yang dipanggil oleh plugin Java. Mesin Unity menggunakan mutex Non-Priority Inheritance untuk memastikan eksekusi yang benar.

libc.so NonPI::MutexLockWithTimeout(pthread_mutex_internal_t*, bool, timespec const*) + 604
com.unity3d.player.UnityPlayer.nativeUnitySendMessage (Native method)
com.unity3d.player.UnityPlayer.UnitySendMessage (UnityPlayer.java:665)

Gambar 4. ANR disebabkan oleh pertentangan kunci.

Penyebab

Masalahnya adalah beberapa pesan dikirim saat aplikasi dilanjutkan. Pesan dimasukkan dalam antrean karena tidak dapat dikirim saat game berada di latar belakang. Semua pesan dikirim secara bersamaan saat aplikasi dilanjutkan.

Selama periode jeda, Anda biasanya menyimpan informasi game di server; misalnya, Anda mencatat posisi pemain dalam game sehingga pemain dapat kembali ke tempat yang sama saat game dilanjutkan.

Workload ini, jika dikombinasikan dengan kode pihak ketiga lain yang membuat workload-nya sendiri, dapat membebani resource perangkat, terutama thread utama. Thread utama menjalankan antarmuka pengguna aplikasi dan sering kali menjadi lokasi utama ANR. Jadi, setiap beban kerja tambahan di thread utama meningkatkan potensi terjadinya ANR.

Solusi

Selama jeda aplikasi, pastikan semua tindakan kode Anda diperlukan, atau coba simpan status pengguna di memori perangkat lokal Anda. Tentu saja, lihat apakah Anda juga dapat menyelesaikan tindakan ini di luar periode jeda.

Beberapa pendekatan:

  • Pindahkan operasi C# yang menangani pesan ke thread selain thread utama.
    • Jika kode Anda tidak bergantung pada konteks thread utama Unity, gunakan Task untuk komunikasi, bukan pesan.
  • Jangan mengirim beberapa pesan dari plugin saat game dijeda.
    • Mesin tidak dapat mengirim pesan saat game berada di latar belakang.
    • Kirim status data terakhir ke game Anda hanya jika tidak memengaruhi fungsi game Anda.

Perujuk Instal

Play Install Referrer adalah string unik yang dikirim ke Play Store setiap kali pengguna mengklik iklan. ID ini adalah ID pelacakan iklan khusus Android. Setelah diinstal, aplikasi akan mengirimkan perujuk penginstalan ke partner atribusi, yang mencocokkan sumber dengan penginstalan (mengatribusikan konversi).

Pelacakan tumpukan

Gambar 5 menunjukkan rekaman aktivitas stack ANR dari game yang menggunakan Facebook SDK untuk mengambil atribusi penginstalan.

Gambar 5. Laporan Android Vitals yang berisi panggilan Binder.

Penyebab

ANR disebabkan oleh panggilan binder yang lambat. Namun, penyebab utamanya tidak dapat ditentukan tanpa akses ke kode sumber SDK.

Solusi

Untuk memecahkan masalah jenis ini, Anda harus berkomunikasi dengan developer SDK atau melakukan banyak penelusuran online untuk menemukan solusi yang mungkin, memeriksa apakah versi SDK yang lebih baru memecahkan ANR untuk orang lain, atau bahkan bereksperimen dengan strategi peluncuran kecil.

Google menyediakan halaman SDK Index yang menggabungkan data penggunaan dari aplikasi Google Play dengan informasi yang dikumpulkan melalui deteksi kode untuk memberikan atribut dan sinyal yang didesain untuk membantu Anda memutuskan apakah akan menggunakan, menyimpan, atau menghapus SDK dari aplikasi Anda.

Referensi lainnya

Untuk mempelajari ANR lebih lanjut, lihat referensi berikut: