ANR ของ Unity เกิดขึ้นได้จากหลายสาเหตุ ANR ที่พบบ่อยที่สุดเกิดจากการ ใช้คอมโพเนนต์ของ Android และ Unity ในทางที่ผิด รวมถึงการสื่อสารที่ไม่ถูกต้อง
WebView
WebView
เป็นคลาส Android ที่แสดงหน้าเว็บ SDK ของบุคคลที่สาม (เช่น โฆษณา) ใช้ WebView
เพื่อแสดงเนื้อหาเว็บแบบไดนามิก
ในกิจกรรมอื่นๆ นอกเหนือจาก UnityPlayerActivity
ANR เกิดขึ้นเมื่อ SDK ของบุคคลที่สาม
ใช้ WebView
ในทางที่ผิด
สแต็กเทรซ
Stack Trace เป็นแหล่งข้อมูลแรกที่คุณควรใช้เพื่อทำความเข้าใจสาเหตุของ 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)
รูปที่ 1. สแต็กเทรซของ ANR ที่เกิดจากการรอ futex
สาเหตุ
ขณะนี้เรายังไม่ทราบสาเหตุของปัญหานี้ สาเหตุที่เป็นไปได้บางส่วนมีดังนี้
- การติดตั้งโฆษณาที่ไม่ดี
WebView
เนื่องจากผู้ใช้อาจเลือกที่จะไม่อัปเดต แอปโดยอัตโนมัติ- การใช้งานทรัพยากรระบบ (CPU, GPU ฯลฯ) สูง ซึ่งอาจต้องมีการทำโปรไฟล์จำนวนมาก
- การคอมไพล์ Shader ขัดข้อง ซึ่งอาจบ่งชี้ว่าเนื้อหามี Shader ที่เข้ากันไม่ได้ หรือผู้ใช้ได้ติดตั้ง
WebView
เวอร์ชันเก่า
โซลูชัน
- หากต้องการจำกัดประเภทเนื้อหาที่ทำให้
WebView
บล็อก เธรดหลัก ให้เพิ่มบันทึกลงในเกมทุกครั้งที่มีการโหลด แสดง หรือปิดหน้าเว็บ- คุณใช้บริการรายงาน Backtrace หรือ Crashlytics ได้
- จากนั้นหลังจากวิเคราะห์ข้อมูลและพบปัญหาแล้ว ให้ลองปิดใช้ผู้ให้บริการโฆษณาที่ทำให้เกิดปัญหา
- รวมบันทึกหน่วยความจำเพื่อให้แน่ใจว่าปัญหาไม่ได้เกี่ยวข้องกับหน่วยความจำ
- แจ้งเตือนให้ผู้ใช้อัปเดต
WebView
จาก Google Play ตั้งแต่ Android 5.0 (API ระดับ 21) ขึ้นไปWebView
ได้ย้ายไปอยู่ใน APK แล้ว จึงสามารถอัปเดตแยกจากแพลตฟอร์ม Android ได้ หากต้องการดูเวอร์ชันของWebView
ที่ใช้อยู่ในอุปกรณ์ ให้ไปที่การตั้งค่า > แอป > Android System WebView แล้วดูเวอร์ชันที่ด้านล่างของหน้า

WebView
เวอร์ชันหยุด Unity ชั่วคราว
เมื่อ UnityPlayerActivity
รับสาย onPause()
ระบบจะเริ่มลำดับการทำงานต่อไปนี้
UnityPlayerActivity
จะแจ้งให้เอ็นจินรันไทม์ของ Unity ทราบว่ากิจกรรมได้ หยุดชั่วคราวแล้ว- Unity จะเรียกใช้ทุก
MonoBehaviour
ที่ใช้เหตุการณ์OnApplicationPause
- Unity จะหยุดคอมโพเนนต์และโมดูลต่างๆ เช่น การเล่นเสียง การแสดงผล ลูปเกม และภาพเคลื่อนไหว
- เพื่อให้แน่ใจว่าทั้ง
Unity Android Player
(UAP) และเครื่องยนต์ จะซิงค์กัน UAP จะรอ 4 วินาทีเพื่อให้เครื่องยนต์หยุด - หากการดำเนินการดังกล่าวใช้เวลานานกว่า 5 วินาที ระบบจะทริกเกอร์ ANR
สแต็กเทรซ
"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)
รูปที่ 3 ANR ที่เกิดจาก Semaphore ที่ไม่เคยปล่อย
โซลูชัน
ตรวจสอบว่าโค้ดเกม C# ใช้เวลาไม่นานเกินไปในการดำเนินการให้เสร็จสมบูรณ์ในระหว่างเหตุการณ์หยุดชั่วคราวหรือเหตุการณ์กลับมาทำงานต่อ
- สร้างโปรไฟล์เกมและตรวจสอบว่า
OnApplicationPause
เป็นการดำเนินการที่ใช้ทรัพยากรมาก หรือไม่ คุณใช้Stopwatch
ได้ - หลีกเลี่ยงการดำเนินการ I/O หรือคำขอเครือข่ายแบบซิงโครนัส
- ย้ายการดำเนินการไปยัง
Thread
อื่นโดยใช้Task
Unity 2023.1 รองรับรูปแบบการเขียนโปรแกรมแบบอะซิงโครนัสที่เรียบง่ายโดยใช้คีย์เวิร์ด C#async
และawait
UnitySendMessage ถูกบล็อก
ปลั๊กอินและ SDK ของ Java Unity จะส่งข้อมูลไปยังเลเยอร์เกม C# โดยใช้ JNI อย่างไรก็ตาม การสื่อสารนี้อาจบล็อกเทรดหลักเนื่องจากกิจวัตรการซิงค์แบบเนทีฟ เช่น Mutex ซึ่งทำให้เกิด ANR เนื่องจากมีการแย่งชิงล็อก
สแต็กเทรซ
ANR ในรูปที่ 4 เกิดจากการดำเนินการที่ใช้เวลานานในโค้ด C# ที่เรียกใช้โดยปลั๊กอิน Java เอนจิน Unity ใช้ Non-Priority Inheritance mutex เพื่อให้มั่นใจว่าการดำเนินการถูกต้อง
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)
รูปที่ 4 ANR มีสาเหตุมาจากการแย่งชิงล็อก
สาเหตุ
ปัญหาคือมีการส่งข้อความหลายข้อความเมื่อแอปพลิเคชัน กลับมาทำงานต่อ ระบบจะจัดคิวข้อความเนื่องจากส่งไม่ได้ขณะที่เกมทำงานอยู่เบื้องหลัง ระบบจะส่งข้อความทั้งหมดพร้อมกันเมื่อ แอปกลับมาทำงานต่อ
โดยปกติแล้ว ในช่วงหยุดชั่วคราว คุณจะจัดเก็บข้อมูลของเกมไว้ในเซิร์ฟเวอร์ เช่น บันทึกตำแหน่งของผู้เล่นในเกมเพื่อให้ผู้เล่นกลับมายังตำแหน่งเดิมได้เมื่อเกมกลับมาเล่นต่อ
ภาระงานนี้เมื่อรวมกับโค้ดของบุคคลที่สามอื่นๆ ที่สร้างภาระงานของตนเอง อาจทำให้ทรัพยากรของอุปกรณ์ทำงานหนักเกินไป โดยเฉพาะอย่างยิ่งเธรดหลัก เธรดหลักจะเรียกใช้อินเทอร์เฟซผู้ใช้ของแอป และมักเป็นตำแหน่งหลักของ ANR ดังนั้น ปริมาณงานที่เพิ่มขึ้นในเทรดหลักจะเพิ่มโอกาสในการเกิด ANR
โซลูชัน
ในระหว่างที่แอปหยุดชั่วคราว ให้ตรวจสอบว่าการดำเนินการของโค้ดทั้งหมดมีความจำเป็น หรือ ลองบันทึกสถานะของผู้ใช้ในหน่วยความจำของอุปกรณ์ในเครื่อง และแน่นอนว่าคุณจะดูได้ว่า คุณสามารถดำเนินการเหล่านี้นอกช่วงหยุดชั่วคราวได้หรือไม่
แนวทางบางส่วน
- ย้ายการดำเนินการ C# ที่จัดการข้อความไปยังเทรดอื่นที่ไม่ใช่เทรดหลัก
- หากโค้ดไม่ได้ขึ้นอยู่กับบริบทของเทรดหลักของ Unity ให้ใช้
Task
เพื่อการสื่อสารแทนข้อความ
- หากโค้ดไม่ได้ขึ้นอยู่กับบริบทของเทรดหลักของ Unity ให้ใช้
- อย่าส่งข้อความหลายรายการจากปลั๊กอินเมื่อเกมหยุดชั่วคราว
- โดยเครื่องมือจะส่งข้อความไม่ได้ขณะที่เกมทำงานในเบื้องหลัง
- ส่งเฉพาะสถานะข้อมูลล่าสุดไปยังเกมหากไม่ส่งผลต่อฟังก์ชันการทำงานของเกม
เครื่องมือระบุแหล่งที่มาของการติดตั้ง
เครื่องมืออ้างอิงการติดตั้ง Play คือสตริงที่ไม่ซ้ำกันซึ่งส่งไปยัง Play Store ทุกครั้งที่ผู้ใช้คลิกโฆษณา ซึ่งเป็นตัวระบุการติดตามโฆษณาที่เฉพาะเจาะจงสำหรับ Android เมื่อติดตั้งแล้ว แอปจะส่งผู้แนะนำการติดตั้งไปยังพาร์ทเนอร์ระบุแหล่งที่มา ซึ่งจะจับคู่แหล่งที่มากับการติดตั้ง (ระบุแหล่งที่มาของ Conversion)
สแต็กเทรซ
รูปที่ 5 แสดง Stack Trace ของ ANR จากเกมที่ใช้ Facebook SDK เพื่อ ดึงข้อมูลการระบุแหล่งที่มาของการติดตั้ง

สาเหตุ
ANR มีสาเหตุมาจากการเรียกใช้ Binder ที่ช้า อย่างไรก็ตาม เราไม่สามารถระบุสาเหตุที่แท้จริงได้หากไม่มีสิทธิ์เข้าถึงซอร์สโค้ดของ SDK
โซลูชัน
การแก้ปัญหาประเภทนี้เกี่ยวข้องกับการสื่อสารกับนักพัฒนาซอฟต์แวร์ SDK หรือ การค้นหาออนไลน์จำนวนมากเพื่อหาโซลูชันที่เป็นไปได้ การตรวจสอบว่า SDK เวอร์ชันใหม่ แก้ปัญหา ANR ให้กับผู้อื่นได้หรือไม่ หรือแม้แต่การทดลองใช้กลยุทธ์การเปิดตัวขนาดเล็ก
Google มีหน้าดัชนี SDK ที่รวมข้อมูลการใช้งาน จากแอปของ Google Play เข้ากับข้อมูลที่รวบรวมผ่านการตรวจหาโค้ดเพื่อ ระบุแอตทริบิวต์และสัญญาณต่างๆ ที่ออกแบบมาเพื่อช่วยคุณตัดสินใจว่าจะใช้ เก็บ หรือนำ SDK ออกจากแอปหรือไม่
แหล่งข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับ ANR ได้จากแหล่งข้อมูลต่อไปนี้
- แก้ไขข้อบกพร่องของ ANR - การพัฒนาเกม Android
- ANR - คุณภาพแอป