网上的大多数博客都是认为RecyclerView是4级缓存,但真的是4级缓存吗?知道我看了源码,才发现,RecyclerView做的远远不止4级缓存,当然是根据LayoutManager不同,缓存实现也不同,本文主要分析最最常用的LinearLayoutManager。
写的有点乱,以后整理吧
RecyclerView复用过程
RecyclerView的中的缓存复用是由内部类Recycler来维护的,在RecyclerView.Adapter调用onCreateViewHolder来创建ViewHolder,这时就开始了利用缓存机制;
这里是以LinearLayoutManager为例,其他也差不多,只是一些细节不一样;
先看下调用流程吧,否则直接看缓存,也不知道什么时候调用的:
可以看到,RecyclerView的在onMeasure时,就将item所有的操作都交给了LayoutManager,并在创建ViewHolder时,将缓存复用机制交给了RecyclerView内部类Recycler,Recycler的tryGetViewHolderForPositionByDeadline方法内,实现了所有的复用机制;
缓存实现
Recycler :缓存机制的管理类
public final class Recycler {
//主要由void scrapView(View view)来处理, 仍旧绑定在RecyclerView上,但是能rebinding和reuse;
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
ArrayList<ViewHolder> mChangedScrap = null;
final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
private final List<ViewHolder> mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
RecycledViewPool mRecyclerPool;
private ViewCacheExtension mViewCacheExtension;
}
从代码里这来看,里面就有4个集合外加一个RecycledViewPool和一个ViewCacheExtension,所以远不止4级缓存;
缓存的实现
接下来我们详细看下复用机制的实现:
其实在LayoutState的next方法中就有一层复用,mScrapList实际就是mUnmodifiableAttachedScrap,这个调用是在onLayoutChildren最后的layoutForPredictiveAnimations的方法中,做动画时,直接去拿mUnmodifiableAttachedScrap中的itemView;
否则才是真正的去创建View;
View next(RecyclerView.Recycler recycler) {
if (mScrapList != null) {
return nextViewFromScrapList();
}
final View view = recycler.getViewForPosition(mCurrentPosition);
mCurrentPosition += mItemDirection;
return view;
getViewForPosition中主要调用tryGetViewHolderForPositionByDeadline方法:
ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs) {
RecyclerView.ViewHolder holder = null;
// 0) If there is a changed scrap, try to find from there
// isPreLayout的条件是在RecyclerView的onMeasure中,如果不是自动测量,adapter大小不是固定的,或者是自定义onMeasure
//如果只是changed,就会进入到getChangedScrapViewForPosition,里面主要从mChangedScrap取数据;
if (mState.isPreLayout()) {
//里面主要是从mChangedScrap中找
holder = getChangedScrapViewForPosition(position);
}
// 1) Find by position from scrap/hidden list/cache
if (holder == null) {
// 里面主要从mAttachedScrap和ChildHelper的mHiddenViews还有mCachedViews中查找
//这里面是确切的匹配,里面各种状态必须完全一致,拿出来的,不需要经过rebinding
holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
}
if (holder == null) {
final int offsetPosition = mAdapterHelper.findPositionOffset(position);
final int type = mAdapter.getItemViewType(offsetPosition);
// 2) Find from scrap/cache via stable ids, if exists
//hasStableIds默认是true,在adapter的构造中赋值的
if (mAdapter.hasStableIds()) {
//利用Position找不到时,再用id和type匹配,拿出来之后需要rebinding
holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition), type, dryRun);
}
// 用户自定义扩展缓存
if (holder == null && mViewCacheExtension != null) {
// We are NOT sending the offsetPosition because LayoutManager does not
// know it.
final View view = mViewCacheExtension.getViewForPositionAndType(this, position, type);
if (view != null) {
holder = getChildViewHolder(view);
}
}
//RecycledView复用池
if (holder == null) { // fallback to pool
holder = getRecycledViewPool().getRecycledView(type);
}
// 创建新的Item
if (holder == null) {
long start = getNanoTime();
if (deadlineNs != FOREVER_NS && !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
// abort - we have a deadline we can't meet
return null;
}
holder = mAdapter.createViewHolder(RecyclerView.this, type);
if (ALLOW_THREAD_GAP_WORK) {
// only bother finding nested RV if prefetching
RecyclerView innerView = findNestedRecyclerView(holder.itemView);
if (innerView != null) {
holder.mNestedRecyclerView = new WeakReference<>(innerView);
}
}
}
}
return holder;
}
在LayoutState中的next方法中还有一层缓存,利用的mScrapList ,其实mScrapList 就是mUnmodifiableAttachedScrap
View next(RecyclerView.Recycler recycler) {
if (mScrapList != null) {
return nextViewFromScrapList();
}
final View view = recycler.getViewForPosition(mCurrentPosition);
mCurrentPosition += mItemDirection;
return view;
}
private View nextViewFromScrapList() {
final int size = mScrapList.size();
for (int i = 0; i < size; i++) {
final View view = mScrapList.get(i).itemView;
final RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams();
if (lp.isItemRemoved()) {
continue;
}
if (mCurrentPosition == lp.getViewLayoutPosition()) {
assignPositionFromScrapList(view);
return view;
}
}
return null;
}
这里面涉及到2个缓存mAttachedScrap和mChangedScrap,可以看到这2个缓存的定义了
void scrapView(View view) {
final ViewHolder holder = getChildViewHolderInt(view);
if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
|| !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
throw new IllegalArgumentException(...);
}
holder.setScrapContainer(this, false);
mAttachedScrap.add(holder);
} else {
if (mChangedScrap == null) {
mChangedScrap = new ArrayList<ViewHolder>();
}
holder.setScrapContainer(this, true);
mChangedScrap.add(holder);
}
}
总结
写的有些乱,代码太多了,都很重要,建议自己再看一遍