Recycler View Internals - I: Birth Of ViewHolder
Implementation of Recycler View ins and outs from Google-I/O 2016

Featured on :


Special thanks to Matt M. for reviewing and providing feedbacks to improving the article
We all have been working with RecyclerViews
for way long and are very familiar using its API to display our list efficiently, but many have just touched its surface on a very high level, few of us have dug deep into it and tried to figure how internally it works. I’m one of the same, I have a generalised idea of what might be happening but I never looked at its implementation from Android open source project (AOSP) but before I do that I wanted to figure out how others in the community have explored it.
After some poking around I was told by many developer to follow talk on RecyclerViews ins and outs - Google I/O 2016 for getting an idea of what RecyclerViews
are doing under the hood. If you haven’t seen it yet, I would highly recommend you to watch this video, in-fact bookmark it you will be visiting it a lot in your android career.
It actually does a pretty nice job explaining various components of RecyclerView
and those diagrams are ❤️ . But that is also the problem with the video, even though there are so many details in the video but everything is just in form of diagrams and flows. We don’t see any code examples or implementations that we can relate to understand Recycler view more deeply.
Since that is not done, and I don’t want to hurt my soul by looking into AOSP, I have used decided to use my big 🧠 to implement these diagrams into workable code.
This video is divided into 4 part :
- Birth Of
ViewHolder
- Adding View to UI
- Death of
ViewHolder
- Animation on
RecyclerView
Operation
From the title of the article, you must have figured that I will be covering the first section i.e Birth Of View-Holder, before that lets look into RecyclerView
Components.
RecycleView Components
First half of the video introduce us to the various components of the RecyclerView
. Let’s make our classes accordingly to it.
p.s. In comments I have added responsibilities of each component.
/**
* Layout Manager :
* - Position the Views
* - Scrolling Views
*/
class LayoutManager
/**
* Recycler View :
* - Coordinator of all component
* - Interaction with Elements
*/
class RecyclerView
/**
* Adapter :
* - Create View and ViewHolder
* - binding data to ViewHolder
* - Multiple View Types
* - Recycle Recovery
* - notify data change and out of sync
*/
class Adapter
/**
* Item Animator :
* - Animates the views
*/
class ItemAnimator
/**
* View Holder :
* - item interaction handler
*/
class ViewHolder()
Enjoying the Post?
a clap is much appreciated if you enjoyed. No sign up or cost associated :)
If you want to support my content do consider dropping a tip/coffee/donation💰
You can clearly see Adapter
are expressed as View Providers but do lot more things than that like
- Binding data,
- Notify data changes,
- Recycle recovery,
- Working with Multiple view types and more
Lets get started with the Implementation
Birth Of View Holder
There three possible scenario encountered in flow of creating of ViewHolder
- View Cache Success
- View Cache Failed, Recycler Pool Success
- View Cache Failed and Recycler Pool Fails
View Cache Success ✅
In the first section, we learn that the LayoutManager
requests RecyclerView
for the View at a Position. Recycler is a very fast component because it tries to cache data on each step.
So instead of going through an expensive cycle of creating a new view, it will try to return it from the cache.
The scenario in which Recycler’s View Cache
Pass i.e it will return cached View
instance immediately satisfying layout manager request.
First flow use case results in the following component interaction,
Component interactions :
[Layout Manager] ---- getViewForPosition ----> [Recycler View]
[Recycler View] ---- getViewForPosition ----> [View Cache]
[View Cache] ---- cache returns View ✅ ----> [Recycler View]
[Recycler View] ---- returns View ----> [Layout Manager]
You can follow the sequence to see flow of control.

In Program this translates to :
// layout manager calls function getViewForPosition over recycler
// thus has a recycler.
class LayoutManager(private val recycler: Recycler) {
fun getViewForPosition(pos: Int): View {
return recycler.getView(pos)
}
}
class RecyclerView(
private val viewCache: ViewCache
) {
fun getView(pos: Int): View {
val viewOrNull = viewCache.getOrNull(pos)
// view found then return view
if (viewOrNull != null) {
return viewOrNull
}
return TODO()
}
}
class ViewCache {
private val viewCache = mutableMapOf<Int, View>() // LRU cache
fun getOrNull(pos: Int): View? = viewCache.get(pos)
}
If you don’t know what is TODO()
checkout my article from here :

View Cache Failed ❌, Recycler Pool Success ✅
When View Cache
Fails to return a view for a given position, it goes through series of steps to create a new view.
Let's break these steps down :
RecyclerView
after receiving failure fromViewCache
, askAdapter
to returnViewType
for the position.- After receiving
ViewType
,RecyclerView
checks itsRecyclerPool
to see if cache ofViewHolder
is present or not. - If
RecyclerPool
Passes i.e. returns aViewHolder
, thenRecyclerView
takes thatViewHolder
to theAdapter
, and tries tobind
new data to it. - Once bound
RecyclerView
takes ViewHolder’sView
and return it to theLayout Manager
.
Told you View Creation is expensive task. Let’s see component interaction in this process :
Component interactions :
[Layout Manager] ---- getViewForPosition ----> [Recycler View]
[Recycler View] ---- getViewForPosition ----> [View Cache]
[View Cache] ---- cache returns null ❌ ----> [Recycler View]
[Recycler View] ---- getViewType ----> [Adapter]
[Adapter] ---- ViewType ----> [Recycler View]
[Recycler View] ---- getViewHolderByType ----> [RecyclerPool]
[RecyclerPool] ---- ViewHolder ----> [Recycler View]
[Recycler View] ---- bindViewHolder ----> [Adapter]
[Adapter] ---- View ----> [Recycler View]
[Recycler View] ---- View ----> [Layout Manager]
You can follow sequence to see flow of control :

Enjoying the Post?
a clap is much appreciated if you enjoyed. No sign up or cost associated :)
If you want to support my content do consider dropping a tip/coffee/donation💰
In Program this translates to :
class LayoutManager(private val recycler: Recycler) {
fun getViewForPosition(pos: Int): View {
return recycler.getView(pos)
}
}
class RecyclerView(
private val adapter: Adapter,
private val recyclerPool: RecyclerPool,
private val viewCache: ViewCache
) {
fun getView(pos: Int): View {
val viewOrNull = viewCache.getOrNull(pos)
// view found then return view
if (viewOrNull != null) {
return viewOrNull
}
// if view not found in cache get view type
val viewType = adapter.getViewType(pos)
// from view type get viewholder from cache
val holderOrNull = recyclerPool.getViewHolderByType(viewType)
// view holder found -> bind data and return view
if (holderOrNull != null) {
adapter.bindViewHolder(holderOrNull, pos)
return holderOrNull.itemView
}
return TODO()
}
}
class ViewCache {
private val viewCache = mutableMapOf<Int, View>() // LRU cache
fun getOrNull(pos: Int): View? = viewCache.get(pos)
}
class RecyclerPool {
private val cache = mutableMapOf<ViewType, ViewHolder>() // LRU cache
fun getViewHolderByType(viewType: ViewType): ViewHolder? {
return cache.get(viewType)
}
}
abstract class Adapter {
abstract fun getViewType(pos: Int): ViewType
abstract fun bindViewHolder(holder: ViewHolder,pos:Int)
}
IMO, API is coming out nicely designed just by following the diagrams in the Video.
View Cache failed ❌, Recycler Pool failed ❌
RecyclerPool
is a cache of Viewholders
, what will happen if the Viewholder
you are looking for isn’t there in cache?
Let’s see :
- If
RecyclerPool
fails, it will returnnull
toRecyclerView
RecyclerView
will go toAdapter
and request it to create newViewHolder
from theViewType
.Adapter
will create a newViewHolder
andbind
it with thedata
for the requestedposition
.Adapter
will returnViewHolder
toRecyclerView
andRecyclerView
return theView
back toLayoutManger
.
So only one step is extra when RecyclerPool
fails, Adapter will create new Viewholder
and binds
the data.
Lets see the component interaction :
Component interactions :
[Layout Manager] ---- getViewForPosition ----> [Recycler View]
[Recycler View] ---- getViewForPosition ----> [View Cache]
[View Cache] ---- cache returns null ❌ ----> [Recycler View]
[Recycler View] ---- getViewType ----> [Adapter]
[Adapter] ---- ViewType ----> [Recycler View]
[Recycler View] ---- getViewHolderByType ----> [Recycler Pool]
[Recycler Pool] ---- returns ViewHolder ❌ ----> [Recycler View]
[Recycler View] ---- createViewHolder ----> [Adapter]
[Adapter] ---- bindViewHolder ----> [Adapter]
[Adapter] ---- ViewHolder ----> [Recycler View]
[Recycler View] ---- View ----> [Layout Manager]
Follow the sequence for flow of control :

In Program this translates to :
class LayoutManager(private val recycler: Recycler) {
fun getViewForPosition(pos: Int): View {
return recycler.getView(pos)
}
}
class Recycler(
private val adapter: Adapter,
private val recyclerPool: RecyclerPool,
private val viewCache: ViewCache
) {
fun getView(pos: Int): View {
val viewOrNull = viewCache.getOrNull(pos)
// view found then return view
if (viewOrNull != null) {
return viewOrNull
}
val viewType = adapter.getViewType(pos)
val holderOrNull = recyclerPool.getViewHolderByType(viewType)
// view holder found -> bind and return view
if (holderOrNull != null) {
adapter.bindViewHolder(holderOrNull,pos)
return holderOrNull.itemView
}
// create viewholder and bind viewholder and return view
val holder = adapter.createViewHolder(viewType)
adapter.bindViewHolder(holder,pos)
return holder.itemView
}
}
class ViewCache {
private val viewCache = mutableMapOf<Int, View>()
fun getOrNull(pos: Int): View? = viewCache.get(pos)
fun put(pos: Int, view: View) {
viewCache.put(pos, view)
}
}
abstract class Adapter {
abstract fun getViewType(pos: Int): ViewType
abstract fun bindViewHolder(holder: ViewHolder,pos:Int)
abstract fun createViewHolder(viewType: ViewType): ViewHolder
}
Adapter
and ViewHolder
is of abstract type, if you want to see how will their implementation looks like check the code below 👇:
/**
* Represent ViewTypes which user provides
*/
sealed class ViewType {
object ListItemHeader : ViewType()
object ListItemContent : ViewType()
}
/**
* Test View Holders
*/
class ContentHeader(view: View) : ViewHolder(view) {
fun bind(){...}
}
class ContentContent(view: View) : ViewHolder(view) {
fun bind(){...}
}
/**
* Implementation of Adapter Class
*/
class PractieAdapter : Adapter() {
/**
* Similar to getItemViewType() of Recycler Adapter
*/
override fun getViewType(pos: Int): ViewType {
return when (pos) {
0 -> ViewType.ListItemHeader
else -> ViewType.ListItemContent
}
}
/**
* Similar to onBindViewHolder() of Recycler Adapter
*/
override fun bindViewHolder(holder: ViewHolder, pos:Int) {
when (holder) {
is ContentHeader -> holder.bind()
is ContentContent -> holder.bind()
}
}
/**
* Similar to onCreateViewHolder() of Recycler Adapter
*/
override fun createViewHolder(viewType: ViewType): ViewHolder {
when (viewType) {
ViewType.ListItemContent -> TODO()
ViewType.ListItemHeader -> TODO()
}
}
}
Conclusion
Alrighty, the design logically appears to be correct till the point where the birth of the view holder is explained in the video.
Hope you like my implementation, if you don’t please suggest improvement in my repository where i’m experimenting with the code :
In the second part of the article, we will continue with how the view of the layout manager is added to the UI. So stay tuned.
If you like my content and want to see more, click right here to see more 👇

Until next time, Happy Hacking! 👨🏻💻
Enjoying the Post?
a clap is much appreciated if you enjoyed. No sign up or cost associated :)
If you want to support my content do consider dropping a tip/coffee/donation💰
Reach me out :
