ANR Umum Game Unity

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

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.

Stack trace

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 penyebabnya meliputi:

  • Penerapan iklan yang buruk.
  • Versi WebView yang sudah tidak berlaku karena pengguna mungkin telah memilih untuk tidak mengupdate aplikasi secara otomatis.
  • Penggunaan resource sistem (CPU, GPU, dll. yang tinggi), yang mungkin memerlukan banyak pembuatan profil.
  • Kompilasi shader mengalami error, yang dapat menunjukkan bahwa konten memiliki shader yang tidak kompatibel atau pengguna telah menginstal versi WebView 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 masalah, coba nonaktifkan penyedia iklan yang menyinggung.
    • Sertakan log memori untuk memastikan masalah tidak terkait dengan memori.
  • Beri tahu pengguna untuk mengupdate WebView dari Google Play. Dari Android 5.0 (level API 21) dan yang lebih tinggi, WebView telah dipindahkan ke APK. Oleh karena itu, aplikasi dapat diupdate secara terpisah dari platform Android. Untuk melihat versi WebView yang digunakan di perangkat, buka Setelan > Aplikasi > Android System 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 operasi 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 disinkronkan, UAP menunggu 4 detik hingga mesin berhenti.
  5. Jika operasi tersebut memerlukan waktu lebih dari 5 detik, sistem akan memicu ANR.

Stack trace

"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 membutuhkan waktu yang 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 mungkin memblokir thread utama karena rutinitas sinkronisasi native seperti mutex, menyebabkan ANR karena pertentangan kunci.

Stack trace

ANR pada gambar 4 disebabkan oleh operasi yang lama 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 ada beberapa pesan yang dikirim saat aplikasi dilanjutkan. Pesan dimasukkan ke dalam antrean karena tidak dapat dikirim saat game berada di latar belakang. Semua pesan dikirim secara bersamaan saat aplikasi dilanjutkan.

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

Workload ini, dikombinasikan dengan kode pihak ketiga lain yang membuat beban kerjanya sendiri, dapat membebani resource perangkat, terutama thread utama. Thread utama menjalankan antarmuka pengguna aplikasi dan sering kali merupakan lokasi utama ANR. Jadi, 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 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 kirim beberapa pesan dari plugin Anda saat game dijeda.
    • Mesin game tidak dapat mengirim pesan saat game berjalan di latar belakang.
    • Hanya kirimkan status data terakhir ke game Anda jika hal tersebut tidak memengaruhi fungsi game.

Perujuk Instal

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

Stack trace

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 masalahnya tidak dapat ditentukan tanpa akses ke kode sumber SDK.

Solusi

Memecahkan jenis masalah ini melibatkan komunikasi dengan developer SDK atau banyak penelusuran online untuk menemukan solusi potensial, memeriksa apakah versi SDK yang lebih baru menyelesaikan 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 dirancang untuk membantu Anda memutuskan apakah akan menggunakan, menyimpan, atau menghapus SDK dari aplikasi Anda.

Referensi tambahan

Untuk mempelajari ANR lebih lanjut, lihat referensi berikut:

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