RecyclerView Optimization - Scrolling Performance Improvement

Authors
RecyclerView Optimization - Scrolling Performance Improvement

We can make RecyclerView scrolling smooth by using an image loading library, setting the image width and height, doing less work in onBindViewHolder, avoiding nested views, and using APIs like setHasFixedSize and setItemViewCacheSize.

In this blog, we are going to learn how to optimize the RecyclerView performance in Android. With these optimizations, we can make the RecyclerView scrolling smooth.

I am Amit Shekhar, Founder @ Outcome School, I have taught and mentored many developers, and their efforts landed them high-paying tech jobs, helped many tech companies in solving their unique problems, and created many open-source libraries being used by top companies. I am passionate about sharing knowledge through open-source, blogs, and videos.

I teach AI and Machine Learning, and Android at Outcome School.

Join Outcome School and get a high-paying tech job:

Let's get started.

When we implement RecyclerView in our Android Application, sometimes, we face problems like: The RecyclerView items are not scrolling smoothly. It leads to bad user experience as it seems that our Android App is laggy.

Let's see what are the things which we can do to improve the performance of the RecyclerView and hence, we get the smooth scrolling.

RecyclerView Optimization Techniques

Use Image-Loading Library

As the Garbage Collection(GC) runs on the main thread, one of the reasons for unresponsive UI is the continuous allocation and deallocation of memory, which leads to the very frequent GC run. By using the bitmap pool concept, we can avoid it.

The best part is that Image-Loading libraries like Glide, Fresco uses this bitmap pool concept. So, always use Image-Loading libraries.

Delegate all the image-related tasks to these libraries.

If you are curious to know: How The Android Image Loading Library Glide and Fresco Works? Read here.

Set Image Width and Height

If our image width and height are dynamic(not fixed), and we are getting the imageUrl from the server.

If we do not set the correct image width and height prior, the UI will flicker during the transition of loading(downloading of image) and setting of the image into the ImageView(actually making it visible when downloading completes).

So, we should ask our backend developer to send the image size or the aspect ratio, accordingly, we can calculate the required width and height of the ImageView.

Then, we will be able to set the width and height prior only. Hence no flickering. Problem Solved!

Do less in onBindViewHolder method

Our onBindViewHolder method should do very less work. We should check our onBindViewHolder method and optimize it. We can see the improvement in our RecyclerView by doing so.

Use Notify Item RecyclerView API

Whenever we have the use-case of the removal, the updation, the addition of item, use the Notify Item API.

adapter.notifyItemRemoved(position)
adapter.notifyItemChanged(position)
adapter.notifyItemInserted(position)
adapter.notifyItemRangeInserted(start, end)

In case, we are forced to use notifyDataSetChanged() based on our use-case, we can try the setHasStableIds(true).

adapter.setHasStableIds(true)

It indicates whether each item in the data set can be represented with a unique identifier of type Long.

Even if we call the notifyDataSetChanged(), it does not have to handle the complete re-ordering of the whole adapter because it can find if the item at a position is the same as before and do less work.

Avoid a nested view

If possible, we should avoid a nested view and try to create a flat view wherever possible. Nesting reduces RecyclerView performance. Flat View improves performance.

A quick note for you

No matter which tech domain you work in, get familiar with these topics:

  • LLM
  • RAG
  • MCP
  • Agent
  • Fine-tuning
  • Quantization

We put it all together in one video:

AI Engineering Explained: LLM, RAG, MCP, Agent, Fine-Tuning, and Quantization

No need to stop reading - bookmark it and watch later when you get time. Future you will thank you.

Now, let's get back to the topic.

Use setHasFixedSize

We should use this method if the height of all the items is equal. Add the below and check the performance.

recyclerView.setHasFixedSize(true)

Use setRecycledViewPool for Optimizing Nested RecyclerView

As we know that the RecyclerView works on the principle of "Reuse View Using Pool" wherever possible.

If we have the use-case of Nested RecyclerView.

- OuterRecyclerView
    - InnerRecyclerViewOne
    - InnerRecyclerViewTwo

But by default, the optimization works for that particular RecyclerView because that particular RecyclerView has its own View Pool.

The pool does not get shared between two RecyclerViews having the same types of views.

So, what we can do is that we can create a single ViewPool and pass it to all the inner RecyclerViews so that it gets shared like below:

class OuterRecyclerViewAdapter() : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    // code removed for brevity

    private val viewPool = RecyclerView.RecycledViewPool()

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {

        // code removed for brevity

        viewHolderOne.innerRecyclerViewOne.setRecycledViewPool(viewPool)

        // code removed for brevity

        viewHolderTwo.innerRecyclerViewTwo.setRecycledViewPool(viewPool)

    }

    // code removed for brevity
}

This will improve scrolling performance as it will start reusing the view from the shared ViewPool.

Stay updated: Subscribe to our newsletter to get our latest AI and Machine Learning blogs straight to your inbox.

Use setItemViewCacheSize

We can experiment by setting the ItemView Cache Size.

recyclerView.setItemViewCacheSize(cacheSize)

As per the official documentation: It sets the number of offscreen views to retain before adding them to the potentially shared recycled view pool. The offscreen view cache stays aware of changes in the attached adapter, allowing a LayoutManager to reuse those views unmodified without needing to return to the adapter to rebind them.

It means that when we scroll the RecyclerView such that there's a view that is just barely completely off-screen, the RecyclerView will keep it around so that we can scroll it back into view without having to call the onBindViewHolder() again.

Generally, we do not change the size of the View Cache, but experiment with it, if it works for you, implement it.

These are things that we can do to improve the performance of RecyclerView. That's it for now.

Frequently Asked Questions

Why does frequent Garbage Collection make RecyclerView scrolling laggy?

Garbage Collection runs on the main thread. The continuous allocation and deallocation of memory leads to very frequent GC runs, which makes the UI unresponsive. Image-loading libraries like Glide and Fresco use the bitmap pool concept to avoid this, so we should delegate image-related tasks to them.

Why does the RecyclerView flicker when images load?

The UI flickers when the image width and height are dynamic and not set before the image is downloaded. We can ask the backend to send the image size or aspect ratio, calculate the required width and height of the ImageView, and set them in advance. Then there is no flickering.

What can we do if we are forced to call notifyDataSetChanged?

We can try setHasStableIds(true). It indicates that each item can be represented with a unique identifier. Then, even when we call notifyDataSetChanged(), the RecyclerView can find whether the item at a position is the same as before and do less work.

Do nested RecyclerViews share the same view pool by default?

No. By default, each RecyclerView has its own view pool, and the pool does not get shared between two RecyclerViews having the same types of views. We can create a single RecycledViewPool and pass it to all the inner RecyclerViews so that they reuse views from the shared pool.

Should we always change the item view cache size?

No. Generally, we do not change the size of the view cache. We can experiment with setItemViewCacheSize, and if it improves scrolling for our use case, we keep it. It lets the RecyclerView scroll a barely off-screen view back without calling onBindViewHolder() again.

Show your love by sharing this blog with your fellow developers.

To master RecyclerView optimization, ViewPool, Bitmap Pool, and App Memory Usage hands-on, check out the Android Program by Outcome School where we go deep into these performance topics.

That's it for now.

Thanks

Amit Shekhar
Founder @ Outcome School

You can connect with me on:

Follow Outcome School on:

Read all of our high-quality blogs here.