阅读 494

Android从源码分析RecyclerView四级缓存复用机制二(复用ViewHolder)

上一篇文章说了RecyclerView的四级缓存中的缓存ViewHolder,文章链接在这里:Android从源码分析RecyclerView四级缓存复用机制一(缓存ViewHolder) 列表视图在原生开发中一直占用重要地位,不管是之前的ListView还是现在RecyclerView,无论实在性能上还是使用功能上都有着巨大的优势,其中最重要的其实还是对于视图的复用机制。从ListView的RecycleBin到RecyclerView的Recycler,Google对于列表视图的缓存的设计一直非常考究值得我们学习和研究。而网页的H5以及RN对于复杂的列表视图的渲染性能不好从这里面其实也可以寻找到一些原因。 本文就来说一下ViewHolder的复用(回收) ,缓存的ViewHolder必须要复用才能够体现缓存的意义。

1.总流程图

放上一张Bugly的一篇博客对RecyclerView的缓存的流程图吧(自己绘制也差不多,直接拿来用了...若侵立删) 在这里插入图片描述

2 .调用方法跟踪

recyclerView在什么时候复用被缓存的ViewHolder,毫无疑问肯定是在recyclerView滚动的时候。recyclerView在滚动的时候触发复用。

// 1.RecyclerView的scrollBy()方法
@Override
public void scrollBy(int x, int y) {
        //...
        final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
        final boolean canScrollVertical = mLayout.canScrollVertically();
        //横向滑动和纵向滑动都可以进入if
        if (canScrollHorizontal || canScrollVertical) {
            scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0, null);
        }
}

// 2.RecyclerView的scrollByInternal()方法
boolean scrollByInternal(int x, int y, MotionEvent ev) {
        int unconsumedX = 0, unconsumedY = 0;
        int consumedX = 0, consumedY = 0;

        consumePendingUpdateOperations();
        if (mAdapter != null) {
            eatRequestLayout();
            onEnterLayoutOrScroll();
            TraceCompat.beginSection(TRACE_SCROLL_TAG);
            if (x != 0) {
                //横向滑动调用LayoutManager的scrollHorizontallyBy
                consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
                unconsumedX = x - consumedX;
            }
            if (y != 0) {
                //纵向滑动调用LayoutManager的scrollVerticallyBy
                consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);
                unconsumedY = y - consumedY;
            }
            //...
        }
        //...
}

// 3.RecyclerView.LayoutManager的scrollVerticallyBy()方法 本文主要分析纵向滑动其实都一样
public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
     return 0;
}

// 4.LinearLayoutManager重写了LayoutManager的scrollVerticallyBy()方法
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,RecyclerView.State state) {
    if (mOrientation == HORIZONTAL) {
        return 0;
    }
    return scrollBy(dy, recycler, state);
}

// 5.LinearLayoutManager的scrollBy()
int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        //...
        //这里调用fill方法
        final int consumed = mLayoutState.mScrollingOffset
                + fill(recycler, mLayoutState, state, false);
        //...
        final int scrolled = absDy > consumed ? layoutDirection * consumed : dy;
        mOrientationHelper.offsetChildren(-scrolled);
        //...
        mLayoutState.mLastScrollDelta = scrolled;
        return scrolled;
}
// 6.LinearLayoutManager的fill()
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
            RecyclerView.State state, boolean stopOnFocusable) {
        //...
        while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
            //...
            //根据当前的布局方向调用适当的回收方法
            layoutChunk(recycler, state, layoutState, layoutChunkResult);
            //...
        }
        //...
    }
 // 7.LinearLayoutManager的layoutChunk()
 void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,LayoutState layoutState, LayoutChunkResult result) {
        //传入recycler获取到相应的view
        View view = layoutState.next(recycler);
        //...
        if (layoutState.mScrapList == null) {
            if (mShouldReverseLayout == (layoutState.mLayoutDirection== LayoutState.LAYOUT_START)) {
                //将获取到的view重新添加到recyclerview中
                addView(view);
            } else {
                addView(view, 0);
            }
        } 
        //...
    }
 // 8.LinearLayoutManager的next()
 View next(RecyclerView.Recycler recycler) {
      if (mScrapList != null) {
           return nextViewFromScrapList();
      }
      //根据位置获取view
      final View view = recycler.getViewForPosition(mCurrentPosition);
      mCurrentPosition += mItemDirection;
      return view;
}
// 9.RecyclerView.Recycler的getViewForPosition()
View getViewForPosition(int position, boolean dryRun) {
     return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
}   
复制代码

回顾一下方法的调用

入口:滑动 Move 事件 --> scrollByInternal --> scrollStep --> mLayout.scrollVerticallyBy 
--> scrollBy  --> fill --> layoutChunk  --> layoutState.next --> addView(view);

layoutState.next--> getViewForPosition --> tryGetViewHolderForPositionByDeadline
复制代码

经过一系列的方法调用,我们调用到了RecyclerView.Recycler的tryGetViewHolderForPositionByDeadline()方法中拿到四级缓存中的ViewHolder然后拿到view,然后在layoutChunk()中的addView(view)方法中添加到recyclerView中。

下面通过源码来验证一下1.总流程图

3.核心源码分析

ViewHolder tryGetViewHolderForPositionByDeadline(int position,
                boolean dryRun, long deadlineNs) {
            //...各种判断
            boolean fromScrapOrHiddenOrCache = false;
            ViewHolder holder = null;
            if (mState.isPreLayout()) {
                // 1.对应的是一级缓存中的 -- mChangeScrap 与动画相关的ViewHolder
                holder = getChangedScrapViewForPosition(position);
                fromScrapOrHiddenOrCache = holder != null;
            }
            //如果一级缓存没有对应位置的holder
            if (holder == null) {
                //2.对应的是二级缓存中-- mAttachedScrap 、mCachedViews 存储屏幕外的ViewHolder
                holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
                if (holder != null) {
                    if (!validateViewHolderForOffsetPosition(holder)) {
                        if (!dryRun) {
                            //...一堆判断
                            //回收方法
                            recycleViewHolderInternal(holder);
                        }
                        holder = null;
                    } else {
                        fromScrapOrHiddenOrCache = true;
                    }
                }
            }
            if (holder == null) {
                //...
                
                final int type = mAdapter.getItemViewType(offsetPosition);
                if (mAdapter.hasStableIds()) {
                    // 3.对应的是二级缓存中-- mAttachedScrap 、mCachedViews 根据(ViewType,itemid) 获取的ViewHolder
                    holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
                            type, dryRun);
                    if (holder != null) {
                        holder.mPosition = offsetPosition;
                        fromScrapOrHiddenOrCache = true;
                    }
                }
                if (holder == null && mViewCacheExtension != null) {
                //4.对应的是三级缓存 -- 自定义缓存 -- (使用情况:局部刷新??)的ViewHolder
                    final View view = mViewCacheExtension
                            .getViewForPositionAndType(this, position, type);
                    if (view != null) {
                        holder = getChildViewHolder(view);
                        //...
                    }
                }
                if (holder == null) { // fallback to pool
                    //...
                    //5.对应的是四级缓存-- 从缓冲池里面获取 ViewHolder
                    holder = getRecycledViewPool().getRecycledView(type);
                    if (holder != null) {
                        holder.resetInternal();
                        if (FORCE_INVALIDATE_DISPLAY_LIST) {
                            invalidateDisplayListInt(holder);
                        }
                    }
                }
                if (holder == null) {
                    long start = getNanoTime();
                    //...
                    // 6.四级缓存都找不到 ViewHolder则走createViewHolder()创建ViewHolder
                    holder = mAdapter.createViewHolder(RecyclerView.this, type);
                    //...
                }
                //...
             return holder;
        }
复制代码

至此文章中的大体的源码就分析完了,如果想进一步了解更多的recyclerView回收复用的更多的细节的话,可以自己阅读一下源码。

文章分类
Android
文章标签