Threading out of a performance labyrinth with
I really love animations in Android. They add so much to the UX of an app that I really wish more product owners added them as part of their acceptance criteria for new features in a mobile app.
The thing with animations however is that they can be quite tricky; timing them has to be perfect especially when
Views have other dependent
View’s in their layout hierarchy. Perhaps more foreboding, is the need to keep animations running at a consistent 60 frames per second, else they begin to hurt the user experience, rather than help it, and at that point why even bother?
In my app Teammate, I had an issue. On screens with editable text, the initial transition to bring the fragment container in stuttered terribly. This was very bad as the screen typically had 3 animations to run concurrently on entrance:
- It had to use a
SharedElementTransitionto expand a thumbnail image to a header image.
- It had to apply a dark tint to the header image to make the
Toolbartitle easier to read.
- I had to animate the
FloatingActionButtonas it’s text, size and icon changed.
I ruled out the usual suspects, inefficient layouts, api calls on the UI thread, everything, and was still feeling flummoxed. Certain my current approach would take me nowhere, I turned to
What is systrace?
From the description at the Android Developer’s site:
systracecommand allows you to collect and inspect timing information across all processes running on your device at the system level. It combines data from the Android kernel, such as the CPU scheduler, disk activity, and app threads, to generate an HTML report.
What this is saying is that
systrace is an amazing way to find out exactly what your app is doing and where it’s doing it. To run it, you simply navigate to your Android sdk’s
platfoorm-tools directory, and go into the systrace folder. once there run the following command:
python systrace.py -o mynewtrace.html -a <your app's package name> sched freq idle am wm gfx view binder_driver hal dalvik camera input res
And the output will be a lovely html report. If you have an idea of where your bottleneck may be, the
Trace utility class is indispensable as it let’s you add markers to the generated html report.
The following is a very simplified usage of the
Trace class to demonstrate it’s ease of use. It’s a slightly modified version of that on the Android developer site, formatted so it’s easier to read, as it omits the
try / finally blocks when calling
Trace. While not explicitly necessary, your trace markers will be mismatched if an exception is thrown and
Trace.endSection() isn’t called for the associated section.
Let’s look at a sample trace report shall we?
RecyclerView already adds method to trace both the layout and bind methods internally. From this, it appears that binding my ViewHolder takes an abnormally long time, 95 milliseconds to exact. It wasn’t because my layout was inefficient either, the purple marker next to RV onBindView is the inflation of the
View in the
ViewHolder; it takes 4 times as long to set properties on the ViewHolder than to create it. Zooming in closer on the trace markers so that my custom markers become visible yields the following:
My custom markers are
Input Set, and
Input Set is the super tiny one in a light purple and all it does is actually set values to fields; methods like
EditText.setText() and the like. The issue therefore was my code and not the Android Framework.
Further digging let me realize for each other custom marker, I was actually pulling a rather large
SharedPreferences; the same string in fact, both in
Input Params and
Input Misc, hence why they both took the exact same amount of time.
This is where
systrace really shines, the ability to glean exactly where in your code the hiccups occur, especially when custom markers are used. I know for a fact it would have taken me much longer to identify the root cause without
systrace, and I highly encourage reading the Android Developer’s site for more information. It’s a very, very handy tool, and doesn’t get enough love in my opinion.
Finally, lets look at the frame performance with
systrace with the SharedPreference bottleneck removed.
Look at all that green! A lovely sequence of highly performant frames in quick succession, even the most ardent pixel peepers would be pressed trying to find jank.
That’s it! I really hope this convinces you to give
systrace a try, I really cannot emphasize more how lovely it is. Thanks for reading!