RecyclerView
的缓存机制包括了那两方面?
RecyclerView
缓存和复用的都是ViewHolder
,一个ViewHolder
相当于是列表中的一个item
,因为ViewHolder
类中就包含了我们的itemView
变量;
然后我们可以从RecylerView
的onTouchEvent()
滑动事件处理过程ACTION_MOVE
中去分析缓存和复用,也可以从RecyclerView
中的布局onLayout
中去分析缓存和复用.
从缓存中取出ViewHolder
复用时不会调用onCreateViewHolder
和onBinderViewHolder方法
,从缓存池中取出ViewHolder
复用时会调用onBinderViewHolder
方法;如果从缓存和缓存池中都没有取到ViewHolder
,则会依次调用onCreateViewHolder
和onBinderViewHolder
方法.
- 缓存
- 复用
RecyclerView
的缓存机制?
onLayout()
- 切入点,在布局的时候去分析缓存机制;或者在
fill()
方法中的前面部分逻辑recycleByLayoutState()
方法中分析,最后会调用recycleViewHolderInternal()
方法,先做缓存操作,然后再处理复用
- 切入点,在布局的时候去分析缓存机制;或者在
- dispatchLayout()->dispatchLayoutStep1()和dispatchLayoutStep2()中都会执行onLayoutChildren()
- (
LinearLayoutManager
)mLayout.onLayoutChildren
(mRecycler, mState)- 根据水平布局或者是竖直布局去分析对应LayoutManager,这里我们分析竖直布局
detachAndScrapAttachedViews()
- 在这个方法中会调用
fill()
方法,这个方法的逻辑与RecyclerView复用
机制相同
- 在这个方法中会调用
scrapOrRecycleView()
- recycler.recycleViewHolderInternal(viewHolder)
mCachedViews.add(targetCacheIndex, holder)
- 将
ViewHolder
添加到缓存mCacheViews
中,如果缓存满了,也就是缓存大小是2
,此时需要将缓存mCacheViews
中第1
个元素添加到缓存池中,然后将缓存中第1
个元素删除,最后将新来的ViewHolder
添加到缓存中; - 从缓存中取的第
1
个元素在添加到缓存池的过程中,如果缓存池大小也满了,也就是缓存池大小是5
,此时就不再对从缓存取出的第1
个元素做添加到缓存池的操作;但是在判断缓存池满之前,都会对这些添加到缓存池中的元素做数据清空操作.
- 将
addViewHolderToRecycledViewPool(holder, true)
- 将
ViewHolder
添加到缓存池,缓存池对象中包含了一个稀疏矩阵(SparseArray)scrap
,ScrapData对象中包含了了这种类型对应的集合(ArrayList)mScrapHeap
,这个集合就是这种类型的缓存池,注意每种类型的缓存池大小都是5
,在ViewHolder
添加到缓存池之前,需要清除ViewHolder
包含的数据,使得每个缓存池中的数据都是清空过数据的相同ViewHodler
,当我们从缓存池中取出数据时,只需要调用数据绑定方法onBindViewHolder()
即可,不需要再调用onCreateViewHolder()
方法
- 将
- recycler.scrapView(view)
屏幕内的缓存,达到性能优化的目的
mChangedScrap.add(holder)
- 用来刷新单个item而不是全局刷新
mAttachedScrap.add(holder)
- 用来做动画处理
- recycler.recycleViewHolderInternal(viewHolder)
总结:
根据View
的绘制流程中的布局onLayout()
作为切入点,去分析我们的RecyclerView
中的缓存机制;根据条件将ViewHolder
添加到mChangedScrap
或者mAttachedScrap
缓存集合中去;如果没有添加到这两个缓存集合中去,就将ViewHolder
添加到mCacheViews
缓存集合中去;如果mCacheViews
缓存集合已满,则将mCacheViews
缓存集合中第一个元素取出添加到缓存池mScrapHeap
中去,同时将mCacheViews
缓存集合中第一个元素移除.
RecyclerView
的复用机制?
- onTouchEvent()->ACTION_MOVE
切入点:
滑动过程中分析复用机制
- scrollByInternal()->scrollStep()->
- (
GridLayoutManager
)mLayout.scrollHorizontallyBy()- 这个水平滚动方法的流程和下面竖直方法滚动流程类似,所以就分析竖直滚动方法的流程
- (
LinearLayoutManager
)mLayout.scrollVerticallyBy()->scrollBy()- 这里分析实现类
LinearLayoutManager
中的scrollVerticallyBy()
方法,因为GridLayoutManager
也是继承自LinearLayoutManager
- 这里分析实现类
fill()
layoutChunk()
->View view = layoutState.next(recycler);- (
RecyclerView.Recycler
)recycler.getViewForPosition() tryGetViewHolderForPositionByDeadline()
这个方法中就会去从缓存中去取ViewHolder,如果缓存中取到的ViewHolder为null,则会创建ViewHoldler并绑定数据
- getChangedScrapViewForPosition()
- 从
mChangedScrap
缓存集合中取ViewHolder
- 从
- getScrapOrHiddenOrCachedHolderForPosition()
- 从
mAttachedScrap
和mCachedViews
缓存集合中通过position
取ViewHolder
- 从
- getScrapOrCachedViewForId()
- 从
mAttachedScrap
和mCachedViews
缓存集合中通过stableId
取ViewHolder
,缓存mCachedViews
的最大值是2
,int DEFAULT_CACHE_SIZE = 2
,我们可以通过这个方法更改缓存大小:setViewCacheSize(int viewCount)
- 从
- mViewCacheExtension.getViewForPositionAndType(this, position, type)
- 自定义复用,缓存需要自己实现
- getRecycledViewPool().getRecycledView(type)
- 从
mScrapHeap
缓存集合中取ViewHolder
,缓存池的最大值为5
,int DEFAULT_MAX_SCRAP = 5
,我们可以通过这个方法更改缓存池大小:setMaxRecycledViews(int viewType, int max)
- 从
- mAdapter.createViewHolder
- 最终调用
Adapter
的onCreateViewHolder()
方法创建ViewHolder
- 最终调用
- tryBindViewHolderByDeadline
- 最终调用
Adapter
的onBinderViewHolder()
方法绑定数据到ViewHolder(item)
上
- 最终调用
- getChangedScrapViewForPosition()
总结:
RecyclerView.Recycler
内部类就是用来做缓存和复用的,里面包含了ViewHolder
的缓存集合ArrayList<ViewHolder>
:
mChangedScrap
存储单个需要刷新的item
,局部刷新(屏幕内缓存
,达到性能优化的目的)mAttachedScrap
存储动画(屏幕内缓存
,达到性能优化的目的)mCachedViews
缓存大小最大值是2
(屏幕外缓存
)mViewCacheExtension
自定义扩展类,需要自己实现缓存和服用mRecyclerPool
缓存池类,里面包含了一个集合(SparseArray<ScrapData>)mScrap
,缓存池的key是类型ViewType
,value是ScrapData
对象,ScrapData
对象里面包含了集合(ArrayList<ViewHolder>)mScrapHeap
,缓存池大小最大值是5
(屏幕外缓存池
)