UI Performance: Improving Text Rendering

Mkhytar Mkhoian
Lalafo
Published in
3 min readJan 17, 2018
picture belongs to uxgu

In Lalafo, we’re currently working on a new design and one of the goals we set out for our team is the best UI performance. We spent some time on research and decided not to use XML, standard layout container and write all UI with custom View and ViewGroup. Many developers steer clear of this approach, but to be the best, we must do our best. This post is about how we improved text rendering in our app.

Problem

All widgets Android has to display text on the screen are extended TextView. Under the hood, it uses android.text.Layout to do rendering. TextView widget converts the String to a android.text.Layout object, and use canvas API to draw the android.text.Layout object on screen. But the problem of TextView is it’s a general-purpose widget which supports a lot of features (watch changes in the text, draw the editor, pop up a drop-down list, and layout the embedded drawable). Also one of the performance-important points is setting text to TextView by calling TextView.setText(CharSequence) function. If we look at the source code for this method, we can see that there’s a lot of code to execute. Most of time we just need to render the static text, so that can be much simpler.

android.text.Layout

The solution is to draw android.text.Layout manually. For that, you need to write a custom view, it also has performance advantages (but we’ll discuss it in next blog post). The android.text.Layout has three implementation:

  • BoringLayout - very simple and faster Layout implementation for text that fits on a single line and is all left-to-right characters;
  • StaticLayout - Layout for text that will not be edited after it is laid out;
  • DynamicLayout - text Layout that updates itself as the text is edited;

We use only the first two. The android.text.Layout independent of Context, you can cache and reuse instances. Also, you can warm up Layout in background thread. Android introduced TextLayoutCache in API 14 to cache last drawn text results. It is an LRU (0.5M) cache and cache keys are text. If you hit the cache, the Android does not measure text nor generate text glyphs, and as the result, we have a ~10x rendering speed improvement, during our tests. To warm up the Layout you need to execute the code below, before draw text on the screen.

Note that TextLayoutCache isn’t used in Android API>= 21.

Working with android.text.Layout API is not so easy. For instance StaticLayout has 11 parameters in constructor.

You need to write the cache and warm up logic by your own. Here the TextLayoutBuilder library comes to the scene and provides a nice API to build android.text.Layout, cache and warm up.

Example of using TextLayoutBuilder

Conclusion

Render text in this way is challenges your skills. There are a lot of places in the code you can make a mistake. You just need to learn it as you would learn another library or framework and in the end you feel like a Norse God.

What about performance improvement? It always depends on the case. The more text you have to render, the greater difference in performance you’ll have. Don’t forget that you have 16.7 ms per frame, so each millisecond counts. In the first place, we made it for our feed, for better scrolling.

Stay tuned! Next time we’ll talk about custom View and ViewGroup. As to why you shouldn't use standard layout containers and should write your own. Also, how to reduce memory allocation.

--

--