This article has authorized WeChat official account. guolin_blog ( Guo Lin ) Exclusive release

Recently under study RecyclerView Recycling and reuse mechanism of , By the way . We know ,RecyclerView stay layout son View
Time , All are managed by recycling mechanism . There are a lot of articles about the analysis and explanation of recycling mechanism on the Internet , The analysis is very detailed , What level 4 cache , Go first mChangedScrap
Where to pick it up or something like that ; But in fact , What I want to say is ,RecyclerView
The recycling mechanism of is really perfect , Covering various scenes , But not every scenario will go through all the processes of the mechanism when it is recycled . For example , stay
setLayoutManager,setAdapter,notifyDataSetChanged
Or when sliding and so on, these scenarios will trigger the work of recycling mechanism . But if it's just RecyclerView When the recycling mechanism triggered by the sliding scene works , In fact, it doesn't need to involve all four levels of cache .


emmm, I'm still a little confused , Then keep reading , I will analyze it slowly and little by little . This article will not be like other great gods' articles , Analyze the recycling mechanism source line by line , I don't have the ability , So I will analyze the source code based on a specific scenario , It will be easier to understand . End of bullshit , Start on the right track .

Main topic

RecyclerView The internal implementation of the recycling and reuse mechanism of Recycler Inner class implementation , The following are all explained with such a page sliding scene RecyclerView
Recycling and reuse mechanism of .



Corresponding version :
RecyclerView: recyclerview-v7-25.1.0.jar
LayoutManager: GridLayoutManager extends LinearLayoutManager
(recyclerview-v7-25.1.0.jar)

Each line of this page can be displayed 5 Card positions , For each card position item layout type agreement .

Before analyzing the recycling mechanism , A few questions first :

Q1: If sliding down , New line 5 The display of card bits will be de multiplexed
ViewHolder, First line 5 Card positions will be removed from the screen and recycled , So in the process , Reuse before recycling ? Recycle before reuse ? Or recycle and reuse ? in other words , New line 5 Multiple card positions
ViewHolder Maybe the first line was recycled 5 Is there a card seat ?

Before the second question , Look at some pictures first :



Black box indicates screen ,RecyclerView Slide down first , The third line shows the card position , Slide up again , The third line moves out of the screen , The first line is displayed . We are at Adapter Of
onCreateViewHolder() and onBindViewHolder() Internal log , Here is the log of this process :



Red box 1 yes RecyclerView Log of slide down operation , The third line 5 The display of each card position is recreated ViewHolder
; Red box 2 Is the log when you slide up again , first line 5 For redisplay of card positions ViewHolder It's all reusable , Because no create viewHolder
Log of , And then just the back 3 Card bits rebind data , Called onBindViewHolder(); So here comes the question... :

Q2: In the process , Why should we RecyclerView Slide up again to redisplay the 5 Card position , Only the back 3 Card position triggered
onBindViewHolder() method , Rebind the data ? Mingming 5 Each card is reused .

Based on the above operation , Let's move on :



Based on the operation of the second question , It has been created 15 individual ViewHolder, The display is No 1,2 Card position of line , Then continue to slide down twice , The log of this process is as follows :


Red box 1 Is the log of the second problem operation , It's just to show that the next log will continue to operate on the basis of the above ;

Red box 2 Is the log of the first downward slide , Contrast question 2 Log of , This third line 5 For each card position ViewHolder They're all reused , And there's only the back 3 Card position triggered
onBindViewHolder() Rebind data ;

Red box 3 Is the log for the second slide down , This time in line four 5 Card positions , Front 3 For card positions of ViewHolder It's reusable , behind 2 Card position ViewHolder
Is recreated , and 5 All the cards are called onBindViewHolder() Rebind data ;

that ,

Q3: And then slide up and down , Slide several times , No more onCreateViewHolder() The Journal of , in other words RecyclerView
A total of 17 individual ViewHolder, But sometimes in a row 5 There are only 3 Card bits need to be rebound , Sometimes 5 Data rebinding is required for all card positions , Why is that ?

If you understand RecyclerView Recycling and reuse mechanism of , Then all three problems will be solved ; conversely , If you know the cause of these three problems , So understand RecyclerView
The recycling and reuse mechanism is simpler ; therefore , With questions , When analyzing the source code in a specific scenario , It should be easier .

Source code analysis

actually , According to the question 2 Log of , We can answer the questions 1 了 . Display at present 1,2 That's ok ,
ViewHolder The number of 10 On the basis of , On the third line 5 New card positions need to be recreated before they can be displayed
ViewHolder, in other words , In this downward sliding process , yes 5 The reuse mechanism of new card bits works first , And then 1 OK 5 Card positions removed from the screen are recycled .

that , Let's first look at the source code of the reuse mechanism

Reuse mechanism

getViewForPosition()







This method is the entrance of reuse mechanism , that is Recycler Open to external users api, You can call this method externally to return what you want View, And as for this View
It's reuse , Or recreated , All by Recycler Internal implementation , Hide from the outside .

tryGetViewHolderForPositionByDeadline()

therefore ,Recycler The internal implementation of the reuse mechanism of is in this method .
Before analyzing logic , Have a look first Recycler Some structures of , Used to cache ViewHolder Of .



mAttachedScrap: Used to cache the item Of ViewHolder, The scene seems to be RecyclerView stay onLayout
I'll put it first children All removed , Add it again , So this List It should be used for temporary storage during layout children Of , Anyway RecyclerView
You won't find reuse in this during sliding ViewHolder That's it .

mChangedScrap: I don't understand why this is used , Look at the name ViewHolder When the data of , stay RecyclerView
In the process of sliding , I didn't find any reuse here ViewHolder, So this one can be put aside for a while .

mCachedViews: This is much more important , Recycling and reuse in the sliding process are all handled first List, It's in this collection ViewHolder
The original data and information of , So it can be added directly to RecyclerView Show in , No need to re onBindViewHolder().

mUnmodifiableAttachedScrap: I don't know why , Skip for now .

mRecyclerPool: This is also important , But it's here ViewHolder Will be reset , amount to ViewHolder
It's a new one , So we need to call it again onBindViewHolder To bind data .

mViewCacheExtension: This is for our own expansion , It doesn't seem to work , I will not analyze it for the time being .

So let's look at the logic of reuse :



The first step is simple ,position If item Beyond the scope of , Let's throw the exception away . Keep looking down



If it's in isPreLayout() Time , Then go mChangedScrap Middle search .
So this isPreLayout What is it ? There are two assignments .





emmm, it seems , stay LayoutManager Of onLayoutChildren It will be set as
false, But I still don't understand what this process is about , It seems that
mState.mInPreLayou = false, So it's not coming here , Skip for now . Keep going .



Follow up on this method



first , go mAttachedScrap Looking for position coincident viewHolder, Some conditions need to be matched , About this viewHolder
Not removed , It's a valid condition or something , If you're satisfied, go back to this viewHolder.

therefore , The key here is to understand this mAttachedScrap In the end what is it? , What is the deposit ViewHolder.

Operation of a remote control key , Whether or not it slips , Will lead to RecyclerView Of onLayout, Then layout The words ,RecyclerView
I'll put all the children before remove fall , And then again add up , Complete once layout The process of . So this is temporary remove Dropped
viewHolder Where to store it , Here it is mAttachedScrap Yes , That's what I understand .

therefore , Feel this mAttachedScrap Stored in viewHolder It has little to do with recycling and reuse .

There are some analysis articles on the Internet ,RecyclerView When reusing, it will go in order mChangedScrap, mAttachedScrap
Wait, wait, look in the cache , If you don't find it, go down there , From the code point of view, that's right , But I think there's something wrong with that . Because this article is based on RecyclerView
For the sliding scene of , Reuse of new card and recovery mechanism of old card , It doesn't even involve mChangedScrap and
mAttachedScrap, So I think it's better to analyze the corresponding recycling mechanism based on a certain scenario . Like mChangedScrap
I don't understand what it's for , But I guess it should be a reuse scenario that will only be involved when the data changes , So when I analyze reuse based on sliding scenes , Even if I don't understand this , It's not going to have a big impact .

Keep looking down



emmm, I still don't understand this passage , But it is estimated that some reuse strategies used in specific scenarios should be needed , Look at the name , Should follow hidden
of ? Don't you understand? , Skip this paragraph , It should be ok , Recycling in the sliding process should have little to do with this .



Here's the point , Take notes take notes , Reuse in a sliding scenario uses the mechanism here .

mCachedViews The default size of is 2. ergodic mCachedViews, find position coincident
ViewHolder, As I said before ,mCachedViews Stored in ViewHolder Data and information of , therefore mCachedViews
It can be understood as , Only the original card can reuse this ViewHolder, The new position of the card cannot be changed from mCachedViews Lena ViewHolder Come out and use it .

find viewholder after



even if position Match found ViewHolder, You need to judge this ViewHolder Has it been remove fall ,type
Inconsistent types , as follows .



The above is in mCachedViews Looking for , If we don't find it , Just keep looking , Just passed position
Come here , Then this time id, Then repeat the above steps and find again , as follows



getScrapOrCachedViewForId() What we do getScrapOrHiddenOrCacheHolderForPosition()
In fact, it's almost the same , Only one is through position Come here ViewHolder, One is through id Come here . And this id It's not that we're here xml Set in
android:id, But Adapter A property held by , This property will not be used by default , So this 5 It's not going to happen , Unless we rewrite it Adapter Of
setHasStableIds(), Since it's not a common scene , Let's skip it , So keep going .



This is often called extension class ,RecyclerView Extension classes provided for our custom implementation , We can rewrite getViewForPositionAndType()
Method to implement its own reuse strategy . however , It didn't work , Then this part will not be implemented , Skip . Keep going



Here's the point , Take notes take notes .

This is to RecyclerViewPool Take from inside ViewHolder,ViewPool According to different item type Create different List, each
List The default size is 5 individual . Take a look ViewPool How to find it



As I said before ,ViewPool According to different viewType Create different collections to store ViewHolder, When reusing , as long as ViewPool Same in
type Yes ViewHolder Cached words , Just take the last one out and reuse it , Not like mCachedViews Various matching conditions are required , It can be reused as long as it has .

Keep looking " Fig 7 step " Later code , Get it ViewHolder after , It will be called again resetInternal() To reset ViewHolder, such
ViewHolder Can be treated as a brand new ViewHolder Here we go , That's why it's taken from here ViewHolder All need to be renewed
onBindViewHolder() 了.

Then if ViewPool I still haven't found it , Keep looking down



If ViewPool None of them ViewHolder To use , Then call Adapter Of onCreateViewHolder To create a new
ViewHolder use .

There are many steps above ViewHolder, No matter what step , Just find ViewHolder
The words , Then we don't need to worry about the next steps , Then we need to continue to judge whether we need to rebind the data , Also check whether the layout parameters are legal . as follows :



Come here ,tryGetViewHolderForPositionByDeadline() That's the end of it . This is about RecyclerView
Reuse mechanism of , In the middle, we jumped a lot , because RecyclerView There are various scenes to refresh him view, For example, re setLayoutManager(), again
setAdapter(), perhaps notifyDataSetChanged(), Or slide and so on , Just try again layout, It's going to be recycled and reused
ViewHolder, So this reuse mechanism needs to consider various scenarios .

It's a bit hard to chew through the lines of code , So I just use RecyclerView To analyze the recycling and reuse mechanism involved .

Let's analyze the recycling mechanism

Recovery mechanism

There are many entrances to the recycling mechanism , because Recycler There are various structures , such as mAttachedScrap,mCachedViews
wait , The recovery time of different structures is different , There are more entrances .

therefore , Or based on RecyclerView Under the sliding scene of , When the card position of the screen is removed for recycling, the entrance is :



The slide scene of this analysis , stay RecyclerView When sliding , Will be handed over to LinearLayoutManager Of scrollVerticallyBy()
To deal with , then LayoutManager Will then call fill() Method to deal with the card bits that need to be reused and recycled , In the end 调用上述 recyclerView()
这个方法开始进行回收工作.











回收的逻辑比较简单,由 LayoutManager 来遍历移出屏幕的卡位,然后对每个卡位进行回收操作,回收时,都是把 ViewHolder 放在
mCachedViews 里面,如果 mCachedViews 满了,那就在 mCachedViews 里拿一个 ViewHolder 扔到 ViewPool
缓存里,然后 mCachedViews 就可以空出位置来放新回收的 ViewHolder 了.

总结一下:

RecyclerView 滑动场景下的回收复用涉及到的结构体两个:
mCachedViews 和 RecyclerViewPool

mCachedViews 优先级高于 RecyclerViewPool,回收时,最新的 ViewHolder 都是往 mCachedViews
里放,如果它满了,那就移出一个扔到 ViewPool 里好空出位置来缓存最新的 ViewHolder.

复用时,也是先到 mCachedViews 里找 ViewHolder,但需要各种匹配条件,概括一下就是只有原来位置的卡位可以复用存在
mCachedViews 里的 ViewHolder,如果 mCachedViews 里没有,那么才去 ViewPool 里找.

在 ViewPool 里的 ViewHolder 都是跟全新的 ViewHolder 一样,只要 type
一样,有找到,就可以拿出来复用,重新绑定下数据即可.

整体的流程图如下:(可放大查看)



最后,解释一下开头的问题

Q1:如果向下滑动,新一行的5个卡位的显示会去复用缓存的
ViewHolder,第一行的5个卡位会移出屏幕被回收,那么在这个过程中,是先进行复用再回收?还是先回收再复用?还是边回收边复用?也就是说,新一行的5个卡位复用的
ViewHolder 有可能是第一行被回收的5个卡位吗?

答:先复用再回收,新一行的5个卡位先去目前的 mCachedViews 和 ViewPool
的缓存中寻找复用,没有就重新创建,然后移出屏幕的那行的5个卡位再回收缓存到 mCachedViews 和 ViewPool
里面,所以新一行5个卡位和复用不可能会用到刚移出屏幕的5个卡位.

Q2: 在这个过程中,为什么当 RecyclerView 再次向上滑动重新显示第一行的5个卡位时,只有后面3个卡位触发了
onBindViewHolder() 方法,重新绑定数据呢?明明5个卡位都是复用的.

答:滑动场景下涉及到的回收和复用的结构体是 mCachedViews 和
ViewPool,前者默认大小为2,后者为5.所以,当第三行显示出来后,第一行的5个卡位被回收,回收时先缓存在 mCachedViews,满了再移出旧的到
ViewPool 里,所有5个卡位有2个缓存在 mCachedViews 里,3个缓存在 ViewPool,至于是哪2个缓存在
mCachedViews,这是由 LayoutManager 控制.

上面讲解的例子使用的是 GridLayoutManager,滑动时的回收逻辑则是在父类 LinearLayoutManager
里实现,回收第一行卡位时是从后往前回收,所以最新的两个卡位是0,1,会放在 mCachedViews 里,而2,3,4的卡位则放在 ViewPool 里.

所以,当再次向上滑动时,第一行5个卡位会去两个结构体里找复用,之前说过,mCachedViews 里存放的 ViewHolder
只有原本位置的卡位才能复用,所以0,1两个卡位都可以直接去 mCachedViews 里拿 ViewHolder 复用,而且这里的 ViewHolder
是不用重新绑定数据的,至于2,3,4卡位则去 ViewPool 里找,刚好 ViewPool 里缓存着3个
ViewHolder,所以第一行的5个卡位都是用的复用的,而从 ViewPool 里拿的复用需要重新绑定数据,才会这样只有三个卡位需要重新绑定数据.

Q3:接下去不管是向上滑动还是向下滑动,滑动几次,都不会再有 onCreateViewHolder() 的日志了,也就是说 RecyclerView
总共创建了17个 ViewHolder,但有时一行的5个卡位只有3个卡位需要重新绑定数据,有时却又5个卡位都需要重新绑定数据,这是为什么呢?

答:有时一行只有3个卡位需要重新绑定的原因跟Q2一样,因为 mCachedView 里正好缓存着当前位置的 ViewHolder,本来就是它的
ViewHolder 当然可以直接拿来用.而至于为什么会创建了17个 ViewHolder,那是因为再第四行的卡位要显示出来时,ViewPool
里只有3个缓存,而第四行的卡位又用不了 mCachedViews 里的2个缓存,因为这两个缓存的是6,7卡位的 ViewHolder,所以就需要再重新创建2个
ViewHodler 来给第四行最后的两个卡位使用.


最近刚开通了公众号,想激励自己坚持写作下去,初期主要分享原创的Android或Android-Tv方面的小知识,感兴趣的可以点一波关注,谢谢支持~~