ANR Umum Game Unity

ANR Unity terjadi karena berbagai alasan. ANR yang paling umum disebabkan oleh penyalahgunaan komponen Android dan Unity dan miskomunikasinya.

WebView

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

Pelacakan tumpukan

Stack trace adalah cara 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 waktu tunggu futex.

Penyebab

Sejauh ini, akar penyebab masalah ini belum jelas. Beberapa kemungkinan penyebab dapat termasuk:

  • Penerapan iklan yang buruk.
  • Versi WebView sudah tidak berlaku karena pengguna mungkin memilih untuk tidak mengupdate aplikasi secara otomatis.
  • Penggunaan resource sistem (CPU, GPU, dll. yang tinggi), yang mungkin memerlukan banyak pembuatan profil Anda.
  • Kompilasi shader mengalami error, yang dapat menunjukkan bahwa konten memiliki shader yang tidak kompatibel atau pengguna memiliki WebView lama versi yang diinstal.

Solusi

  • Untuk mempersempit jenis konten yang menyebabkan WebView memblokir thread utama, menambahkan log ke game Anda setiap kali halaman web dimuat, ditampilkan, atau tertutup.
    • Anda dapat menggunakan Backtrace atau Crashlytics layanan pelaporan.
    • Kemudian, setelah menganalisis data dan menemukan masalah, coba nonaktifkan penyedia iklan yang melakukan pelanggaran.
    • Sertakan log memori untuk memastikan masalah tidak terkait dengan memori.
  • Beri tahu pengguna untuk mengupdate WebView dari Google Play. Dari Android 5.0 (API level 21) dan yang lebih tinggi, WebView telah dipindahkan ke APK. Oleh karena itu, dapat diupdate secara terpisah dari platform Android. Untuk melihat versi WebView sedang digunakan di perangkat, buka Setelan > Aplikasi > Sistem Android WebView, lalu 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(), rantai berikut akan dimulai:

  1. UnityPlayerActivity memberi tahu mesin runtime Unity bahwa aktivitas telah dijeda.
  2. Unity memanggil setiap MonoBehaviour yang mengimplementasikan 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 telusur sudah disinkronkan, UAP menunggu selama 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 semafor yang tidak pernah dirilis.

Solusi

Pastikan kode game C# Anda tidak memakan waktu terlalu lama untuk menyelesaikan eksekusi selama menjeda atau melanjutkan peristiwa.

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

UnitySendMessage diblokir

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

Pelacakan tumpukan

ANR pada gambar 4 disebabkan oleh operasi yang lama dalam kode C# yang dipanggil oleh Plugin Java. Mesin Unity menggunakan Non-Priority Inheritance mutex 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 akan dilanjutkan. Pesan dimasukkan ke dalam antrean karena tidak dapat dikirim saat game berjalan di latar belakang. Semua pesan dikirim bersamaan ketika aplikasi dilanjutkan.

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

Workload ini, digabungkan dengan kode pihak ketiga lain yang menciptakan workload sendiri, dapat membebani sumber daya perangkat, terutama thread utama. Utama thread menjalankan antarmuka pengguna aplikasi dan sering kali merupakan lokasi utama ANR. Jadi, setiap beban kerja tambahan di thread utama meningkatkan potensi ANR.

Solusi

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

Beberapa pendekatan:

  • Memindahkan 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 kirim beberapa pesan dari plugin Anda saat game dijeda.
    • Mesin game tidak dapat mengirim pesan saat game berjalan di latar belakang.
    • Hanya kirim status data terakhir ke game Anda jika hal ini tidak memengaruhi game Anda fungsionalitasnya.

Perujuk Instal

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

Pelacakan tumpukan

Gambar 5 menunjukkan stack trace 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, akar penyebab tidak dapat ditentukan tanpa akses ke kode sumber SDK.

Solusi

Pemecahan jenis masalah ini melibatkan komunikasi dengan developer SDK atau melakukan pencarian {i>online<i} untuk menemukan solusi yang mungkin dilakukan, memeriksa apakah ada versi SDK ini memecahkan ANR untuk orang lain, atau bahkan bereksperimen dengan strategi peluncuran.

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

Referensi lainnya

Untuk mempelajari ANR lebih lanjut, lihat referensi berikut:

  • ANR Debug — Pengembangan game Android
  • ANR — Kualitas aplikasi