Recycler view, power of asynchronous view holders creation

The easy way to optimize your lists

Dmitry Chernozubov
4 min readAug 31, 2020

Recycler solves a lot of the developer’s problems and it becomes smarter and better every release. Mostly, it is enough to use it the right way, but sometimes flags juggling and views hierarchy optimization — are not a cure. You have to fight for your lists performance.

1. Few words about recycler view

Take a look at the image below. The stages of view holder are there. You can google recycler view principle if you need.

Take a look at the image below. The stages of view holder are there. You can google recycler view principle if you need.

As for view holders binding and creation, in fact, the recycler has just two options: either to create a new holder or to use the existing one and bind it. Of course, we would like our view holders to be created more rarely, because it can become a heavy operation, to make matters worse, it happens in the UI thread. And it is totally painful, when the holders is being created and a user sees that your list is slowing down while it’s scrolling. Why does it happen? Because we spend a lot of time rendering frames. The special aspects of rendering in Android (https://developer.android.com/topic/performance/vitals/render).

2. The problem isolation

Now we need a problem confirmation despite the user experience.

I added “sleep” for clarity. The easiest thing we can do — to use developer settings.

On the right screenshot there is a graph of the frames rendering. The green live — 16 ms, the red one — 32 ms. Obviously, I have a problem in my application. Another approach is more detailed — profiler.

To make sure that the problem is in the view holders creation, you can get more information about the calls and the resources expense in other tabs of the profiler. But it is enough for my example.

3. Solution

Why not to create the view holders in the background thread and push them to the recycler view pull? We would solve the problem this way when our holders are created in the UI thread and annoy a user. Firstly, we should create our view pull.

HolderPrefetcherthe interface to count the holders and get their types.
ViewHolderCreator — the class which does the magic for the holders creation. The code is below.
attachToPreventViewPoolFromClearing() — is an important extension having the only method attach(), which needs to be called before RecycledViewPool addition to prevent its cleanup on a call setAdapter()
getRecycledView(viewType: Int) —
we override this method to catch a moment when the recycler has created the holders to prevent their further creation and extra work.
fakeParent — needs to add our parameters from xml, e.g. margins

Filter out the channels, I did it this way just for fun. In fact, you can use RX or to create a background thread.

now we have no frame drops

4. Gap worker

An interested listener could notice the method factorInCreateTime and gap worker reference. In short, we got a render thread besides UI thread, which took the right for the rendering in Android 5.0+. So, the creation and binding processes are going on the UI thread and, therefore, the render stage starts a bit later and we lose a frame. Google developers decided to move the creation and binding holders processes to the UI thread because it is cleared early on some frames. This is what gap worker does: it searches the moments when UI thread is cleared and tries to create and bind the holders. It is enabled since the 25th support as default. Also, it works with nested lists.

That’s it! Now we get a pretty simple and, at the same time, sufficient optimization. We used it on a real project and it worked great for the heavy holders.

--

--