Lỗi ANR của Unity xảy ra vì nhiều lý do. Các lỗi ANR thường gặp nhất là do việc sử dụng sai mục đích các thành phần Android và Unity cũng như hiểu nhầm các thành phần này.
WebView
WebView
là một lớp Android hiển thị các trang web. Bên thứ ba
SDK (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 bên thứ ba
SDK sử dụng sai WebView
.
Dấu vết ngăn xếp
Dấu vết ngăn xếp là nguồn hỗ trợ đầu tiên của bạn để tìm 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 quá trình chờ futex gây ra.
Nguyên nhân
Cho đến nay, nguyên nhân gốc rễ của vấn đề này vẫn chưa rõ ràng. Có thể có một số nguyên nhân bao gồm:
- Triển khai quảng cáo không tốt.
- Phiên bản
WebView
đã lỗi thời vì có thể người dùng đã chọn không cập nhật ứng dụng. - Mức sử dụng tài nguyên hệ thống cao (CPU, GPU, v.v.) có thể đòi hỏi nhiều lập hồ sơ.
- Sự cố biên dịch đổ bóng có thể cho biết rằng
nội dung có chương trình đổ bóng không tương thích hoặc người dùng đã sử dụng
WebView
cũ phiên bản đã cài đặt.
Giải pháp
- Để thu hẹp loại nội dung nào 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 trang web được tải, hiển thị, hoặc đã đóng.- Bạn có thể sử dụng Backtrace hoặc Crashlytics báo cáo.
- Sau đó, sau khi phân tích dữ liệu và tìm ra vấn đề, hãy thử tắt nhà cung cấp quảng cáo vi phạm.
- Cung cấp nhật ký bộ nhớ để đảm bảo vấn đề không liên quan đến bộ nhớ.
- Nhắc người dùng cập nhật
WebView
trên Google Play. Từ Android 5.0 (API cấp 21) trở lên,WebView
đã chuyển sang một tệp APK. Do đó, có thể được cập nhật riêng biệt với nền tảng Android. Để biết phiên bảnWebView
nào đang được sử dụng trên một thiết bị, hãy chuyển đến phần Cài đặt > Ứng dụng > Hệ thống Android WebView và xem phiên bản ở cuối trang.
Tạm dừng Unity
Khi UnityPlayerActivity
nhận được lệnh gọi onPause()
, chuỗi sau
hoạt động bắt đầu:
UnityPlayerActivity
thông báo cho công cụ thời gian chạy Unity rằng hoạt động đã bị tạm dừng.- Unity gọi mọi
MonoBehaviour
triển khaiOnApplicationPause
sự kiện. - 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ụ được đồng bộ hoá, UAP sẽ chờ 4 giây để động cơ dừng lại. - Nếu quá trình đó mất hơn 5 giây, thì hệ thống sẽ kích hoạt mộ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 semaphore gây ra chưa từng được phát hành.
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 tạm dừng hoặc tiếp tục sự kiện.
- Phân tích tài nguyên cho trò chơi của bạn và kiểm tra xem
OnApplicationPause
có tốn kém không hoạt động. Bạn có thể dùngStopwatch
. - Tránh các hoạt động I/O hoặc cá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 hàmTask
. Unity 2023.1 hỗ trợ phiên bản đơn giản hoá mô hình lập trình không đồng bộ bằng C#async
vàawait
từ khoá.
UnitySendMessage bị chặn
Các trình bổ trợ và SDK Java cho 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 mã gốc quy trình đồng bộ hoá, 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 do một thao tác dài trong mã C# được gọi bởi Trình bổ trợ Java. Công cụ Unity sử dụng cơ chế Kế thừa không ưu tiên mutex để đả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á gây ra.
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. Tin nhắn được xếp vào hàng đợi vì không gửi được tin nhắn khi đang chơi trò chơi đang ở chế độ nền. Tất cả các tin nhắn sẽ được gửi đi cùng một lúc khi ứng dụng tiếp tục.
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 một người chơi trong trò chơi để người chơi đó có thể quay lại vị trí cũ khi trò chơi tiếp tục.
Khối lượng công việc này, kết hợp với mã của bên thứ ba khác đang tạo ra khối lượng công việc 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. Chính luồng này chạy giao diện người dùng của một ứng dụng và thường là vị trí chính của lỗi ANR. Vì vậy, mọi khối lượng công việc tăng thêm trên luồng chính đều làm tăng nguy cơ 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 tất cả thao tác liên quan đến mã đều cần thiết, hoặc hãy thử lưu trạng thái của người dùng vào bộ nhớ cục bộ của thiết bị. Và tất nhiên là bạn cũng có thể thực hiện cả những hành động này 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 tin nhắn sang chuỗi
ngoài luồng chính.
- Nếu mã của bạn không phụ thuộc vào ngữ cảnh luồng chính của Unity, hãy sử dụng
Task
để liên lạc thay vì nhắn tin.
- Nếu mã của bạn không phụ thuộc vào ngữ cảnh 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ụ không thể gửi tin nhắn khi trò chơi đang chạy trong nền.
- Chỉ gửi trạng thái dữ liệu gần đây nhất đến trò chơi nếu trạng thái đó không ảnh hưởng đến trò chơi của Google.
Cài đặt 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 một người dùng nhấp vào 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. Một lần thì ứng dụng sẽ gửi liên kết giới thiệu lượt cài đặt đến đối tác phân bổ, 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 minh hoạ dấu vết ngăn xếp ANR của một trò chơi sử dụng SDK Facebook để truy xuất thuộc tính lượt cài đặt.
Nguyên nhân
Lỗi ANR xảy ra do lệnh gọi liên kết chậm. Tuy nhiên, căn nguyên không thể được xác định mà 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 lạc với nhà phát triển SDK hoặc rất nhiều người tìm kiếm giải pháp tiềm năng trên mạng, kiểm tra xem giải pháp mới hơn phiên bản SDK giải quyết lỗi ANR cho người khác hoặc thậm chí thử nghiệm với một phát hành ứng dụng.
Google cung cấp một trang Chỉ mục SDK kết hợp dữ liệu sử dụng từ các ứng dụng trên Google Play bằng thông tin thu thập được qua tính năng phát hiện mã để cung cấp các thuộc tính và tín hiệu giúp bạn quyết định có nên áp dụng hay không giữ lại hoặc xoá SDK khỏi ứng dụng của bạn.
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 trên Android
- ANR — Chất lượng ứng dụng