RecyclerView的缓存机制
RecyclerView的使用相信大家都很熟悉了,可以完全代替ListView和GridView,并且口味更佳,例如
- 四级缓存,ListView只有两级缓存
- Item添加动画
- 数据局部跟新
- 瀑布流。当然也可以自定义LayoutManager,规则布局你说了算
- .....
本文只讲解缓存机制。先来看一张Structure图
写RecyclerView的大佬可能有内部类情节,所以整个RecyclerView下来有1.2w余行,这还不包括已经实现的几个LinearLayoutManager、DividerItemDecoration等等几个类,所以想看完整个源码是不可能的,看懂大概流程就可以了
四级缓存
RecyclerView的缓存由内部类Recycler负责,回收和复用都是以ViewHolder为单位
第一级缓存
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
ArrayList<ViewHolder> mChangedScrap = null;
第一级缓存有两个类型为ViewHolder的ArrayList集合,用来临时缓存屏幕中的Item。当数据更新重新绘制列表前会先清除所有Item,mChangedScrap保存被标记更新的Item,mAttachedScrap保存其他的Item
第二级缓存
final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
第二级缓存保存刚刚被移除屏幕,保存的ViewHolder都是有效的,默认最大缓存是2。先进先出原则,当大于最大缓存数时,会先移除第一个,再添加
第三级缓存
private ViewCacheExtension mViewCacheExtension;
第三级缓存Google没有实现,开发者一般也不会实现,可能是Google为了以后扩展。可以忽略
第四级缓存
RecycledViewPool mRecyclerPool;
第四级缓存保存无效的ViewHolder,从二级缓存放进去的,默认大小是5
RecycledViewPool有一个mScrap,保存不同类型的ViewHolder
SparseArray<RecyclerView.RecycledViewPool.ScrapData> mScrap = new SparseArray();
保存
Adapter和RecycledView之间是通过观察者模式来实现数据变化通知。当调用Adapter一系列notify方法时,Adapter会通知观察者,RecyclerView的观察者RecyclerViewDataObserver就会执行对应的方法
RecyclerViewDataObserver
public void onChanged() {
...
RecyclerView.this.requestLayout();
...
}
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
...
this.triggerUpdateProcessor();
...
}
void triggerUpdateProcessor() {
//先执行动画,最后还是跑不掉了requestLayout,代码不贴了
if (RecyclerView.POST_UPDATES_ON_ANIMATION && RecyclerView.this.mHasFixedSize && RecyclerView.this.mIsAttached) {
ViewCompat.postOnAnimation(RecyclerView.this, RecyclerView.this.mUpdateChildViewsRunnable);
} else {
RecyclerView.this.mAdapterUpdateDuringMeasure = true;
RecyclerView.this.requestLayout();
}
}
观察者的方法里都又直接间接地调用了RecyclerView的requestLayout(会执行View绘制的三部曲onMeasure、onLayout、onDraw),在onLayout经过层层调用到mLayout.onLayoutChildren,mLayout是LayoutManager,LayoutManager是抽象类,这里以LinearLayoutManager为例看一下onLayoutChildren方法的具体实现
RecyclerView.java
protected void onLayout(boolean changed, int l, int t, int r, int b) {
...
this.dispatchLayout();
...
}
void dispatchLayout() {
...
if (this.mState.mLayoutStep == 1) {
//预布局,记录数据更新时需要执行的动画的信息
this.dispatchLayoutStep1();
this.mLayout.setExactMeasureSpecsFrom(this);
//实际布局
this.dispatchLayoutStep2();
} else if (!this.mAdapterHelper.hasUpdates() && this.mLayout.getWidth() == this.getWidth() && this.mLayout.getHeight() == this.getHeight()) {
this.mLayout.setExactMeasureSpecsFrom(this);
} else {
this.mLayout.setExactMeasureSpecsFrom(this);
this.dispatchLayoutStep2();
}
//执行动画
this.dispatchLayoutStep3();
...
}
private void dispatchLayoutStep2() {
...
//布局
mLayout.onLayoutChildren(mRecycler, mState);
...
}
LinearLayoutManager.java
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
if (mPendingSavedState != null || mPendingScrollPosition != NO_POSITION) {
if (state.getItemCount() == 0) {
//1.清空列表,回收所有的View,return
removeAndRecycleAllViews(recycler);
return;
}
}
...
//2.临时回收
detachAndScrapAttachedViews(recycler);
...
}
调用流程,代码不粘了,很简单
- LayoutManager.removeAndRecycleAllViews ->
LayoutManager.removeAndRecycleViewAt ->
Recycler.recycleViewHolderInternal() - LayoutManager.detachAndScrapAttachedViews ->
Recycler.scrapOrRecycleView ->
Recycler.scrapView或者Recycler.recycleViewHolderInternal()
Recycler
void recycleViewHolderInternal(ViewHolder holder) {
...
if (mViewCacheMax > 0
&& !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
| ViewHolder.FLAG_REMOVED
| ViewHolder.FLAG_UPDATE
| ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
int cachedViewSize = mCachedViews.size();
if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
//超过最大缓存数,删除第一个
recycleCachedViewAt(0);
cachedViewSize--;
}
...
//存到第二级缓存
mCachedViews.add(targetCacheIndex, holder);
cached = true;
}
if (!cached) {
//没有存到第二季缓存的话,存第四级
addViewHolderToRecycledViewPool(holder, true);
recycled = true;
}
...
}
//临时缓存,数据未改变,可以直接使用
void scrapView(View view) {
final ViewHolder holder = getChildViewHolderInt(view);
if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
|| !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
holder.setScrapContainer(this, false);
//没有变化的
mAttachedScrap.add(holder);
} else {
if (mChangedScrap == null) {
mChangedScrap = new ArrayList<ViewHolder>();
}
holder.setScrapContainer(this, true);
//位置变化的
mChangedScrap.add(holder);
}
}
复用
在LayoutManager的onLayoutChildren里面调用fill()为RecyclerView填充Item,fill里面循环调用layoutChunk来addView,直到填满屏幕
LinearLayoutManager.java
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
...
fill(recycler, mLayoutState, state, false);
...
}
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
RecyclerView.State state, boolean stopOnFocusable) {
...
while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
layoutChunkResult.resetInternal();
layoutChunk(recycler, state, layoutState, layoutChunkResult);
if (layoutChunkResult.mFinished) {
break;
}
...
if (stopOnFocusable && layoutChunkResult.mFocusable) {
break;
}
...
}
...
return start - layoutState.mAvailable;
}
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
LayoutState layoutState, LayoutChunkResult result) {
...
View view = layoutState.next(recycler);
...
}
View next(RecyclerView.Recycler recycler) {
...
//通过position获取View
final View view = recycler.getViewForPosition(mCurrentPosition);
mCurrentPosition += mItemDirection;
return view;
}
Recycler
最终获取缓存都会调用Recycler的tryGetViewHolderForPositionByDeadline方法,这个方法里可以看到尝试从四级缓存中获取ViewHolder,如果获取不到就重新创建ViewHolder
public View getViewForPosition(int position) {
return getViewForPosition(position, false);
}
View getViewForPosition(int position, boolean dryRun) {
return tryGetViewHolderForPositionByDeadline(position,dryRun,FOREVER_NS).itemView;
}
//非常重要的一个方法
@Nullable
ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs) {
...
ViewHolder holder = null;
//通过mChangedScrap获取缓存
if (mState.isPreLayout()) {
holder = getChangedScrapViewForPosition(position);
fromScrapOrHiddenOrCache = holder != null;
}
...
//通过mAttachedScrap或者mCachedViews获取缓存
if (holder == null) {
holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
}
...
//通过StableId进行获取缓存,也是从mAttachedScrap或者mCachedViews获取
if (holder == null) {
...
if (mAdapter.hasStableIds()) {
holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition), type, dryRun);
}
//通过mViewCacheExtension获取缓存
if (holder == null && mViewCacheExtension != null) {
final View view = mViewCacheExtension.getViewForPositionAndType(this, position, type);
if (view != null) {
holder = getChildViewHolder(view);
}
}
//通过mRecyclerPool获取缓存
if (holder == null) {
holder = getRecycledViewPool().getRecycledView(type);
}
//只能创建ViewHolder
if (holder == null) {
holder = mAdapter.createViewHolder(RecyclerView.this, type);
}
return holder;
}
这就是RecyclerView的四级缓存机制,其实也就三级,ViewCacheExtension这里是空实现
脑瓜疼,想喝手磨咖啡
End