Android

RecyclerView的复用缓存机制

2020-08-14  本文已影响0人  remax1

前言

在正式分析缓存复用机制时,先去缓存与复用的时机在哪里。在RecyclerView滑动时,item会显示出来。所以首先先想到在onTouchEvent()中ACTION_MOVE事件里做了什么事。

  case MotionEvent.ACTION_MOVE:
      if (scrollByInternal(
                            canScrollHorizontally ? dx : 0,
                            canScrollVertically ? dy : 0,
                            e)) {
                        getParent().requestDisallowInterceptTouchEvent(true);
                    }
  

接着 又到 scrollStep(x, y, mReusableIntPair) -> mLayout.scrollVerticallyBy(dy, mRecycler, mState)
->scrollBy(dy, recycler, state)[不同的layoutManager有不同的实现,这里以LinearLayoutManager为例]-> fill(recycler, mLayoutState, state)

int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
            RecyclerView.State state, boolean stopOnFocusable) {
      `````省略
        recycleByLayoutState(recycler, layoutState);//回收
      
         layoutChunk(recycler, state, layoutState, layoutChunkResult);//复用
}

复用机制

RecyclerView的缓存分为四级
第一级 mAttachedScrap 和 mChangedScrap:RecyclerView在获取viewHolder时,其中mAttachedScrap 存储的是当前屏幕的和滑出屏幕的ViewHolder,mChangeScrap存储的是数据被更新的ViewHolder。

第二级 mCacheViews :默认的大小为2,通常用来存储预取或者准备回收的ViewHolder。

第三级 ViewCacheExtention:自定义缓存。

第四级 RecyclerViewPool:根据viewType来缓存viewHolder,每个viewType的缓存的viewHolder的个数为5.

接着往源码下找:
layoutState.next(recycler)->getViewForPosition->tryGetViewHolderForPositionByDeadline.到这里就开始RecyclerView的复用流程了。

 ViewHolder tryGetViewHolderForPositionByDeadline(int position,
                boolean dryRun, long deadlineNs) {
       
            boolean fromScrapOrHiddenOrCache = false;
            ViewHolder holder = null;
            // 0) If there is a changed scrap, try to find from there
            if (mState.isPreLayout()) {
                //注释①:第一次查找
                holder = getChangedScrapViewForPosition(position);
                fromScrapOrHiddenOrCache = holder != null;
            }
            // 1) Find by position from scrap/hidden list/cache
            if (holder == null) {
              //注释②:第二次查找
                holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
                if (holder != null) {
                    if (!validateViewHolderForOffsetPosition(holder)) {
                        // recycle holder (and unscrap if relevant) since it can't be used
                        if (!dryRun) {
                            // we would like to recycle this but need to make sure it is not used by
                            // animation logic etc.
                            holder.addFlags(ViewHolder.FLAG_INVALID);
                            if (holder.isScrap()) {
                                removeDetachedView(holder.itemView, false);
                                holder.unScrap();
                            } else if (holder.wasReturnedFromScrap()) {
                                holder.clearReturnedFromScrapFlag();
                            }
                            recycleViewHolderInternal(holder);
                        }
                        holder = null;
                    } else {
                        fromScrapOrHiddenOrCache = true;
                    }
                }
            }
            if (holder == null) {
             
                }

                final int type = mAdapter.getItemViewType(offsetPosition);
                // 2) Find from scrap/cache via stable ids, if exists
                if (mAdapter.hasStableIds()) {
                  //注释③:第三次查找。
                    holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
                            type, dryRun);
                    if (holder != null) {
                        // update position
                        holder.mPosition = offsetPosition;
                        fromScrapOrHiddenOrCache = true;
                    }
                }
                if (holder == null && mViewCacheExtension != null) {
                   t.
                    //注释④:第四次查找
                    final View view = mViewCacheExtension
                            .getViewForPositionAndType(this, position, type);
                    if (view != null) {
                        holder = getChildViewHolder(view);
                    }
                }
                if (holder == null) { // fallback to pool
                  //注释⑤:第五次查找
                    holder = getRecycledViewPool().getRecycledView(type);
                    if (holder != null) {
                        holder.resetInternal();
                        if (FORCE_INVALIDATE_DISPLAY_LIST) {
                            invalidateDisplayListInt(holder);
                        }
                    }
                }
                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);
                 ······
        }

注释①:如果有改变的viewHolder,先从mChangedScrap去查找数据被更新的viewHolder,有两种方式,一种是position另一种是id。

注释②:先从mAttachedScrap中查找废弃的ViewHolder,接着再去消失的View中去找有没有可以使用的viewHolder,最后会从mCacheViews(第一级回收缓存) 中去搜索。

注释③:先在mAttachedScrap中查找,再去mCacheViews中去寻找。

注释④:从自定义的缓存策略中去寻找。

注释⑤:从回收池RecyclerViewPool中去找,找到了返回viewHolder并从回收池中移除。

注释⑥:前面都没找到,就调用creatViewHolder去创建viewHolder了。

回收机制

回收主要经历recycleByLayoutState->recycleViewsFromEnd->recycleChildren->removeAndRecycleViewAt-> recycler.recycleView(view)->recycleViewHolderInternal

 void recycleViewHolderInternal(ViewHolder holder) {
         
            final boolean transientStatePreventsRecycling = holder
                    .doesTransientStatePreventRecycling();
            @SuppressWarnings("unchecked")
            final boolean forceRecycle = mAdapter != null
                    && transientStatePreventsRecycling
                    && mAdapter.onFailedToRecycleView(holder);
            boolean cached = false;
            boolean recycled = false;
            if (DEBUG && mCachedViews.contains(holder)) {
                throw new IllegalArgumentException("cached view received recycle internal? "
                        + holder + exceptionLabel());
            }
            if (forceRecycle || holder.isRecyclable()) {
                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--;
                    }

                    int targetCacheIndex = cachedViewSize;
                    if (ALLOW_THREAD_GAP_WORK
                            && cachedViewSize > 0
                            && !mPrefetchRegistry.lastPrefetchIncludedPosition(holder.mPosition)) {
                        // when adding the view, skip past most recently prefetched views
                        int cacheIndex = cachedViewSize - 1;
                        while (cacheIndex >= 0) {
                            int cachedPos = mCachedViews.get(cacheIndex).mPosition;
                            if (!mPrefetchRegistry.lastPrefetchIncludedPosition(cachedPos)) {
                                break;
                            }
                            cacheIndex--;
                        }
                        targetCacheIndex = cacheIndex + 1;
                    }
                    //注释②:第二次回收
                    mCachedViews.add(targetCacheIndex, holder);
                    cached = true;
                }
                if (!cached) {
                    addViewHolderToRecycledViewPool(holder, true);
                    recycled = true;
                }
            } 
            mViewInfoStore.removeViewHolder(holder);
            if (!cached && !recycled && transientStatePreventsRecycling) {
                holder.mOwnerRecyclerView = null;
            }
        }

注释①:mViewCacheMax 默认为2,如果mCacheViews满了,将它缓存最旧的VH放入回收池,并移除。

注释②:如果没满,就加入到mCacheViews中。

上一篇下一篇

猜你喜欢

热点阅读