Lỗi ANR của Unity xảy ra vì nhiều lý do. Lỗi ANR phổ biến nhất là do việc sử dụng sai các thành phần Android và Unity cũng như việc truyền thông tin sai giữa các thành phần này.
WebView
WebView
là một lớp Android hiển thị các trang web. SDK của bên thứ ba (chẳng hạn như quảng cáo) sử dụng WebView
để hiển thị nội dung web động trong các hoạt động khác ngoài UnityPlayerActivity
. Lỗi ANR xảy ra khi SDK của bên thứ ba sử dụng sai WebView
.
Dấu vết ngăn xếp
Dấu vết ngăn xếp là biện pháp đầu tiên để bạn hiểu nguyên nhân gây ra lỗi 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)
Hình 1.Dấu vết ngăn xếp ANR do lệnh chờ futex gây ra.
Nguyên nhân
Cho đến nay, chúng tôi vẫn chưa xác định được nguyên nhân cốt lõi của vấn đề này. Một số nguyên nhân có thể là:
- Triển khai quảng cáo không hợp lệ.
- Một phiên bản
WebView
đã lỗi thời vì người dùng có thể đã chọn không tự động cập nhật ứng dụng. - Mức sử dụng tài nguyên hệ thống (CPU, GPU, v.v.) cao, có thể cần nhiều hoạt động phân tích hiệu suất.
- Lỗi biên dịch chương trình đổ bóng. Lỗi này có thể cho biết nội dung có chương trình đổ bóng không tương thích hoặc người dùng đã cài đặt phiên bản
WebView
cũ.
Giải pháp
- Để thu hẹp loại nội dung đang khiến
WebView
chặn luồng chính, hãy thêm nhật ký vào trò chơi của bạn bất cứ khi nào một trang web được tải, hiển thị hoặc đóng.- Bạn có thể sử dụng dịch vụ báo cáo Backtrace hoặc Crashlytics.
- Sau đó, sau khi phân tích dữ liệu và tìm ra vấn đề, hãy thử vô hiệu hoá các nhà cung cấp quảng cáo vi phạm.
- Gửi kèm nhật ký bộ nhớ để đảm bảo vấn đề không liên quan đến bộ nhớ.
- Cảnh báo người dùng cập nhật
WebView
từ Google Play. Từ Android 5.0 (API cấp 21) trở lên,WebView
đã chuyển sang APK. Do đó, bạn có thể cập nhật riêng biệt với nền tảng Android. Để xem phiên bảnWebView
đang được dùng trên một thiết bị, hãy chuyển đến phần Cài đặt > Ứng dụng > Android System WebView rồi xem phiên bản ở cuối trang.

WebView
.Tạm dừng Unity
Khi UnityPlayerActivity
nhận được lệnh gọi onPause()
, chuỗi thao tác sau sẽ bắt đầu:
UnityPlayerActivity
thông báo cho công cụ thời gian chạy Unity rằng hoạt động đã tạm dừng.- Unity gọi mọi
MonoBehaviour
triển khai sự kiệnOnApplicationPause
. - Unity sẽ dừng các thành phần và mô-đun của mình, chẳng hạn như phát âm thanh, kết xuất, vòng lặp trò chơi và ảnh động.
- Để đảm bảo cả
Unity Android Player
(UAP) và công cụ đều được đồng bộ hoá, UAP sẽ đợi 4 giây để công cụ dừng. - Nếu thao tác đó mất hơn 5 giây, hệ thống sẽ kích hoạt lỗi ANR.
Dấu vết ngăn xếp
"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)
Hình 3. Lỗi ANR do một tín hiệu bán tải không bao giờ được giải phóng.
Giải pháp
Đảm bảo rằng mã trò chơi C# của bạn không mất quá nhiều thời gian để hoàn tất quá trình thực thi trong sự kiện tạm dừng hoặc tiếp tục.
- Lập hồ sơ cho trò chơi của bạn và kiểm tra xem
OnApplicationPause
có phải là một thao tác tốn kém hay không. Bạn có thể sử dụngStopwatch
. - Tránh các thao tác I/O hoặc yêu cầu mạng đồng bộ.
- Di chuyển các thao tác sang một
Thread
khác bằng cách sử dụngTask
. Unity 2023.1 hỗ trợ một mô hình lập trình không đồng bộ đơn giản bằng cách sử dụng các từ khoáasync
vàawait
C#.
UnitySendMessage bị chặn
Các trình bổ trợ và SDK Java Unity gửi dữ liệu đến lớp trò chơi C# bằng JNI. Tuy nhiên, hoạt động giao tiếp này có thể chặn luồng chính do một quy trình đồng bộ hoá gốc (chẳng hạn như mutex), gây ra lỗi ANR do tranh chấp khoá.
Dấu vết ngăn xếp
Lỗi ANR trong hình 4 là do một thao tác diễn ra trong thời gian dài trong mã C# do một trình bổ trợ Java gọi. Công cụ Unity sử dụng mutex Kế thừa không ưu tiên để đảm bảo thực thi chính xác.
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)
Hình 4. Lỗi ANR do tranh chấp khoá.
Nguyên nhân
Vấn đề là một số thông báo đang được gửi đi khi ứng dụng được tiếp tục. Các thông báo được xếp hàng đợi vì không thể gửi khi trò chơi ở chế độ nền. Tất cả các thông báo đều được gửi cùng lúc khi ứng dụng tiếp tục hoạt động.
Trong thời gian tạm dừng, bạn thường lưu trữ thông tin của trò chơi trên máy chủ; ví dụ: bạn ghi lại vị trí của người chơi trong trò chơi để người chơi có thể quay lại cùng một vị trí khi trò chơi tiếp tục.
Tải này, kết hợp với mã của bên thứ ba khác tạo ra tải riêng, có thể làm quá tải tài nguyên của thiết bị, đặc biệt là luồng chính. Luồng chính chạy giao diện người dùng của ứng dụng và thường là vị trí chính của lỗi ANR. Vì vậy, mọi tải công việc được thêm vào luồng chính đều làm tăng khả năng xảy ra lỗi ANR.
Giải pháp
Trong thời gian tạm dừng ứng dụng, hãy đảm bảo mọi thao tác mã của bạn đều cần thiết hoặc thử lưu trạng thái của người dùng vào bộ nhớ thiết bị cục bộ. Và tất nhiên, hãy xem liệu bạn có thể hoàn tất những hành động này bên ngoài khoảng thời gian tạm dừng hay không.
Một số phương pháp:
- Di chuyển thao tác C# xử lý một message sang một luồng không phải là luồng chính.
- Nếu mã của bạn không phụ thuộc vào ngữ cảnh của luồng chính của Unity, hãy sử dụng
Task
để giao tiếp thay vì thông báo.
- Nếu mã của bạn không phụ thuộc vào ngữ cảnh của luồng chính của Unity, hãy sử dụng
- Không gửi nhiều thông báo từ trình bổ trợ khi trò chơi đang tạm dừng.
- Công cụ này không thể gửi thông báo khi trò chơi ở chế độ nền.
- Chỉ gửi trạng thái dữ liệu cuối cùng đến trò chơi nếu trạng thái đó không ảnh hưởng đến chức năng của trò chơi.
Install Referrer
Play Install Referrer là một chuỗi duy nhất được gửi đến Cửa hàng Play bất cứ khi nào người dùng nhấp vào một quảng cáo. Đây là giá trị nhận dạng theo dõi quảng cáo dành riêng cho Android. Sau khi được cài đặt, ứng dụng sẽ gửi trình giới thiệu lượt cài đặt cho đối tác phân bổ. Đối tác này sẽ so khớp nguồn với lượt cài đặt (phân bổ lượt chuyển đổi).
Dấu vết ngăn xếp
Hình 5 cho thấy dấu vết ngăn xếp ANR của một trò chơi sử dụng Facebook SDK để truy xuất thông tin phân bổ lượt cài đặt.

Nguyên nhân
Lỗi ANR là do lệnh gọi liên kết bị chậm gây ra. Tuy nhiên, bạn không thể xác định nguyên nhân gốc rễ nếu không có quyền truy cập vào mã nguồn SDK.
Giải pháp
Để giải quyết loại vấn đề này, bạn cần liên hệ với nhà phát triển SDK hoặc tìm kiếm nhiều trên mạng để tìm ra giải pháp tiềm năng, kiểm tra xem phiên bản SDK mới hơn có giải quyết được lỗi ANR cho những người khác hay không, hoặc thậm chí thử nghiệm với một chiến lược phát hành quy mô nhỏ.
Google cung cấp một trang Chỉ mục SDK kết hợp dữ liệu sử dụng của các ứng dụng trên Google Play với thông tin thu thập được thông qua tính năng phát hiện đoạn mã để cung cấp các thuộc tính và tín hiệu giúp bạn quyết định nên sử dụng, giữ hay xoá một SDK khỏi ứng dụng.
Tài nguyên khác
Để tìm hiểu thêm về lỗi ANR, hãy tham khảo các tài nguyên sau:
- Gỡ lỗi ANR – Phát triển trò chơi Android
- Lỗi ANR – Chất lượng ứng dụng