1、RecycleView四级缓存:
1.1、从上图中看出:RecycleView的缓存从上到下分为四层:scrap、cache、ViewCacheExtension、RecycleViewPool
1.2、RecycleView缓存的对象是ViewHolder,而RecycleView复用的对象也是ViewHolder
1.3、Recycler负责管理和缓存所有的ViewHolder。
2、四级缓存介绍:
2.1、scrap:
从图中可以看出,scrap是用来缓存正在显示的ViewHolder
scrap分为两个集合:mAttachedScrap和mChangedScrap
- mAttachedScrap用来缓存正在显示的ViewHolder
- mChangedScrap用来缓存屏幕上发生变化的ViewHolder,也可能是ViewHolder类型发生了变化,mChangedScrap中的ViewHolder会被移动到RecycleViewPool中
2.2、cached:
数据结构mCacheViews,用来缓存从屏幕中移除,但是可能很快被再次显示的ViewHolder
- 它是一个ArrayList类型,不区分ViewHolder的类型
- mCacheViews大小限制为2,但是可以使用setItemViewCacheSized()这个方法调整它的大小
2.3、ViewCacheExtension:
这个是需要自定义的,而且使用有很大的限制,所以不深入介绍
2.4、RecycleViewViewPool:
RecycledViewPool 储存各个类型的 viewHolder 它缓存的是被恢复出厂设置的viewHolder,需要重新调用bind 绑定数据。
- RecycledViewPool 是按照ItemViewType 存储ViewHolder的,每种ItemViewType最大数量为5
- 可以通过 setMaxRecycledViews() 方法来设置每个类型储存的容量。
- 针对RecycleView嵌套的场景,如一个纵向的RecycleView 嵌套横向的RecycleView ,可以使用 setRecycledViewPool() 方法,公用RecycledViewPool
RecycleView滑出屏幕时的ViewHolder的复用过程
滚出屏幕的View会优先保存到mCacheViews, 如果mCacheViews中保存满了,就会保存到RecyclerViewPool中。
- 检查mCacheViews集合中是否还有空位,如果有空位,则直接放到mCacheViews集合
- 如果没有的话就把mCacheViews集合中最前面的ViewHolder拿出来放到RecyclerViewPool中,然后再把最新的这个ViewHolder放到mCacheViews集合
- 如果没有成功缓存到mCacheViews集合中,就直接放到RecyclerViewPool
3、Recycler 缓存加载流程
- scrap负责缓存屏幕中正在显示的ViewHolder,命中缓存后直接使用,不需要create和Bind
- 如果在 cache (mCachedViews)负责缓存刚刚移出屏幕,很可能被复用的ViewHolder。通过position获取,命中后不需create和bind
- ViewCacheExtension google预留的一个空的缓存,暂不讨论
- pool (RecycledViewPool )根据ViewType缓存ViewHolder, 用于缓存数据解绑后的ViewHolder,pool中命中的viewHolder 需要进行重新bind 进行数据绑定
- 如果所有缓存中都没有命中 viewHolder,会重新调用createViewHolder 和 bindViewHolder
4、RecyclerView的部分优化
1.在 onCreateViewHolder 里面写点击事件
这个很好理解,onBindViewHolder会多次调用
2.LinearLayoutManager.setInitialPrefetchItemCount
注意,此方法LinearLayoutManager专属
public void setInitialPrefetchItemCount(int itemCount) {
mInitialItemPrefetchCount = itemCount;
}
mInitialItemPrefetchCount 在这里用到
@Override
public void collectInitialPrefetchPositions(int adapterItemCount,
LayoutPrefetchRegistry layoutPrefetchRegistry) {
final int direction = fromEnd
? LayoutState.ITEM_DIRECTION_HEAD
: LayoutState.ITEM_DIRECTION_TAIL;
int targetPos = anchorPos;
for (int i = 0; i < mInitialItemPrefetchCount; i++) {
if (targetPos >= 0 && targetPos < adapterItemCount) {
layoutPrefetchRegistry.addPosition(targetPos, 0);
} else {
break; // no more to prefetch
}
targetPos += direction;
}
}
说白了就是预取操作。collectInitialPrefetchPositions定义当此LayoutManager的RecyclerView嵌套在另一个RecyclerView中时应预取多少内部项目。也就是说只有RecyclerView嵌套才会生效,否则无效
3.setHasFixedSize
setHasFixedSize 会给 mHasFixedSize 赋值,看一下用到的地方
// 第一处
@Override
protected void onMeasure(int widthSpec, int heightSpec) {
if (mLayout == null) {
defaultOnMeasure(widthSpec, heightSpec);
return;
}
if (mLayout.mAutoMeasure) {
......
} else {
if (mHasFixedSize) {
mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
return;
}
// custom onMeasure
if (mAdapterUpdateDuringMeasure) {
eatRequestLayout();
onEnterLayoutOrScroll();
processAdapterUpdatesAndSetAnimationFlags();
onExitLayoutOrScroll();
if (mState.mRunPredictiveAnimations) {
mState.mInPreLayout = true;
} else {
// consume remaining updates to provide a consistent state with the layout pass.
mAdapterHelper.consumeUpdatesInOnePass();
mState.mInPreLayout = false;
}
mAdapterUpdateDuringMeasure = false;
resumeRequestLayout(false);
}
if (mAdapter != null) {
mState.mItemCount = mAdapter.getItemCount();
} else {
mState.mItemCount = 0;
}
eatRequestLayout();
mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
resumeRequestLayout(false);
mState.mInPreLayout = false; // clear
}
}
// 第二处
private class RecyclerViewDataObserver extends AdapterDataObserver {
void triggerUpdateProcessor() {
if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
RecyclerView.this.postOnAnimation(mUpdateChildViewsRunnable);
} else {
mAdapterUpdateDuringMeasure = true;
requestLayout();
}
}
}
mLayout.mAutoMeasure 默认是false,当然也可以自己设置。当mHasFixedSize是true的时候只会执行onMeasure。另外,每次notifyDataSetChanged的时候只会mUpdateChildViews而不用整个requestLayout。当然,有个前提,adapter的数据变化不会导致RecycleView的大小变化 。
总结,当RecycleView大小固定的时候,setHasFixedSize 为true可以使每次onMeasure和notifyDataSet的时候不用去request整个layout
4.多个RecycleView共用RecycledViewPool
RecycledViewPool 其实并不陌生,就是缓存机制的最后一个pool。如果这里还取不到view就重新create。