Unity ANRs happen for a variety of reasons. Most common ANRs are caused by misuse of Android and Unity components and their miscommunication.
WebView
WebView
is an Android class that displays web pages. Third-party
SDKs (such as ads) use WebView
to display dynamic web content
in activities other than the UnityPlayerActivity
. ANRs occur when third-party
SDKs misuse WebView
.
Stack trace
The stack trace is your first recourse for understanding the cause of the 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)
Figure 1.ANR stack trace caused by a futex wait.
Cause
So far, the root cause of this issue is unclear. Some potential causes may include:
- Bad ad implementation.
- An outdated version of
WebView
since the user may have opted not to update the app automatically. - High usage of system resources (CPU, GPU, etc.), which may require a lot of profiling.
- Shader compilation crashes, which could indicate that the
content has an incompatible shader or that the user has an old
WebView
version installed.
Solution
- To narrow down which type of content is causing the
WebView
to block the main thread, add logs to your game whenever a web page is loaded, displayed, or closed.- You can use Backtrace or Crashlytics reporting services.
- Then, after analyzing the data and finding the issue, try disabling the offending ad providers.
- Include memory logs to ensure the issue is not memory related.
- Alert the user to update the
WebView
from Google Play. From Android 5.0 (API level 21) and higher,WebView
has moved to an APK. Therefore, it can be updated separately from the Android platform. To see what version ofWebView
is in use on a device, go to Settings > Apps > Android System WebView and look at the version at the bottom of the page.
Unity pause
When UnityPlayerActivity
receives an onPause()
call, the following chain of
operations starts:
UnityPlayerActivity
notifies the Unity runtime engine that the activity has paused.- Unity calls every
MonoBehaviour
that implements theOnApplicationPause
event. - Unity stops its components and modules, such as sound playback, rendering, game loop, and animation.
- To make sure both
Unity Android Player
(UAP) and engine are synchronized, the UAP waits 4 seconds for the engine to stop. - If that operation takes over 5 seconds, the system triggers an 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)
Figure 3. ANR caused by a semaphore that's never released.
Solution
Ensure that your C# game code doesn't take too long to finish execution during a pause or resume event.
- Profile your game and check whether the
OnApplicationPause
is an expensive operation. You can use aStopwatch
. - Avoid I/O operations or synchronous network requests.
- Move the operations to another
Thread
using theTask
. Unity 2023.1 supports a simplified asynchronous programming model using C#async
andawait
keywords.
UnitySendMessage blocked
Java Unity plugins and SDKs send data to the C# game layer using JNI. However, this communication might block the main thread due to a native synchronization routine such as a mutex, causing an ANR due to lock contention.
Stack trace
The ANR in figure 4 was caused by a long operation in the C# code called by a Java plugin. The Unity engine uses a Non-Priority Inheritance mutex to ensure correct execution.
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)
Figure 4. ANR caused by a lock contention.
Cause
The problem is that several messages are being dispatched when the application is resumed. The messages are queued because they cannot be sent while the game is in the background. The messages are all dispatched simultaneously when the app resumes.
During a pause period, you generally store your game's information on the server; for example, you record a player's position in the game so the player can return to the same place when the game resumes.
This workload, combined with other third-party code creating its own workload, can overload the device's resources, particularly the main thread. The main thread runs an app's user interface and is often the main location of ANRs. So, any added workload on the main thread increases the potential for an ANR.
Solution
During an application pause, make sure all your code actions are necessary, or try saving the user's state in your local device memory. And, of course, see whether you can also complete these actions outside of the pause period.
A few approaches:
- Move the C# operation that handles a message to a thread
other than the main thread.
- If your code does not depend on Unity's main thread context, use
Task
for communication instead of message.
- If your code does not depend on Unity's main thread context, use
- Don't send multiple messages from your plugin when the game is paused.
- The engine cannot send messages while the game is in the background.
- Only send the last data state to your game if that does not impact your game functionality.
Install Referrer
Play Install Referrer is a unique string sent to the Play Store whenever a user clicks on an ad. It is an Android-specific ad tracking identifier. Once installed, the app sends the install referrer to the attribution partner, which matches the source with the install (attributing the conversion).
Stack trace
Figure 5 shows an ANR stack trace from a game that uses the Facebook SDK to retrieve the install attribution.
Cause
The ANR was caused by a slow binder call. However, the root cause can't be determined without access to the SDK source code.
Solution
Solving this type of problem involves communication with the SDK developer or a lot of online searching for a potential solution, checking whether a newer version of the SDK solves the ANR for others, or even experimenting with a small rollout strategy.
Google provides an SDK Index page that combines usage data from Google Play apps with information gathered through code detection to provide attributes and signals designed to help you decide whether to adopt, keep, or remove an SDK from your app.
Additional resources
To learn more about ANRs, consult the following resources:
- Debug ANRs — Android game development
- ANRs — App quality