Identifying and resolving ANR (Application Not Responding) issues can be frustrating and time consuming, especially in the realm of Unity-based apps. Unfortunately, it’s work that can’t be avoided, as ANRs severely impact the user experience and Google Play Store discoverability.
A common source of pain for Unity engineers comes from ANRs that are mistakenly associated with the OnApplicationPause Unity callback.
In this post, we’ll provide some context, outline the common mixup, and explain how Unity engineers can avoid this confusion by focusing their efforts in more appropriate areas.
First, some context.
Android lifecycle events in Unity
The UnityPlayerActivity is a Java class implemented by Unity that is included in all Unity Android builds. It is the entry-point for the application, runs on the Android UI thread, is responsible for managing the app lifecycle, and serves as a sort of middleman between the OS and the Unity runtime.
The Unity runtime, and all game code, runs on a separate thread.
When the OS tells the app to pause, that message goes to the UnityPlayerActivity on the Android UI thread, which eventually gets to the UnityPlayerActivity.onPause function.
One of the things the activity needs to do to proceed with the pause is tell the Unity engine to invoke OnApplicationPause and suspend the game loop. But, since the Unity engine is executing on a separate thread, the activity must wait for a synchronization point at the end of the engine’s frame loop (this wait is the java.util.concurrent.Semaphore.tryAcquire we see in the ANR stack trace).
Under normal circumstances that wait would be only a few milliseconds, but if the Unity thread happens to already be blocked on some long running operation when this pause process starts, that is what results in an ANR.
Identifying UnityPlayerActivity.onPause ANRs
The Unity thread has been a constant source of ANR-related headaches for many engineers. Understanding the root cause of these issues is the first step towards resolving them.
Often, ANRs are mistakenly associated with the OnApplicationPause Unity callback, leading to confusion and ineffective problem-solving strategies. For example, the mixup can lead to logs and breadcrumbs being implemented in the wrong place, resulting in the appearance of the ANRs occurring before you thought they should.
To shed light on this misunderstanding, let’s dissect a common example:
- Synchronous operation on Unity thread: During normal gameplay, the Unity main thread initiates a synchronous operation, such as scene loading, asset bundle loading, or resource loading. These operations can be time-consuming and any one of them could be responsible for blocking the Unity thread.
- App backgrounded: Before the synchronous operation can be completed, the user sends the app to the background.
- OnApplicationPause is triggered: UnityPlayerActivity.onPause is called on the Java thread in response to the app being backgrounded. However, this call does not mean the ANR is happening inside the OnApplicationPause Unity callback itself. Rather, it’s because the Java thread is waiting for the Unity thread to finish its ongoing operation and reach the end of the current frame loop so that it can execute OnApplicationPause.
- Unity thread remains blocked: If the Unity thread remains blocked for several seconds while the Java thread is waiting, the ultimate result is an ANR.
In essence, the ANR is not directly tied to the pause callback but is a result of the Unity thread being stuck in some other operation when the pause is triggered.
To effectively address ANRs in this scenario, developers should focus on identifying synchronous operations on the Unity thread that could be slowing down the application, rather than focusing solely on the OnPauseApplication callback.
Learn more about solving Unity ANRs
Understanding the intricacies of ANRs in Unity applications is crucial for effective troubleshooting. With that in mind, it’s important to remember that ANRs related to Unity threads often result from synchronous operations on the Unity thread, rather than the OnApplicationPause callback itself.
Issues like these can be more easily identified and resolved with tooling like Embrace.
To learn more about solving Unity-based ANRs with Embrace, including ones related to the OnApplicationPause callback, read the Wildlife Games Studios case study, here.
Learn how Wildlife uses Embrace to solve ANRs
ANRs negatively impact player experience and Google Play Store visibility. Learn how one of the biggest mobile gaming studios in the world is using Embrace to eliminate ANRs from your favorite games.Read the case study