RecyclerView 源码-缓存机制

本文深入探讨了RecyclerView的源码,重点分析了其缓存机制。在布局过程中,LayoutManager在onLayoutChildren中分四步完成。首先,通过detachAndScrapAttachedViews拆分itemView并存入不同缓存。接着,fill()方法不断调用layoutChunk(),使用layoutState.next()查找并布局View。查找View缓存逻辑包括:mChangedScrap中查找、scrap/hidden list/cache中查找、mAttachedScrap和mCachedViews中根据itemId和type查找,以及RecycledViewPool中获取,最后由adapter创建新的ViewHolder。
摘要由CSDN通过智能技术生成

查看RecyclerView 源码
避免忘记 特此记录

下面部分代码截取 LinearLayoutManager.javaRecyclerView.java
RecyclerView 的布局过程是由 LayoutManager 完成,在onLayoutChildren中 分为四步完成

// layout algorithm:
1) by checking children and other variables, find an anchor coordinate and an anchor item position.
2) fill towards start, stacking from bottom
3) fill towards end, stacking from top
4) scroll to fulfill requirements like stack from bottom.

LayoutManager 在调用填充方法 fill(recycler, mLayoutState, state, false)之前会调用 detachAndScrapAttachedViews(recycler) 将 RecyclerView 的childView 进行拆分,RecyclerView 拆分后的itemView会根据flag标记分别存入mChangedScrap 和 mAttachedScrap

//拆分RecyclerView 过程 会调用 scrapView 将itemView临时存储
detachAndScrapAttachedViews(recycler);

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("Called scrap view with an invalid view."
                        + " Invalid views cannot be reused from scrap, they should rebound from"
                       + " recycler pool." + exceptionLabel());
           }
           holder.setScrapContainer(this, false);
           mAttachedScrap.add(holder);
        } else {
           if (mChangedScrap == null) {
               mChangedScrap = new ArrayList<ViewHolder>();
           }
          holder.setScrapContainer(this, true);
          mChangedScrap.add(holder);
       }
}

在fill()方法中 不断循环layoutChunk() ,将View添加进RecyclerView
通过layoutState.next(recycler) 方法查找View

void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
      LayoutState layoutState, LayoutChunkResult result) {
           //获取View入口
         View view = layoutState.next(recycler);
         if (view == null) {
         	//找不到View赋值结束状态 
         	 result.mFinished = true;
            return;
        }
 .....
	if (layoutState.mScrapList == null) {
	   if (mShouldReverseLayout == (layoutState.mLayoutDirection  == LayoutState.LAYOUT_START)) {
	         addView(view);
	   } else {
	         addView(view, 0);
	   }
	 } else {
	    if (mShouldReverseLayout == (layoutState.mLayoutDirection == LayoutState.LAYOUT_START)) {
	          addDisappearingView(view);
	     } else {
	          addDisappearingView(view, 0);
	      }
	 }
 .....
}

获取布局,并将索引指向下一个

	//在缓存类 Recycler 中获取到View
    View next(RecyclerView.Recycler recycler) {
         if (mScrapList != null) {
              return nextViewFromScrapList();
         }
         //调用 RecyclerView 中 getViewForPosition 方法
       	 final View view = recycler.getViewForPosition(mCurrentPosition);
         mCurrentPosition += mItemDirection;
         return view;
    }

开始查找View缓存的逻辑,RecyclerView是将ViewHolder缓存起来然后获取对应的itemView、
查找过程大致分为四个过程、如果都没有则会调用adapter的 onCreateViewHolder 创建

ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs) {
    // 0 先从  ArrayList<ViewHolder> mChangedScrap = null; 列表中取
    // 0) If there is a changed scrap, try to find from there
    if (mState.isPreLayout()) {
          holder = getChangedScrapViewForPosition(position);
          ......
     }
    // 1) Find by position from scrap/hidden list/cache
    // 1) 如果在mChangedScrap 中未找到,根据索引在 scrap/hidden list/cache 中查找
    if (holder == null) {
      	holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
          ....
     }
     if (holder == null) {
      // 2 根据itemId 和 type 从mChangedScrap ,mCachedViews中查找
      // 2) Find from scrap/cache via stable ids, if exists‘’
       	if (mAdapter.hasStableIds()) {
           holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),type, dryRun);
           ....
       	}
        if (holder == null && mViewCacheExtension != null) {
             final View view = mViewCacheExtension .getViewForPositionAndType(this, position, type);
             .....
             if (view != null) {
                 holder = getChildViewHolder(view);
           	}
      	}
     }
     // 3 在RecycledViewPool 中查找
     if (holder == null) { // fallback to pool
            holder = getRecycledViewPool().getRecycledView(type);
      }
      // 4 adapter createViewHolder 创建
     if (holder == null) {
	       holder = mAdapter.createViewHolder(RecyclerView.this, type);
      }
}

step 0 mChangedScrap 中根据position / id 查找

ViewHolder getChangedScrapViewForPosition(int position) {
   ....
   // find by position 遍历列表并从中找到ViewHolder
    for (int i = 0; i < changedScrapSize; i++) {
        final ViewHolder holder = mChangedScrap.get(i);
        if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
            holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
             return holder;
        }
 	}
 	// find by id
	if (mAdapter.hasStableIds()) {
	     final int offsetPosition = mAdapterHelper.findPositionOffset(position);
	     if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
	          final long id = mAdapter.getItemId(offsetPosition);
	          for (int i = 0; i < changedScrapSize; i++) {
	          	 final ViewHolder holder = mChangedScrap.get(i);
	           	 if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
	                 holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
	                 return holder;
	             }
	          }
	    }
	}
  return null;
}

step 1 在scrap/hidden list/cache 中依次查找
在 getScrapOrHiddenOrCachedHolderForPosition 方法中查找 ViewHolder 如果找到则 将ViewHolder缓存到mCachedViews中
如果没有找到则进行step2

//先从mAttachedScrap 中获取 并且更新flag
for (int i = 0; i < scrapCount; i++) {
    final ViewHolder holder = mAttachedScrap.get(i);
     if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
           && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
       holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
       return holder;
     }
}
//如果 mAttachedScrap 中没有找到 则从 mHiddenViews 中查找
View view = mChildHelper.findHiddenNonRemovedView(position);
     if (view != null) {
      // This View is good to be used. We just need to unhide, detach and move to the
      // scrap list.
      final ViewHolder vh = getChildViewHolderInt(view);
     //改变view 的hide 状态
      mChildHelper.unhide(view); 
       int layoutIndex = mChildHelper.indexOfChild(view);
       if (layoutIndex == RecyclerView.NO_POSITION) {
           throw new IllegalStateException("layout index should not be -1 after "
            + "unhiding a view:" + vh + exceptionLabel());
       }
        mChildHelper.detachViewFromParent(layoutIndex);
        //将View存储在 mAttachedScrap 或者 mChangedScrap 中
        scrapView(view);
        vh.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP| ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
      return vh;
 }
 //最后在mCachedViews 中查找缓存过的ViewHolder 
// Search in our first-level recycled view cache.
	final int cacheSize = mCachedViews.size();
	 for (int i = 0; i < cacheSize; i++) {
	   final ViewHolder holder = mCachedViews.get(i);
	   ....
     	return holder;
     }
}

step 2 在scrap/cache 中根据itemId 和type 从mAttachedScrap 和 mCachedViews 查找

final int type = mAdapter.getItemViewType(offsetPosition);
 if (mAdapter.hasStableIds()) {
 //根据itemId 和type 从mAttachedScrap 和 mCachedViews 查找
    holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),type, dryRun);
       if (holder != null) {
           // update position
          holder.mPosition = offsetPosition;
          fromScrapOrHiddenOrCache = true;
        }
 }
if (holder == null && mViewCacheExtension != null) {
     // We are NOT sending the offsetPosition because LayoutManager does not know it.
     // ViewCacheExtension 抽象类中提供
      final View view = mViewCacheExtension.getViewForPositionAndType(this, position, type);
      if (view != null) {
     	 holder = getChildViewHolder(view);
      }
      .....
}

step 3 从RecycledViewPool 中获取

//从RecycledViewPool 中获取,RecycledViewPool 可用于多个不同RecyclerView共享item
if (holder == null) { // fallback to pool
    holder = getRecycledViewPool().getRecycledView(type);
    if (holder != null) {
       holder.resetInternal();
      if (FORCE_INVALIDATE_DISPLAY_LIST) {
          invalidateDisplayListInt(holder);
      }
   }
}

step 4 通过adapter 自行创建 ViewHolder

if (holder == 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);
      }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值