Five Lesser-Known Ways to Hang Your Main Thread
By now, most Android Developers already know that the main thread of an app is the one that handles user interaction, and should consequently be considered off-limits for time-consuming work. In general, any method invocation that causes the main thread to hang for 16*N milliseconds will lead to N dropped frames. We call such methods hung methods. In this blog post, we’ll first look at a hung method example, then look into five lesser-known ways of hanging the main thread.
Example Hung Method in Hollister
Let’s check out an example of a hung method in Hollister v3.1.2
Fortunately, our developer friends at Abercrombie & Fitch were able to use NimbleDroid’s profiling tools to detect and fix this issue (as well as others) in version 4.0.0, dramatically increasing their application’s performance and their users’ satisfaction :).
Let’s take a closer look at the icicle graph:
You can tell that it wasn’t a super complicated fix — developers simply moved the parsing of a large initial config into another thread (using the popular RxJava library).
A similar example is Fox News, which spends over 700ms reading data during startup.
There are many possible ways to hang the main thread. It is often a bad idea to use the main thread to access networks, storage, or databases — which are accesses that Android’s Strict Mode can detect. There are, however, other lesser known ways to hang the main thread that Strict Mode unfortunately doesn’t pick up. The battle here is to understand all the different ways the main thread can be hung, and to check apps often to avoid these issues. Let’s take a look at five other problems that can also lead to hung UI threads.
1. Parsing Network Responses in the Main Thread
Although the developers of Akinator FREE app correctly access the network in a background thread, they parse the response in the main thread.
The developers parse data using java.util.Scanner, which, as you can see, is not very fast: 192 calls take up 279 ms.
2. Doing Cryptography Computations
3. Eager Initialization
Initialization can take time, so it’s more efficient to do expensive initialization either in the background or lazily. FIFA spends over 269ms in Charset.availableCharsets(). The problem is somewhere deep in the SDK, but please note that this issue could be device specific.
4. Listing Assets
Remember that the more assets you have, the more time this method call will take.
5. Waiting For Synchronization
The hung method examples that we’ve given so far prevent the main thread from responding to user actions by keeping the main thread busy. There are also hung methods that block the main thread by waiting for synchronization (e.g., inside Object.wait or Thread.join).
To illustrate this issue, consider the Crittercism SDK. During Crittercism.initialize(), the developers correctly spawn a background thread to do expensive network operations, but initialize() calls Thread.join(), which waits for the background thread to finish, effectively blocking whichever thread is calling initialize(). Hotel Tonight 8.5.0 is a good example of this problem; there are many more apps that also suffer from the same issue.
Recommendation: Profile your app frequently to check if the main thread is hung. Move work to background threads, and as always, write efficient code.
With the help of Nimbledroid, you can make sure you’re not overburdening your main thread.
Originally published at blog.nimbledroid.com on March 21, 2016.