Unity 发生 ANR 问题的原因有很多。导致最常见的 ANR 的原因如下: Android 和 Unity 组件滥用及其通信错误。
WebView
WebView
是一个用于显示网页的 Android 类。第三方
SDK(例如广告)使用 WebView
展示动态 Web 内容
在 UnityPlayerActivity
之外的活动中产生的费用。ANR 的发生位置是
SDK 滥用 WebView
。
堆栈轨迹
堆栈轨迹是您了解 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. 由 futex 等待导致的 ANR 堆栈轨迹。
原因
到目前为止,该问题的根本原因尚不明确。一些潜在原因 包括:
- 广告植入方式有问题。
WebView
的版本已过时,因为用户可能已选择不更新 自动应用。- 系统资源(CPU、GPU 等)使用率较高,可能需要大量 性能分析
- 着色器编译崩溃,这可能表明
内容的着色器不兼容,或者用户的
WebView
版本过旧 已安装版本。
解决方案
- 为了缩小导致
WebView
阻止 在主线程上,每当网页加载、显示网页时,向游戏添加日志 或关闭状态。- 您可以使用 Backtrace 或 Crashlytics 报告服务。
- 然后,在分析数据并找出问题后,尝试停用 违规的广告提供商。
- 添加内存日志,确保问题与内存无关。
- 提醒用户在 Google Play 中更新
WebView
。从 Android 5.0 开始 (API 级别 21)及更高版本,WebView
已移至 APK。因此, 与 Android 平台分开更新。如需查看WebView
的哪个版本 是否正在使用某个设备,请转至设置 >应用 >Android 系统 WebView,然后在页面底部查看版本。
Unity 暂停
当 UnityPlayerActivity
收到 onPause()
调用时,以下链
操作开始:
UnityPlayerActivity
会通知 Unity 运行时引擎 activity 已 已暂停。- 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 是由从未发布的信号量引起的。
解决方案
确保您的 C# 游戏代码在 暂停或恢复事件
- 分析您的游戏并检查
OnApplicationPause
是否贵重 操作。您可以使用Stopwatch
。 - 避免 I/O 操作或同步网络请求。
- 使用
Thread
Task
。Unity 2023.1 支持简化的 使用 C# 的异步编程模型async
和await
个关键字。
UnitySendMessage 已被屏蔽
Java Unity 插件和 SDK 使用 JNI 将数据发送到 C# 游戏层。 不过,这种通信可能会由于原生进程的 同步例程(如互斥量),从而导致因锁争用而引发 ANR。
堆栈轨迹
图 4 中的 ANR 是由 C# 代码中的长操作(由 Java 插件。Unity 引擎使用非优先级继承 互斥量来确保正确执行。
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 Install Referrer 是每当 用户点击广告。它是 Android 专用的广告跟踪标识符。一次 应用向归因合作伙伴发送安装引荐来源网址,而归因合作伙伴 来源与安装相匹配(归因转化)。
堆栈轨迹
图 5 显示了某款游戏的 ANR 堆栈轨迹,该游戏使用 Facebook SDK 来 检索安装归因。
原因
ANR 是由 binder 调用速度缓慢引起的。但根本原因在于 无需访问 SDK 源代码即可确定。
解决方案
若要解决这类问题,需要与 SDK 开发者或 许多在线搜索潜在解决方案的用户,查看是否有较新的 其他版本的 SDK 可解决 ANR 问题,甚至可以使用 部署策略。
Google 提供了一个 SDK 索引页面,其中汇总了使用情况数据 从 Google Play 应用中收集通过代码检测功能收集的信息, 提供的属性和信号可帮助您决定是否采用、 在应用中保留或移除某个 SDK。
其他资源
如需详细了解 ANR,请参阅以下资源: