• RecyclerView Prefetch
  • Chet Haase
  • The Nuggets translation Project
  • Translator: tanglie1993
  • Proofreader: Skyar2009, Zhiw

Process tasks faster and make scrolling and swiping smoother

When I was growing up, my mom used to cure my procrastination by telling me, “If you clean your room now, you won’t have to clean it later.” But I never did. I know it’s best to delay as long as I can. One reason: if I clean now, the room will get dirty again, and I will have to clean it again. Besides, if I let it go long enough, mom might forget about it.

Procrastination always works for me. But I never had to deal with maintaining frame rates, unlike my friend RecyclerView.

The solution

Moving the creation and binding work to the previous frame enables the UI thread to work at the same time as the renderer thread, thereby avoiding subsequent synchronization before the renderer thread draws the results. Obviously, this is a good time to optimize time. Chris has rearranged the default RecyclerView layout in which events occur in order so that it now prefetchdata when an item is about to come into view so that we can finish work during the idle period and avoid dragging it out until everyone is waiting for the result. This is essentially free, because the UI thread does nothing in the space between frames. We can use this free time to complete future work and make future frames appear faster because the hard parts have already been done.

Details, details

The way the system works is to schedule a Runnable when the RecyclerView starts a scroll. This Runnable is responsible for prefetching items that are about to come into view based on the layout Manager and scrolling direction. Prefetching is not limited to a single entry. It can fetch multiple entries simultaneously, for example when using GridLayoutManager and a new row is about to appear. In version 25.1, prefetch operations were split into separate create/bind operations, making it easier to fit into the GAP of the UI thread than an entire set of entries.

Interestingly, the system must predict how long operations will take and whether they can be put into the void. After all, if prefetching pushes the current frame past the deadline, we’ll still feel stuck due to frame drops, just for a different reason than if we didn’t prefetch. The way the system handles these details is by tracking the average create/bind time for each view type, making it possible to make reasonable predictions of future create/bind times.

Doing this for nested recyclerViews (each item is itself a RecyclerView container) is more complicated because binding the internal RecyclerView does not involve the allocation of any child controls — the RecyclerView takes child controls on demand as it is bound and distributed. The prefetch system can still pre-prepare the inner RecyclerView internal child controls, but it must know how many there are. This is the version 25.1 LinearLayoutManager new API setInitialItemPrefetchCount (). It tells the system how many items need to be prefetched to fill the RecyclerView as it scrolls.

I want some — where can I get it?

Prefetch optimization was introduced in Support Library V25 and improved in V25.1.0. So the first step is to download the latest version of the support library.

If you use the default Layout Manager provided by RecyclerView, you will automatically get this optimization. However, if you use nested RecyclerView or write your own Layout Manager, you will need to change your code to take advantage of this feature.

For nested RecyclerView, to get the best performance, in the internal LayoutManager calls LinearLayoutManager setInitialItemPrefetchCount () method (version 25.1 is available). Vertical direction for example, if you show a list of at least three entries, call setInitialItemPrefetchCount (4).

If you achieved his LayoutManager, you need to rewrite the LayoutManager collectAdjacentPrefetchPositions () method. This method is called by RecyclerView when prefetching is turned on (the default implementation of LayoutManager does nothing). Second, in the lining of the nested RecyclerView, if you want your LayoutManager prefetching data, you should also realize LayoutManager. CollectInitialPrefetchPositions ().

As before, it pays to optimize your creation and binding steps with as little work as possible. The fastest code to run is code that doesn’t need to be run at all; Even though the framework can work in parallel with data prefetching, it still consumes time, and long item creation can still lead to stalling. For example, a minimal View tree is easier to create and bind than a complex one. Essentially, binding should be just as convenient and just as fast as calling setters. Even if you can get the job done within the frame limit with current code, further optimization means it will be more likely to work well on low-end user models. In addition, saving performance for these common scenarios on high-end devices is always good for the battery. If you have minimized the creation and binding time, prefetching will help you shorten the remaining time between frames.

If you want to see the actual optimization, in the default or custom LayoutManager, you can switch LayoutManager. SetItemPrefetchEnabled () and compare the results. You should be able to see the difference visually; It’s really remarkable, especially if entries take a lot of time to create and bind. But if you want to know what’s going on under the surface, run Systrace on and off prefetch, or turn on GPU Profiling.

Systrace displays data prefetch when the UI thread is idle.

GOTO the end

Check out the latest Support Library and play with a RecyclerView that prefetchable data. In the meantime, I will continue not to clean my room.