美文网首页Android
RecyclerView的复用缓存机制

RecyclerView的复用缓存机制

作者: remax1 | 来源:发表于2020-08-14 10:57 被阅读0次

    前言

    在正式分析缓存复用机制时,先去缓存与复用的时机在哪里。在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中。

    相关文章

      网友评论

        本文标题:RecyclerView的复用缓存机制

        本文链接:https://www.haomeiwen.com/subject/wljbdktx.html