美文网首页
Android-RecyclerView布局显示和回收复用流程

Android-RecyclerView布局显示和回收复用流程

作者: zzq_nene | 来源:发表于2020-07-22 15:08 被阅读0次

    一、RecyclerView的四级缓存

    (1)mChangedScrap、mAttachedScrap:

    用于屏幕内ItemView的快速重用。
    Scrap缓存,其实就是用来保存被RecyclerView移除但是最近又马上要使用的缓存,比如RecyclerView自带的item的动画等,Scrap有两个,一个是mChangedScrap,一个是mAttachedScrap,这两个的区别就是保存的对象有些不一样。一般调用adapter的notifyItemRangeChanged被移除的holder保存在mChangedScrap中,其他的notify系列方法(不包括notifyDataSetChanged)移除的holder保存在mAttachedScrap中。这两个缓存是在布局阶段使用的,其他时候是空的。布局完成之后,这两个缓存中的holder就会移动到mCacheView或者RecyclerViewPool中。
    mChangedScrap一般是在预布局阶段使用的(dispatchLayoutStep1()方法),而mAttachedScrap是在整个布局阶段使用的。
    mAttachedScrap 保存依附于 RecyclerView 的 ViewHolder。包含移出屏幕但未从 RecyclerView 移除的 ViewHolder。就是未与RecyclerView分离的ViewHolder。这里的是ViewHolder的数据没有发生变化的
    mChangedScrap 保存数据发生改变的 ViewHolder,即调用 notifyDataSetChanged() 等系列方法后需要更新的 ViewHolder。就是与RecyclerView分离的ViewHolder。这里的是ViewHolder的数据发生变化的,需要重新走Adapter的bind的

    • 在onLayout布局阶段,在LayoutManager.onLayoutChildren方法中会调用detachAndScrapAttachedViews方法,在这里又会调用scrapOrRecycleView方法,接着判断条件,如果调用了Recycler.scrapView方法,就会将ViewHolder回到到scrap中。
    • 在复用阶段,在tryGetViewHolderForPositionByDeadline方法中调用getScrapOrHiddenOrCachedHolderForPosition方法时,会将隐藏的View回收到一级缓存中,即隐藏但是没有被删除的View。即在dryRun为false的时候,获取隐藏的但是没有被remove的View,调用scrapView方法保存在mAttachedScrap或者mChangedScrap(mChangedScrap还有就是在预布局阶段加入)

    (2)mCachedViews

    默认上限为2个,缓存屏幕外2个ItemView。mCachedViews中保存的是有数据的ViewHolder。

    • 在重新布局时,会将ViewHolder添加到mCachedViews中,也是在onLayoutChildren方法中调用detachAndScrapAttachedViews,但是在scrapOrRecycleView方法中判断不同,则会调用Recycler.recycleViewHolderInternal方法回收
    • 在复用阶段,如果从一级缓存中取出的ViewHolder是不可用的,则会调用recycleViewHolderInternal方法,将ViewHolder放入到二级缓存中
      在这里存放的是detachView之后的视图,它里面存放的是已经remove掉的视图,已经和RV分离的关系的视图,但是它里面的ViewHolder依然保存着之前的信息,比如position、和绑定的数据等等。这一级缓存是有容量限制的,默认是2

    (3)mViewCacheExtension

    默认不实现的,这个是需要开发者自定义实现的缓存机制

    (4)mRecyclerViewPool

    缓存池,同一种ViewType的ViewHolder缓存默认上限为5个。保存的只是ViewHolder,但是这个ViewHolder没有数据,当RecyclerViewPool缓存的ViewHolder已经满了,则不会再加入了。

    • 在调用recycleViewHolderInternal回收加入到mRecyclerViewPool的时候,会先判断是否可以加入到mCachedViews,如果不满足加入到mCachedViews的条件,则会加入到mRecyclerViewPool中。而添加四级缓存,可能是在回收流程中,也可能是在复用流程中,如果是在回收流程中,则是在调用Recycler.recycleViewHolderInternal方法的时候,调用了addViewHolderToRecycledViewPool方法;如果是在复用流程,则是在tryGetViewHolderForPositionByDeadline方法中有两次,一次就是通过position寻找ViewHolder的时候,如果找到的是不可用的,则会调用Recycler.recycleViewHolderInternal可能又会进入第四级缓存,一次就是通过ID找到ViewHolder,如果还是不可用则会调用recycleCachedViewAt方法,进而调用addViewHolderToRecycledViewPool加入四级缓存
    缓存 涉及对象 作用 重新创建视图View(onCreateViewHolder) 重新绑定数据(onBindViewHolder)
    一级缓存 mAttachedScrap 缓存屏幕中可见范围的ViewHolder false false
    一级缓存 mChangedScrap 缓存屏幕中可见范围的但是数据发生改变ViewHolder false true
    二级缓存 mCachedViews 缓存滑动时即将与RecyclerView分离的ViewHolder,按子View的position或id缓存,默认最多存放2个 false false
    三级缓存 mViewCacheExtension 开发者自行实现的缓存 - -
    四级缓存 mRecyclerPool ViewHolder缓存池,本质上是一个SparseArray,其中key是ViewType(int类型),value存放的是 ArrayList< ViewHolder>,默认每个ArrayList中最多存放5个ViewHolder false true

    二、RecyclerView的预取功能(预加载功能)

    Android系统是通过每16ms刷新一次页面来保证UI的流畅性,现在Android系统中刷新UI会通过CPU产生数据,然后交给GPU渲染的形式来完成,这样当CPU完成数据交给GPU之后,此时还没到16ms,那么这段时间CPU就会处于空闲状态,需要等待下一帧才会进行数据处理,这样就白白浪费了空闲时间,所以在API25开始,RecyclerView内部就实现了预取功能。
    RecyclerView的预取功能是依赖于GapWorker,通过每次的MOVE事件中,来判断是否预取下一个可能要显示的Item数据,判断的依据主要就是通过传入的dx和dy得到手指接下来可能要滑动的方向,如果dx或者dy的偏移量会导致下一个item要被显示出来则预取出来,但是并不是说预取下一个可能要显示的item一定都是成功的。
    其实每次rv取出要显示的一个item本质上就是取出一个viewholder,根据viewholder上关联的itemview来展示这个item。而预取出viewholder最核心的方法就是

    ViewHolder tryGetViewHolderForPositionByDeadline(int position,
                    boolean dryRun, long deadlineNs)
    

    deadlineNs的取值有两种,一种是为了兼容api25之前的没有预取功能的情况,一种就是实际的deadlineNs数值,超过这个deadline则表示预取失败,预取机制主要目的就是提高RecyclerView整体滑动的流畅性,所以有限制主要是为了保证整体流畅性。

    RecyclerView.onTouch

                case MotionEvent.ACTION_MOVE: {
                    final int index = e.findPointerIndex(mScrollPointerId);
                    if (index < 0) {
                        Log.e(TAG, "Error processing scroll; pointer index for id "
                                + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
                        return false;
                    }
    
                    final int x = (int) (e.getX(index) + 0.5f);
                    final int y = (int) (e.getY(index) + 0.5f);
                    int dx = mLastTouchX - x;
                    int dy = mLastTouchY - y;
    
                    if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset, TYPE_TOUCH)) {
                        dx -= mScrollConsumed[0];
                        dy -= mScrollConsumed[1];
                        vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
                        // Updated the nested offsets
                        mNestedOffsets[0] += mScrollOffset[0];
                        mNestedOffsets[1] += mScrollOffset[1];
                    }
    
                    if (mScrollState != SCROLL_STATE_DRAGGING) {
                        boolean startScroll = false;
                        if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
                            if (dx > 0) {
                                dx -= mTouchSlop;
                            } else {
                                dx += mTouchSlop;
                            }
                            startScroll = true;
                        }
                        if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
                            if (dy > 0) {
                                dy -= mTouchSlop;
                            } else {
                                dy += mTouchSlop;
                            }
                            startScroll = true;
                        }
                        if (startScroll) {
                            setScrollState(SCROLL_STATE_DRAGGING);
                        }
                    }
                    // 如果是正在滑动
                    if (mScrollState == SCROLL_STATE_DRAGGING) {
                        mLastTouchX = x - mScrollOffset[0];
                        mLastTouchY = y - mScrollOffset[1];
    
                        if (scrollByInternal(
                                canScrollHorizontally ? dx : 0,
                                canScrollVertically ? dy : 0,
                                vtev)) {
                            getParent().requestDisallowInterceptTouchEvent(true);
                        }
                        // 执行预取功能
                        if (mGapWorker != null && (dx != 0 || dy != 0)) {
                            mGapWorker.postFromTraversal(this, dx, dy);
                        }
                    }
                } break;
    

    GapWorker.postFromTraversal

        void postFromTraversal(RecyclerView recyclerView, int prefetchDx, int prefetchDy) {
            if (recyclerView.isAttachedToWindow()) {
                if (RecyclerView.DEBUG && !mRecyclerViews.contains(recyclerView)) {
                    throw new IllegalStateException("attempting to post unregistered view!");
                }
                // 判断第一次拖动时,是否将runnable交给Mainhandler里面,等待UI thread执行完成再执行prefetch
                // GapWorker其实就是一个Runnable的实现类
                if (mPostTimeNs == 0) {
                    mPostTimeNs = recyclerView.getNanoTime();
                    recyclerView.post(this);
                }
            }
            // 后续动作触发去更新最新的dx和dy,prefetch会按照最新的dx和dy计算prefetch的item的position
            recyclerView.mPrefetchRegistry.setPrefetchVector(prefetchDx, prefetchDy);
        }
    

    GapWorker.run

        @Override
        public void run() {
            try {
                TraceCompat.beginSection(RecyclerView.TRACE_PREFETCH_TAG);
                // 这里是因为存在嵌套RecyclerView的情况
                if (mRecyclerViews.isEmpty()) {
                    // abort - no work to do
                    return;
                }
    
                // Query most recent vsync so we can predict next one. Note that drawing time not yet
                // valid in animation/input callbacks, so query it here to be safe.
                final int size = mRecyclerViews.size();
                long latestFrameVsyncMs = 0;
                // 获取RecyclerView最近一次开始RenderThread的时间
                for (int i = 0; i < size; i++) {
                    RecyclerView view = mRecyclerViews.get(i);
                    if (view.getWindowVisibility() == View.VISIBLE) {
                        latestFrameVsyncMs = Math.max(view.getDrawingTime(), latestFrameVsyncMs);
                    }
                }
    
                if (latestFrameVsyncMs == 0) {
                    // abort - either no views visible, or couldn't get last vsync for estimating next
                    return;
                }
    
                // 计算预加载的最后时间,如果能在截止时间之前完成预加载,那么就可以成功完成ViewHolder的预加载
                // 否则就是预加载失败
                long nextFrameNs = TimeUnit.MILLISECONDS.toNanos(latestFrameVsyncMs) + mFrameIntervalNs;
    
                prefetch(nextFrameNs);
    
                // TODO: consider rescheduling self, if there's more work to do
            } finally {
                mPostTimeNs = 0;
                TraceCompat.endSection();
            }
        }
    

    GapWorker.prefetch

        void prefetch(long deadlineNs) {
            // 计算预加载任务列表
            buildTaskList();
            // 开始预加载
            flushTasksWithDeadline(deadlineNs);
        }
    

    GapWorker.buildTaskList

        private void buildTaskList() {
            // Update PrefetchRegistry in each view
            final int viewCount = mRecyclerViews.size();
            int totalTaskCount = 0;
            for (int i = 0; i < viewCount; i++) {
                RecyclerView view = mRecyclerViews.get(i);
                if (view.getWindowVisibility() == View.VISIBLE) {
                    view.mPrefetchRegistry.collectPrefetchPositionsFromView(view, false);
                    totalTaskCount += view.mPrefetchRegistry.mCount;
                }
            }
    
            // Populate task list from prefetch data...
            mTasks.ensureCapacity(totalTaskCount);
            int totalTaskIndex = 0;
            for (int i = 0; i < viewCount; i++) {
                RecyclerView view = mRecyclerViews.get(i);
                if (view.getWindowVisibility() != View.VISIBLE) {
                    // Invisible view, don't bother prefetching
                    continue;
                }
    
                LayoutPrefetchRegistryImpl prefetchRegistry = view.mPrefetchRegistry;
                final int viewVelocity = Math.abs(prefetchRegistry.mPrefetchDx)
                        + Math.abs(prefetchRegistry.mPrefetchDy);
                for (int j = 0; j < prefetchRegistry.mCount * 2; j += 2) {
                    final Task task;
                    if (totalTaskIndex >= mTasks.size()) {
                        // 针对每个预加载的ViewHolder创建一个Task
                        task = new Task();
                        mTasks.add(task);
                    } else {
                        task = mTasks.get(totalTaskIndex);
                    }
                    final int distanceToItem = prefetchRegistry.mPrefetchArray[j + 1];
    
                    task.immediate = distanceToItem <= viewVelocity;
                    task.viewVelocity = viewVelocity;
                    task.distanceToItem = distanceToItem;
                    task.view = view;
                    task.position = prefetchRegistry.mPrefetchArray[j];
    
                    totalTaskIndex++;
                }
            }
    
            // ... and priority sort
            Collections.sort(mTasks, sTaskComparator);
        }
    

    GapWorker.flushTasksWithDeadline

        private void flushTasksWithDeadline(long deadlineNs) {
            // 遍历所有的Task开始预加载
            for (int i = 0; i < mTasks.size(); i++) {
                final Task task = mTasks.get(i);
                // 当task中的view==null的时候完成预加载任务
                if (task.view == null) {
                    break; // done with populated tasks
                }
                flushTaskWithDeadline(task, deadlineNs);
                task.clear();
            }
        }
    

    GapWorker.flushTaskWithDeadline

        private void flushTaskWithDeadline(Task task, long deadlineNs) {
            long taskDeadlineNs = task.immediate ? RecyclerView.FOREVER_NS : deadlineNs;
            // 如果没能在deadlineNs之前构造好ViewHolder,则预加载失败
            // 在prefetchPositionWithDeadline中会调用RecyclerView.tryGetViewHolderForPositionByDeadline进行预加载
            // 这里是预取position位置的ViewHolder,在截止日期之前
            RecyclerView.ViewHolder holder = prefetchPositionWithDeadline(task.view,
                    task.position, taskDeadlineNs);
            if (holder != null
                    && holder.mNestedRecyclerView != null
                    && holder.isBound()
                    && !holder.isInvalid()) {
                // 预取RecyclerView内部的ViewHolder,这是处理嵌套RecyclerView的情况
                // 当外部预取的ViewHolder不是无效的,且是被绑定的
                prefetchInnerRecyclerViewWithDeadline(holder.mNestedRecyclerView.get(), deadlineNs);
            }
        }
    

    在RecyclerView.tryGetViewHolderForPositionByDeadline方法中会根据dx和dy以及当前滑动的方向计算预加载的position,dx和dy是在RecyclerView.onTouchEvent中滑动动态更新的,

    三、RecyclerView的ViewHolder的复用

    RecyclerView的ViewHolder的复用,其实就是从缓存中取出相应的ViewHolder来重新使用。
    复用流程包括手指滑动的时候和requestLayout()的
    而具体的复用流程其实都是依赖于RecyclerView.Recycler来实现的。


    RecyclerView复用机制流程.png

    1.手指滑动在onTouchEvent中触发

    (1)RecyclerView.onTouchEvent

    还是与预加载部分累死你,其实就是在预加载之前,通过调用scroollByInternal,开始判断缓存中是否有可以复用的ViewHolder

                    if (mScrollState == SCROLL_STATE_DRAGGING) {
                        mLastTouchX = x - mScrollOffset[0];
                        mLastTouchY = y - mScrollOffset[1];
    
                        if (scrollByInternal(
                                canScrollHorizontally ? dx : 0,
                                canScrollVertically ? dy : 0,
                                vtev)) {
                            getParent().requestDisallowInterceptTouchEvent(true);
                        }
                        if (mGapWorker != null && (dx != 0 || dy != 0)) {
                            mGapWorker.postFromTraversal(this, dx, dy);
                        }
                    }
    
    (2)RecyclerView.scrollByInternal
        boolean scrollByInternal(int x, int y, MotionEvent ev) {
            ...
            if (mAdapter != null) {
                // 执行滑动过程,在scrollStep中会调用mLayout.scrollVerticallyBy或者mLayout.scrollHorizontallyBy
                // mLayout其实就是LayoutManager的实现类对象,比如以LinearLayoutManager举例
                scrollStep(x, y, mScrollStepConsumed);
                consumedX = mScrollStepConsumed[0];
                consumedY = mScrollStepConsumed[1];
                unconsumedX = x - consumedX;
                unconsumedY = y - consumedY;
            }
            ...
            return consumedX != 0 || consumedY != 0;
        }
    
    (3)LinearLayoutManager.scrollVerticallyBy
        @Override
        public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
                RecyclerView.State state) {
            if (mOrientation == HORIZONTAL) {
                return 0;
            }
            return scrollBy(dy, recycler, state);
        }
    
    (4)LinearLayoutManager.scrollBy
        int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
            if (getChildCount() == 0 || dy == 0) {
                return 0;
            }
            mLayoutState.mRecycle = true;
            ensureLayoutState();
            final int layoutDirection = dy > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;
            final int absDy = Math.abs(dy);
            updateLayoutState(layoutDirection, absDy, true, state);
            // 调用fill填充LayoutState定义的给定布局,
            // 在fill中主要就是做了两件事:回收ViewHolder到缓存中;从缓存中取出ViewHolder进行复用
            final int consumed = mLayoutState.mScrollingOffset
                    + fill(recycler, mLayoutState, state, false);
            if (consumed < 0) {
                if (DEBUG) {
                    Log.d(TAG, "Don't have any more elements to scroll");
                }
                return 0;
            }
            final int scrolled = absDy > consumed ? layoutDirection * consumed : dy;
            mOrientationHelper.offsetChildren(-scrolled);
            if (DEBUG) {
                Log.d(TAG, "scroll req: " + dy + " scrolled: " + scrolled);
            }
            mLayoutState.mLastScrollDelta = scrolled;
            return scrolled;
        }
    

    在fill中主要会调用两个方法:recycleByLayoutState和layoutChunk,recycleByLayoutState主要的目的是用来回收ViewHolder的,而layoutChunk主要的目的就是从缓存中取出ViewHolder进行复用

    (5)LinearLayoutManager.fill()
        int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
                RecyclerView.State state, boolean stopOnFocusable) {
            // max offset we should set is mFastScroll + available
            final int start = layoutState.mAvailable;
            if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
                // TODO ugly bug fix. should not happen
                if (layoutState.mAvailable < 0) {
                    layoutState.mScrollingOffset += layoutState.mAvailable;
                }
                // 首先调用recycleByLayoutState回收ViewHolder
                // 这个回收是从二级缓存开始回收,即回收的时候,最少都是将回收的ViewHolder加入到mCacheViews中
                recycleByLayoutState(recycler, layoutState);
            }
            int remainingSpace = layoutState.mAvailable + layoutState.mExtra;
            LayoutChunkResult layoutChunkResult = mLayoutChunkResult;
            while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
                layoutChunkResult.resetInternal();
                if (RecyclerView.VERBOSE_TRACING) {
                    TraceCompat.beginSection("LLM LayoutChunk");
                }
                // 执行复用,layoutChunk的过程其实就是从缓存中取出View、或者创建View,最后调用addView
                // 遍历调用layoutChunk
                layoutChunk(recycler, state, layoutState, layoutChunkResult);
                if (RecyclerView.VERBOSE_TRACING) {
                    TraceCompat.endSection();
                }
                if (layoutChunkResult.mFinished) {
                    break;
                }
                layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection;
                /**
                 * Consume the available space if:
                 * * layoutChunk did not request to be ignored
                 * * OR we are laying out scrap children
                 * * OR we are not doing pre-layout
                 */
                if (!layoutChunkResult.mIgnoreConsumed || mLayoutState.mScrapList != null
                        || !state.isPreLayout()) {
                    layoutState.mAvailable -= layoutChunkResult.mConsumed;
                    // we keep a separate remaining space because mAvailable is important for recycling
                    remainingSpace -= layoutChunkResult.mConsumed;
                }
    
                if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
                    layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
                    if (layoutState.mAvailable < 0) {
                        layoutState.mScrollingOffset += layoutState.mAvailable;
                    }
                    recycleByLayoutState(recycler, layoutState);
                }
                if (stopOnFocusable && layoutChunkResult.mFocusable) {
                    break;
                }
            }
            if (DEBUG) {
                validateChildOrder();
            }
            return start - layoutState.mAvailable;
        }
    
    (6)LinearLayoutManager.layoutChunk
        void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
                LayoutState layoutState, LayoutChunkResult result) {
            // 取出下一个View
            View view = layoutState.next(recycler);
            if (view == null) {
                if (DEBUG && layoutState.mScrapList == null) {
                    throw new RuntimeException("received null view when unexpected");
                }
                // if we are laying out views in scrap, this may return null which means there is
                // no more items to layout.
                result.mFinished = true;
                return;
            }
            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
            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);
                }
            }
            // 测量子View(即每个item的预留空间)
            // 测量预留空间主要就是通过mItemDecorations遍历取出每个ItemDecoration,然后调用getItemOffsets
            // 在getItemOffsets中调用outRect给每个item预留空间,用于绘制
            measureChildWithMargins(view, 0, 0);
            result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);
            int left, top, right, bottom;
            if (mOrientation == VERTICAL) {
                if (isLayoutRTL()) {
                    right = getWidth() - getPaddingRight();
                    left = right - mOrientationHelper.getDecoratedMeasurementInOther(view);
                } else {
                    left = getPaddingLeft();
                    right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
                }
                if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
                    bottom = layoutState.mOffset;
                    top = layoutState.mOffset - result.mConsumed;
                } else {
                    top = layoutState.mOffset;
                    bottom = layoutState.mOffset + result.mConsumed;
                }
            } else {
                top = getPaddingTop();
                bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view);
    
                if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
                    right = layoutState.mOffset;
                    left = layoutState.mOffset - result.mConsumed;
                } else {
                    left = layoutState.mOffset;
                    right = layoutState.mOffset + result.mConsumed;
                }
            }
            // We calculate everything with View's bounding box (which includes decor and margins)
            // To calculate correct layout position, we subtract margins.
            layoutDecoratedWithMargins(view, left, top, right, bottom);
            if (DEBUG) {
                Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:"
                        + (left + params.leftMargin) + ", t:" + (top + params.topMargin) + ", r:"
                        + (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin));
            }
            // Consume the available space if the view is not removed OR changed
            if (params.isItemRemoved() || params.isItemChanged()) {
                result.mIgnoreConsumed = true;
            }
            result.mFocusable = view.hasFocusable();
        }
    
    (7)LinearLayoutManager.LayoutState.next
            View next(RecyclerView.Recycler recycler) {
                if (mScrapList != null) {
                    return nextViewFromScrapList();
                }
                // 调用RecyclerView.Recycler的复用逻辑,这里就是进行复用的流程
                // 这个View其实取出的就是ViewHolder.itemView
                final View view = recycler.getViewForPosition(mCurrentPosition);
                mCurrentPosition += mItemDirection;
                return view;
            }
    
    (8)RecyclerView.Recycler.getViewForPosition

    这个方法内部很简单,主要是做了一件事,其实就是调用RecyclerView.Recycler.tryGetViewHolderForPositionByDeadline方法,这个方法在预取的时候,其实就是用来复用或者创建ViewHolder的

    (9)RecyclerView.Recycler.tryGetViewHolderForPositionByDeadline

    tryGetViewHolderForPositionByDeadline方法是RecyclerView的整个预取复用流程的关键,因为RecyclerView的缓存其实是基于ViewHolder的,需要的View其实也是从ViewHolder中取出

            @Nullable
            ViewHolder tryGetViewHolderForPositionByDeadline(int position,
                    boolean dryRun, long deadlineNs) {
                if (position < 0 || position >= mState.getItemCount()) {
                    throw new IndexOutOfBoundsException("Invalid item position " + position
                            + "(" + position + "). Item count:" + mState.getItemCount()
                            + exceptionLabel());
                }
                boolean fromScrapOrHiddenOrCache = false;
                ViewHolder holder = null;
                // 0) If there is a changed scrap, try to find from there
                // 判断mChangedScrap是否有缓存,如果有则取出
                // 不过这个有一个前提,就是在预布局的时候,我们这个流程是onTouchEvent的时候所以并不会满足
                if (mState.isPreLayout()) {
                    // 分别根据position或者id取出对应的ViewHolder
                    // ChangedScrap缓存主要是与动画相关的
                    // 这里有一个条件mState.isPreLayout()要为true
                    // 一般在我们调用adapter的notifyItemChanged等方法时为true
                    // 因为数据发生了变化,viewholder被detach掉后缓存在mChangedScrap之中,在这里拿到的viewHolder后续需要重新绑定
                    holder = getChangedScrapViewForPosition(position);
                    fromScrapOrHiddenOrCache = holder != null;
                }
                // 1) Find by position from scrap/hidden list/cache
                // 从mAttachedScrap或者mCachedViews中查找是否有ViewHolder,这里是通过position寻找
                // 即判断ViewHolder.LayoutPosition==position
                if (holder == null) {
                    // 主要是根据position获取
                    // 这里可以做三件事:1.从mAttachedScrap一级缓存中找到ViewHolder
                    // 2.回收隐藏的但是未删除的View到一级缓存中(调用scrapView,可能是进入mAttachedScrap也可能是mChangedScrap)
                    // 3.从mCachedViews二级缓存中找到ViewHolder
                    holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
                    if (holder != null) {
                        if (!validateViewHolderForOffsetPosition(holder)) {
                            // recycle holder (and unscrap if relevant) since it can't be used
                            // 回收该holder,因为该holder不可用,这时是将这个不可用的ViewHolder加入到二级缓存中
                            // 这个ViewHolder可能是从一级缓存中取出,也可能是从二级缓存中取出
                            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();
                                }
                                // 如果取出的ViewHolder是不可用的,则加入到mCachedViews中,
                                // 但是如果不满足加入到mCachedViews的条件,则会加入到RecyclerViewPool中
                                recycleViewHolderInternal(holder);
                            }
                            holder = null;
                        } else {
                            fromScrapOrHiddenOrCache = true;
                        }
                    }
                }
                if (holder == null) {
                    final int offsetPosition = mAdapterHelper.findPositionOffset(position);
                    if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
                        throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
                                + "position " + position + "(offset:" + offsetPosition + ")."
                                + "state:" + mState.getItemCount() + exceptionLabel());
                    }
    
                    final int type = mAdapter.getItemViewType(offsetPosition);
                    // 2) Find from scrap/cache via stable ids, if exists
                    // 找到ViewHolder对应的itemId,从mAttachedScrap或者mCachedViews中找到对应的ViewHolder
                    // 即通过判断holder.getItemId() == id,id是传入的目标id
                    if (mAdapter.hasStableIds()) {
                        // 首先会从mAttachedScrap获取ViewHolder
                        // 如果是不可用的,则会调用quickRecycleScrapView,其内部会调用recycleViewHolderInternal
                        // 将ViewHolder回收加入到mCachedViews中
                        // 如果mAttachedScrap找不到,则会从mCachedViews中查询ViewHolder
                        // 如果找到但是不可用的,则会调用recycleCachedViewAt,其内部会调用addViewHolderToRecycledViewPool
                        // 将ViewHolder加入到RecyclerViewPool中
                        holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
                                type, dryRun);
                        if (holder != null) {
                            // update position
                            holder.mPosition = offsetPosition;
                            fromScrapOrHiddenOrCache = true;
                        }
                    }
                    // 从mViewCacheExtension中查询缓存的ViewHolder
                    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);
                            if (holder == null) {
                                throw new IllegalArgumentException("getViewForPositionAndType returned"
                                        + " a view which does not have a ViewHolder"
                                        + exceptionLabel());
                            } else if (holder.shouldIgnore()) {
                                throw new IllegalArgumentException("getViewForPositionAndType returned"
                                        + " a view that is ignored. You must call stopIgnoring before"
                                        + " returning this view." + exceptionLabel());
                            }
                        }
                    }
                    // 如果前面四种情况都没找到holder,则查询RecyclerViewPool
                    // RecyclerViewPool缓存默认上限是5个,是每个ViewType的上限为5个
                    // 根据ViewHolder的ViewType先查找对应的缓存ViewHolder的List
                    // 然后从List的末尾出队一个ViewHolder
                    if (holder == null) { // fallback to pool
                        if (DEBUG) {
                            Log.d(TAG, "tryGetViewHolderForPositionByDeadline("
                                    + position + ") fetching from shared pool");
                        }
                        holder = getRecycledViewPool().getRecycledView(type);
                        if (holder != null) {
                            holder.resetInternal();
                            if (FORCE_INVALIDATE_DISPLAY_LIST) {
                                invalidateDisplayListInt(holder);
                            }
                        }
                    }
                    // 如果前面四步查询缓存都没找到对应的ViewHolder,则调用adapter.createViewHolder创建ViewHolder
                    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);
                            }
                        }
    
                        long end = getNanoTime();
                        mRecyclerPool.factorInCreateTime(type, end - start);
                        if (DEBUG) {
                            Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder");
                        }
                    }
                }
    
                // This is very ugly but the only place we can grab this information
                // before the View is rebound and returned to the LayoutManager for post layout ops.
                // We don't need this in pre-layout since the VH is not updated by the LM.
                if (fromScrapOrHiddenOrCache && !mState.isPreLayout() && holder
                        .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) {
                    holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
                    if (mState.mRunSimpleAnimations) {
                        int changeFlags = ItemAnimator
                                .buildAdapterChangeFlagsForAnimations(holder);
                        changeFlags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
                        final ItemHolderInfo info = mItemAnimator.recordPreLayoutInformation(mState,
                                holder, changeFlags, holder.getUnmodifiedPayloads());
                        recordAnimationInfoIfBouncedHiddenView(holder, info);
                    }
                }
    
                boolean bound = false;
                if (mState.isPreLayout() && holder.isBound()) {
                    // do not update unless we absolutely have to.
                    holder.mPreLayoutPosition = position;
                } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
                    if (DEBUG && holder.isRemoved()) {
                        throw new IllegalStateException("Removed holder should be bound and it should"
                                + " come here only in pre-layout. Holder: " + holder
                                + exceptionLabel());
                    }
                    final int offsetPosition = mAdapterHelper.findPositionOffset(position);
                    // 获取到holder之后,调用Adapter.bindViewHolder方法绑定ViewHolder
                    bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
                }
    
                final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
                final LayoutParams rvLayoutParams;
                if (lp == null) {
                    rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
                    holder.itemView.setLayoutParams(rvLayoutParams);
                } else if (!checkLayoutParams(lp)) {
                    rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
                    holder.itemView.setLayoutParams(rvLayoutParams);
                } else {
                    rvLayoutParams = (LayoutParams) lp;
                }
                rvLayoutParams.mViewHolder = holder;
                rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound;
                return holder;
            }
    
    tryGetViewHolderForPositionByDeadline方法总结:

    在复用阶段:

    • 首先会从getScrapOrHiddenOrCachedHolderForPosition查询,如果找到的ViewHolder是不可用的,如果是从一级缓存中找到的,则加入到mCachedViews中,如果是从二级缓存中找到的,则加入到mRecyclerViewPool中。在这里,如果dryRun为false的时候,还会去获取隐藏的但是没有被remove的View,调用scrapView方法保存在mAttachedScrap或者mChangedScrap
    • 如果Adapter有稳定的id,则从getScrapOrCachedViewForId中获取ViewHolder,首先也是从一级缓存mAttachedScrap中获取,如果该holder是不可用的,则调用quickRecycleScrapView,其内部调用recycleViewHolderInternal,将ViewHolder加入到二级缓存中,但是如果mCachedViews已经无法加入,则会将mCachedViews的第0个出队加入到RecyclerViewPool中,然后将新进入的ViewHolder加入到mCachedViews中,如果一级缓存中无法获取到对应的ViewHolder,则从二级缓存mCachedViews中获取,如果获取到的ViewHolder是不可用的,则通过调用recycleCachedViewAt方法,在其内部调用addViewHolderToRecycledViewPool方法,将ViewHolder加入到RecyclerViewPool中。
    tryGetViewHolderForPositionByDeadline
    ->getScrapOrHiddenOrCachedHolderForPosition
    ->scrapView(回收隐藏但没有remove的View加入一级缓存)
    ->recycleViewHolderInternal(回收取自一级缓存的ViewHolder加入mCachedViews,回收取自mCachedViews中的ViewHolder加入RecyclerViewPool)
    
    tryGetViewHolderForPositionByDeadline
    ->getScrapOrCachedViewForId
      ->quickRecycleScrapView(回收取自mAttachedScrap一级缓存的View)
        ->recycleViewHolderInternal(具体回收,将上一步取出的View加入mCachedViews)
      ->recycleCachedViewAt(回收取自mCachedViews二级缓存的View)
        ->addViewHolderToRecycledViewPool(将上一步取自mCachedViews的View,加入到RecyclerViewPool中)
    

    整个复用流程,其实最终是通过调用RecyclerView.Recycler.tryGetViewHolderForPositionByDeadline方法进行,而复用流程中,还会判断从缓存中或者隐藏但是没有remove的ViewHolder是否可以使用,如果不可使用,就从低级缓存加入到高级缓存中。
    在这里使用的方法,其实都是属于RecyclerView.Recycler这个内部类,Recycler是真正对ViewHolder进行回收复用的类

    关于RecyclerViewPool

    Pool默认大小为5个。即每个ViewType对应的ArrayList的大小最大为5个。
    RecyclerViewPool是通过ViewType进行缓存的,缓存的是一个ArrayList<VIewHolder>
    RecyclerViewPool.ScrapData就是RecyclerViewPool的缓存单元

            static class ScrapData {
                final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
                int mMaxScrap = DEFAULT_MAX_SCRAP;
                long mCreateRunningAverageNs = 0;
                long mBindRunningAverageNs = 0;
            }
    

    RecyclerViewPool,是根据ViewType取出对应的缓存数据。根据ViewType缓存ScrapData到SparseArray中,而ScrapData这个类中有一个ArrayList,这个ArrayList就是用来缓存这个ViewType类型对应的ViewHolder的。这里是一个先进后出的结构,这是跟回收机制有关,才采用先进后出的结构。看下面的源码,可以看出,每次对RecyclerViewPool的出队都是优先最后一个,入队都是添加到末尾。

            @Nullable
            public ViewHolder getRecycledView(int viewType) {
                final ScrapData scrapData = mScrap.get(viewType);
                if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
                    final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
                    return scrapHeap.remove(scrapHeap.size() - 1);
                }
                return null;
            }
    
    (10)RecyclerView的预布局

    判断RecyclerView是否处于预布局,则需要通过RecyclerView.State.mInPreLayout属性来判断,即当mInPreLayout为true的时候是处于预布局的时候。
    而mInPreLayout赋值为true的地方有两部分,一部分是在onMeasure中,一部分是在onLayout中。不过这两部分都与RecyclerView.State.mRunPredictiveAnimations挂钩,在onMeasure中的mInPreLayout = true,需要满足RecyclerView.State.mRunPredictiveAnimations = true,而在onLayout中,直接就是将mInPreLayout = mRunPredictiveAnimations,所以就需要找到mRunPredictiveAnimations赋值为true的地方,即在RecyclerView.processAdapterUpdatesAndSetAnimationFlags()方法中,将mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations,所以有需要找到mRunSimpleAnimations赋值为true的地方,而mRunSimpleAnimations的赋值又与另一个变量有关,即RecyclerView.mFirstLayoutComplete,而mFirstLayoutComplete赋值为true的时候,才能让mInPreLayout为true。
    mFirstLayoutComplete赋值为true,是在onLayout中,但是是在onLayout的末尾才赋值为true。

        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG);
            dispatchLayout();
            TraceCompat.endSection();
            mFirstLayoutComplete = true;
        }
    

    所以在第一次执行onLayout的时候,肯定不会执行预布局。其实onLayout调用过程中,调用dispatchLayout中dispatchLayoutStep1()方法其实主要就是用来完成预布局功能。
    dispatchLayoutStep1()
    1.处理Adapter更新;2.决定是否执行ItemAnimator;3.保存ItemView的动画信息。本方法也被称为preLayout(预布局),当Adapter更新了,这个方法会保存每个ItemView的旧信息(oldViewHolderInfo)

    2.RecyclerView.onLayout触发回收复用

    (1)RecyclerView.onLayout
    (2)RecyclerView.dispatchLayout()
    (3)RecyclerView.dispatchLayoutStep1/2/3

    不过dispatchLayoutStep1()内部的执行,都想需要依赖于mRunSimpleAnimations为true,而mRunSimpleAnimations为true,需要mFirstLayoutComplete为true,所以在第一次执行onLayout的时候,并不会执行dispatchLayoutStep1()中的主要的内容。

        // 这里如果mRunSimpleAnimations不为true,主要就是做一些初始化
        private void dispatchLayoutStep1() {
            mState.assertLayoutStep(State.STEP_START);
            // 保存剩余的滚动值,包括x和y方向还可以滚动多少距离
            fillRemainingScrollValues(mState);
            mState.mIsMeasuring = false;
            // 开始拦截布局请求,应该是拦截requestLayout过程
            startInterceptRequestLayout();
            mViewInfoStore.clear();
            // 布局或者滚动计数+1,即onTouchEvent的move事件或者onLayout过程的计数+1
            onEnterLayoutOrScroll();
            processAdapterUpdatesAndSetAnimationFlags();
            saveFocusInfo();
            mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;
            mItemsAddedOrRemoved = mItemsChanged = false;
            mState.mInPreLayout = mState.mRunPredictiveAnimations;
            mState.mItemCount = mAdapter.getItemCount();
            findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
            // 运行简单动画
            if (mState.mRunSimpleAnimations) {
                // Step 0: Find out where all non-removed items are, pre-layout
                // 找到没有被remove的itemView,并且将这个itemView的ViewHolder保存在mViewInfoStore,
                // 同时还将预布局的位置也保存在mViewInfoStore中
                int count = mChildHelper.getChildCount();
                for (int i = 0; i < count; ++i) {
                    final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
                    if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
                        continue;
                    }
                    final ItemHolderInfo animationInfo = mItemAnimator
                            .recordPreLayoutInformation(mState, holder,
                                    ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
                                    holder.getUnmodifiedPayloads());
                    mViewInfoStore.addToPreLayout(holder, animationInfo);
                    if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
                            && !holder.shouldIgnore() && !holder.isInvalid()) {
                        long key = getChangedHolderKey(holder);
                        // This is NOT the only place where a ViewHolder is added to old change holders
                        // list. There is another case where:
                        //    * A VH is currently hidden but not deleted
                        //    * The hidden item is changed in the adapter
                        //    * Layout manager decides to layout the item in the pre-Layout pass (step1)
                        // When this case is detected, RV will un-hide that view and add to the old
                        // change holders list.
                        mViewInfoStore.addToOldChangeHolders(key, holder);
                    }
                }
            }
            // 运行预动画
            if (mState.mRunPredictiveAnimations) {
                // 在这里会使用旧的position的item进行预布局。而且在这里会调用onLayoutChildren进行布局
                // 不过这里只是进行预布局,只是先确定每个itemView的位置,预布局之后,
                // 此时取到的每个ItemView的ViewHolder和ItemHolderInfo,便是每个ItemView的最终信息。
                // Step 1: run prelayout: This will use the old positions of items. The layout manager
                // is expected to layout everything, even removed items (though not to add removed
                // items back to the container). This gives the pre-layout position of APPEARING views
                // which come into existence as part of the real layout.
    
                // Save old positions so that LayoutManager can run its mapping logic.
                saveOldPositions();
                final boolean didStructureChange = mState.mStructureChanged;
                mState.mStructureChanged = false;
                // temporarily disable flag because we are asking for previous layout
                mLayout.onLayoutChildren(mRecycler, mState);
                mState.mStructureChanged = didStructureChange;
    
                for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
                    final View child = mChildHelper.getChildAt(i);
                    final ViewHolder viewHolder = getChildViewHolderInt(child);
                    if (viewHolder.shouldIgnore()) {
                        continue;
                    }
                    if (!mViewInfoStore.isInPreLayout(viewHolder)) {
                        int flags = ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder);
                        boolean wasHidden = viewHolder
                                .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
                        if (!wasHidden) {
                            flags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
                        }
                        final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation(
                                mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads());
                        if (wasHidden) {
                            recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo);
                        } else {
                            mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
                        }
                    }
                }
                // we don't process disappearing list because they may re-appear in post layout pass.
                clearOldPositions();
            } else {
                clearOldPositions();
            }
            onExitLayoutOrScroll();
            stopInterceptRequestLayout(false);
            mState.mLayoutStep = State.STEP_LAYOUT;
        }
    
    (4)LinearLayoutManager.onLayoutChildren()

    从这个方法开始,其实流程就与MOVE事件滑动触发onTouchEvent,然后复用或者创建ViewHolder的流程一致。

        @Override
        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
            // 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.
            // create layout state
            if (DEBUG) {
                Log.d(TAG, "is pre layout:" + state.isPreLayout());
            }
            if (mPendingSavedState != null || mPendingScrollPosition != RecyclerView.NO_POSITION) {
                // 如果列表的item数目为0,清空所有的View
                if (state.getItemCount() == 0) {
                    removeAndRecycleAllViews(recycler);
                    return;
                }
            }
            if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) {
                mPendingScrollPosition = mPendingSavedState.mAnchorPosition;
            }
    
            ensureLayoutState();
            mLayoutState.mRecycle = false;
            // resolve layout direction
            resolveShouldLayoutReverse();
            // 第一步:确定锚点信息
            final View focused = getFocusedChild();
            if (!mAnchorInfo.mValid || mPendingScrollPosition != RecyclerView.NO_POSITION
                    || mPendingSavedState != null) {
                mAnchorInfo.reset();
                mAnchorInfo.mLayoutFromEnd = mShouldReverseLayout ^ mStackFromEnd;
                // 计算锚点的位置和坐标
                updateAnchorInfoForLayout(recycler, state, mAnchorInfo);
                mAnchorInfo.mValid = true;
            } else if (focused != null && (mOrientationHelper.getDecoratedStart(focused)
                            >= mOrientationHelper.getEndAfterPadding()
                    || mOrientationHelper.getDecoratedEnd(focused)
                    <= mOrientationHelper.getStartAfterPadding())) {
                mAnchorInfo.assignFromViewAndKeepVisibleRect(focused, getPosition(focused));
            }
            if (DEBUG) {
                Log.d(TAG, "Anchor info:" + mAnchorInfo);
            }
    
            // LLM may decide to layout items for "extra" pixels to account for scrolling target,
            // caching or predictive animations.
            int extraForStart;
            int extraForEnd;
            final int extra = getExtraLayoutSpace(state);
            // If the previous scroll delta was less than zero, the extra space should be laid out
            // at the start. Otherwise, it should be at the end.
            if (mLayoutState.mLastScrollDelta >= 0) {
                extraForEnd = extra;
                extraForStart = 0;
            } else {
                extraForStart = extra;
                extraForEnd = 0;
            }
            extraForStart += mOrientationHelper.getStartAfterPadding();
            extraForEnd += mOrientationHelper.getEndPadding();
            if (state.isPreLayout() && mPendingScrollPosition != RecyclerView.NO_POSITION
                    && mPendingScrollPositionOffset != INVALID_OFFSET) {
                // if the child is visible and we are going to move it around, we should layout
                // extra items in the opposite direction to make sure new items animate nicely
                // instead of just fading in
                final View existing = findViewByPosition(mPendingScrollPosition);
                if (existing != null) {
                    final int current;
                    final int upcomingOffset;
                    if (mShouldReverseLayout) {
                        current = mOrientationHelper.getEndAfterPadding()
                                - mOrientationHelper.getDecoratedEnd(existing);
                        upcomingOffset = current - mPendingScrollPositionOffset;
                    } else {
                        current = mOrientationHelper.getDecoratedStart(existing)
                                - mOrientationHelper.getStartAfterPadding();
                        upcomingOffset = mPendingScrollPositionOffset - current;
                    }
                    if (upcomingOffset > 0) {
                        extraForStart += upcomingOffset;
                    } else {
                        extraForEnd -= upcomingOffset;
                    }
                }
            }
            int startOffset;
            int endOffset;
            final int firstLayoutDirection;
            if (mAnchorInfo.mLayoutFromEnd) {
                firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL
                        : LayoutState.ITEM_DIRECTION_HEAD;
            } else {
                firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD
                        : LayoutState.ITEM_DIRECTION_TAIL;
            }
    
            onAnchorReady(recycler, state, mAnchorInfo, firstLayoutDirection);
            // 对所有的ItemView进行回收,这里是将ItemView回收到了mCachedViews中
            detachAndScrapAttachedViews(recycler);
            mLayoutState.mInfinite = resolveIsInfinite();
            mLayoutState.mIsPreLayout = state.isPreLayout();
            if (mAnchorInfo.mLayoutFromEnd) {
                // 开始填充
                updateLayoutStateToFillStart(mAnchorInfo);
                mLayoutState.mExtra = extraForStart;
                fill(recycler, mLayoutState, state, false);
                startOffset = mLayoutState.mOffset;
                final int firstElement = mLayoutState.mCurrentPosition;
                if (mLayoutState.mAvailable > 0) {
                    extraForEnd += mLayoutState.mAvailable;
                }
                // fill towards end
                updateLayoutStateToFillEnd(mAnchorInfo);
                mLayoutState.mExtra = extraForEnd;
                mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
                fill(recycler, mLayoutState, state, false);
                endOffset = mLayoutState.mOffset;
    
                if (mLayoutState.mAvailable > 0) {
                    // end could not consume all. add more items towards start
                    extraForStart = mLayoutState.mAvailable;
                    updateLayoutStateToFillStart(firstElement, startOffset);
                    mLayoutState.mExtra = extraForStart;
                    fill(recycler, mLayoutState, state, false);
                    startOffset = mLayoutState.mOffset;
                }
            } else {
                // fill towards end
                updateLayoutStateToFillEnd(mAnchorInfo);
                mLayoutState.mExtra = extraForEnd;
                fill(recycler, mLayoutState, state, false);
                endOffset = mLayoutState.mOffset;
                final int lastElement = mLayoutState.mCurrentPosition;
                if (mLayoutState.mAvailable > 0) {
                    extraForStart += mLayoutState.mAvailable;
                }
                // fill towards start
                updateLayoutStateToFillStart(mAnchorInfo);
                mLayoutState.mExtra = extraForStart;
                mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
                fill(recycler, mLayoutState, state, false);
                startOffset = mLayoutState.mOffset;
    
                if (mLayoutState.mAvailable > 0) {
                    extraForEnd = mLayoutState.mAvailable;
                    // start could not consume all it should. add more items towards end
                    updateLayoutStateToFillEnd(lastElement, endOffset);
                    mLayoutState.mExtra = extraForEnd;
                    fill(recycler, mLayoutState, state, false);
                    endOffset = mLayoutState.mOffset;
                }
            }
    
            // changes may cause gaps on the UI, try to fix them.
            // TODO we can probably avoid this if neither stackFromEnd/reverseLayout/RTL values have
            // changed
            if (getChildCount() > 0) {
                // because layout from end may be changed by scroll to position
                // we re-calculate it.
                // find which side we should check for gaps.
                if (mShouldReverseLayout ^ mStackFromEnd) {
                    int fixOffset = fixLayoutEndGap(endOffset, recycler, state, true);
                    startOffset += fixOffset;
                    endOffset += fixOffset;
                    fixOffset = fixLayoutStartGap(startOffset, recycler, state, false);
                    startOffset += fixOffset;
                    endOffset += fixOffset;
                } else {
                    int fixOffset = fixLayoutStartGap(startOffset, recycler, state, true);
                    startOffset += fixOffset;
                    endOffset += fixOffset;
                    fixOffset = fixLayoutEndGap(endOffset, recycler, state, false);
                    startOffset += fixOffset;
                    endOffset += fixOffset;
                }
            }
            layoutForPredictiveAnimations(recycler, state, startOffset, endOffset);
            if (!state.isPreLayout()) {
                mOrientationHelper.onLayoutComplete();
            } else {
                mAnchorInfo.reset();
            }
            mLastStackFromEnd = mStackFromEnd;
            if (DEBUG) {
                validateChildOrder();
            }
        }
    

    3.RecyclerView缓存复用机制总结

    其实复用机制,最终都是进入fill方法进行复用,将ViewHolder从缓存中取出进行复用。而fill获取View,然后将View通过addView添加到布局中。而fill()方法中获取的View,其实就是通过调用LayoutState.next(),内部会优先从4级缓存中取出holder,如果都没有,则调用createViewHolder创建holder,然后会返回holder中的itemView用来添加到RecyclerView中。

    四、RecyclerView的ViewHolder的回收

    在onLayout流程的回收中,其实会有两部分缓存的回收,其一就是在onLayoutChildren中调用detachAndScrapAttachedViews调用scrapOrRecycleView()回收holder到一级缓存,调用RecyclerView.Recycler.recycleViewHolderInternal回收holder到2-4级缓存中;其二就是在onLayoutChildren中调用fill回收holder到2-4级缓存中。


    RecyclerView回收机制流程.png

    RecyclerView的ViewHolder的回收,也从onTouchEvent和onLayout两个流程进行分析

    1.onTouchEvent分析ViewHolder的回收

    前面一部分与ViewHolder复用过程其实是一样的。
    RecyclerView.onTouchEvent的move事件->
    RecyclerView.scrollByInternal->
    mLayout.scrollVerticallyBy()(mLayout其实就是LayoutManager)->
    LinearLayoutManager.scrollVerticallyBy()->
    LinearLayoutManager.scrollBy()->
    LinearLayoutManager.fill(recycler, mLayoutState, state, false)->
    即最终都会执行LinearLayoutManager.fill()方法,而在LinearLayoutManager.fill()方法中,首先就会调用recycleByLayoutState进行回收。

    (1)LinearLayoutManager.recycleByLayoutState
        private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) {
            if (!layoutState.mRecycle || layoutState.mInfinite) {
                return;
            }
            if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
                recycleViewsFromEnd(recycler, layoutState.mScrollingOffset);
            } else {
                recycleViewsFromStart(recycler, layoutState.mScrollingOffset);
            }
        }
    

    这里的两个方法其实类似,只不过一个是开始位置,一个是从结束位置。

    (2)LinearLayoutManager.recycleViewsFromEnd
        private void recycleViewsFromEnd(RecyclerView.Recycler recycler, int dt) {
            final int childCount = getChildCount();
            if (dt < 0) {
                if (DEBUG) {
                    Log.d(TAG, "Called recycle from end with a negative value. This might happen"
                            + " during layout changes but may be sign of a bug");
                }
                return;
            }
            final int limit = mOrientationHelper.getEnd() - dt;
            if (mShouldReverseLayout) {
                for (int i = 0; i < childCount; i++) {
                    View child = getChildAt(i);
                    if (mOrientationHelper.getDecoratedStart(child) < limit
                            || mOrientationHelper.getTransformedStartWithDecoration(child) < limit) {
                        // stop here
                        recycleChildren(recycler, 0, i);
                        return;
                    }
                }
            } else {
                for (int i = childCount - 1; i >= 0; i--) {
                    View child = getChildAt(i);
                    if (mOrientationHelper.getDecoratedStart(child) < limit
                            || mOrientationHelper.getTransformedStartWithDecoration(child) < limit) {
                        // stop here
                        recycleChildren(recycler, childCount - 1, i);
                        return;
                    }
                }
            }
        }
    

    在这里其实就是调用recycleChildren进行回收

    (3)LinearLayoutManager.recycleChildren
        private void recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex) {
            if (startIndex == endIndex) {
                return;
            }
            if (DEBUG) {
                Log.d(TAG, "Recycling " + Math.abs(startIndex - endIndex) + " items");
            }
            if (endIndex > startIndex) {
                for (int i = endIndex - 1; i >= startIndex; i--) {
                    removeAndRecycleViewAt(i, recycler);
                }
            } else {
                for (int i = startIndex; i > endIndex; i--) {
                    removeAndRecycleViewAt(i, recycler);
                }
            }
        }
    
    (4)LinearLayoutManager.removeAndRecycleViewAt
            public void removeAndRecycleViewAt(int index, @NonNull Recycler recycler) {
                final View view = getChildAt(index);
                // 在这里,只是先remove,但是并没有detach
                // 绑定的数据等信息都还在,这意味着从mCachedViews取出的视图如果符合需要的目标视图是可以直接展示的,而不需要重新绑定
                removeViewAt(index);
                recycler.recycleView(view);
            }
    

    其实从这四步可以看出,最终就是调用RecyclerView.Recycler.recycleView()方法,而该方法内部就是通过调用RecyclerView.Recycler.recycleViewHolderInternal()进行回收,将ViewHolder保存在mCachedViews、RecyclerViewPool等二到四级缓存中。
    在RecyclerView.Recycler.recycleViewHolderInternal()方法进行回收的过程中,首先会判断mCachedViews是否已经满了,mCachedViews的默认大小为2,如果mCachedViews已经满了,则会将先进入mCachedViews中的ViewHolder移除,并且将这个移除的ViewHolder加入到RecyclerViewPool中,在加入到RecyclerViewPool中的时候,会判断当前ViewType对应的List集合是否已经大于等于5个,如果已经达到这个最大值,则会放弃最新加入的ViewHolder。
    mCachedViews中缓存的ViewHolder是带有数据的,所以这些ViewHolder是不同的;而RecyclerViewPool中缓存的ViewHolder是一样的,是不带有数据的。因为在将ViewHolder添加到pool之前,会调用ViewHolder.resetInternal方法重置数据,这样每个ViewHolder就变成一样的不带数据的。
    因为在添加进mCachedViews之前,只是remove了视图,而没有detach,说明绑定的数据等信息都还在,这意味着从mCachedViews取出的视图如果符合需要的目标视图是可以直接展示的,而不需要重新绑定

    2.onLayout回收流程

    (1)根据onLayout的回收流程:在RecyclerView.dispatchLayout()中调用的方法解析

    dispatchLayoutStep1

    1. Adapter的更新;
    2. 决定该启动哪种动画; 即是否执行ItemAnimator
    3. 保存当前View的信息(getLeft(), getRight(), getTop(), getBottom()等);
    4. 如果有必要,先跑一次布局并将信息保存下来。
      dispatchLayoutStep1方法其实也是RecyclerView的预布局,在第一次执行onLayout的时候,并不会执行该方法,因为该方法的执行在流程上mFirstLayoutComplete赋值为true的时候,而第一个onLayout的时候,dispatchLayoutStep1是在dispatchLayout()中执行,dispatchLayout()是早于mFirstLayoutComplete = true;,dispatchLayoutStep1预布局会保存每个ItemView的旧信息(oldViewHolderInfo)

    dispatchLayoutStep2
    真正对子View做布局的地方。

    1. 计算锚点,以锚点开始填充RecyclerView(其实就是执行fill方法)。
    2. 执行fill方法,判断RecyclerView是否还有空间,如果有,执行layoutChunk方法,直至填充满。
    3. layoutChunk方法中,寻找到当前要添加的子view,add到RecyclerView中。
    4. 对子view进行measure和layout。

    dispatchLayoutStep3
    为动画保存View的相关信息; 触发动画; 相应的清理工作。
    其实dispatchLayoutStep3()就是做了一些收尾工作,将一些变量重置,处理下动画。

    mState.mLayoutStep

    • 初始化为STEP_START
    • 执行完dispatchLayoutStep1后,mState.mLayoutStep = State.STEP_LAYOUT;
    • 执行完dispatchLayoutStep2后,mState.mLayoutStep = State.STEP_ANIMATIONS;
    • 执行完dispatchLayoutStep3后,mState.mLayoutStep = State.STEP_START;
    (2)具体的onLayout回收流程

    mAttachedScrap和mChangedScrap的值是通过onLayout流程中放入缓存的ViewHolder的。这个放入的流程
    RecyclerView.onLayout->
    RecyclerView.dispatchLayout()->
    RecyclerView.dispatchLayoutStep1/2/3这三个方法->
    LinearLayoutManager.onLayoutChildren()->
    RecyclerView.LayoutManager.detachAndScrapAttachedViews->
    RecyclerView.LayoutManager.scrapOrRecycleView()->
    recycler.scrapView(view);
    在detachAndScrapAttachedViews方法回收中,还有一种可能会调用RecyclerView.Recycler.recycleViewHolderInternal回收holder到2-4级缓存中。

    private void scrapOrRecycleView(Recycler recycler, int index, View view) {
        final ViewHolder viewHolder = getChildViewHolderInt(view);
        if (viewHolder.shouldIgnore()) {
            if (DEBUG) {
                Log.d(TAG, "ignoring view " + viewHolder);
            }
            return;
        }
        if (viewHolder.isInvalid() && !viewHolder.isRemoved()
                && !mRecyclerView.mAdapter.hasStableIds()) {
            removeViewAt(index);
            // 回收到2-4级缓存中
            recycler.recycleViewHolderInternal(viewHolder);
        } else {
            detachViewAt(index);
            // 回收到一级缓存中。
            // 标记没有移除或者失效、ViewHolder没有更新,可重复使用更新ViewHolder的时候
            // 加入到mAttachedScrap中
            recycler.scrapView(view);
            mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
        }
    }
    

    如果这里的if判断,ViewHolder是无效的&&没有移除&&没有改变&&没有StateIds的话,则会调用RecyclerView.Recycler.recycleViewHolderInternal回收ViewHolder到2-4级缓存中。
    这里就是一级的回收,将回收后的数据保存在mChangedScrap或者mAttachedScrap中。
    但是并不是所有情况都会进行一级回收,只有当ViewHolder是无效的,并且没有被remove,并且adapter没有稳定的id的时候,就不会进行一级回收,而是与onTouchEvent中的回收流程一致,进行删除表项,将mCachedViews中的缓存添加到Pool中,然后向mCachedViews中添加新的缓存。
    而一级缓存只有在onLayout布局阶段才会回收然后进行添加,并且并不是所有的布局阶段都会添加一级缓存。

            void recycleViewHolderInternal(ViewHolder holder) {
                if (holder.isScrap() || holder.itemView.getParent() != null) {
                    throw new IllegalArgumentException(
                            "Scrapped or attached views may not be recycled. isScrap:"
                                    + holder.isScrap() + " isAttached:"
                                    + (holder.itemView.getParent() != null) + exceptionLabel());
                }
    
                if (holder.isTmpDetached()) {
                    throw new IllegalArgumentException("Tmp detached view should be removed "
                            + "from RecyclerView before it can be recycled: " + holder
                            + exceptionLabel());
                }
    
                if (holder.shouldIgnore()) {
                    throw new IllegalArgumentException("Trying to recycle an ignored view holder. You"
                            + " should first call stopIgnoringView(view) before calling recycle."
                            + exceptionLabel());
                }
                //noinspection unchecked
                final boolean transientStatePreventsRecycling = holder
                        .doesTransientStatePreventRecycling();
                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)) {
                        // Retire oldest cached view
                        int cachedViewSize = mCachedViews.size();
                        if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
                            // 当mCachedViews存放满了,则会mCachedViews的第0个加入到RecyclerViewPool中,并且删除第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;
                    }
                } else {
                    // NOTE: A view can fail to be recycled when it is scrolled off while an animation
                    // runs. In this case, the item is eventually recycled by
                    // ItemAnimatorRestoreListener#onAnimationFinished.
    
                    // TODO: consider cancelling an animation when an item is removed scrollBy,
                    // to return it to the pool faster
                    if (DEBUG) {
                        Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
                                + "re-visit here. We are still removing it from animation lists"
                                + exceptionLabel());
                    }
                }
                // even if the holder is not removed, we still call this method so that it is removed
                // from view holder lists.
                mViewInfoStore.removeViewHolder(holder);
                if (!cached && !recycled && transientStatePreventsRecycling) {
                    holder.mOwnerRecyclerView = null;
                }
            }
    
    四级缓存数据回收总结
    • 一级回收都是调用的Recycler.scrapView,这个方法可能是在onLayout中被调用,也可能是在复用的时候回收隐藏但是没有被remove的ViewHolder。在onLayout的过程中,是在调用detachAndScrapAttachedViews方法中,调用scrapOrRecycleView中的Recycler.scrapView进行一级缓存的回收,可能是mChangedScrap,也可能是mAttachedScrap;而在复用阶段回收,即在fill填充的时候回收到一级缓存中,则是在tryGetViewHolderForPositionByDeadline方法根据position获取ViewHolder的时候,调用getScrapOrHiddenOrCachedHolderForPosition方法中,判断ViewHolder是隐藏且没有被remove的,调用Recycler.scrapView进行一级缓存的回收,加入到mChangedScrap或者mAttachedScrap

    • 二级回收都是调用的Recycler.recycleViewHolderInternal方法,这个方法可能是在下面的情况中被调用

    RecyclerView.removeAnimatingView
    在onLayout的回收流程中LinearLayoutManager.scrapOrRecycleView
    在复用流程中tryGetViewHolderForPositionByDeadline中根据position获取的ViewHolder不可用的时候
    tryGetViewHolderForPositionByDeadline --> getScrapOrCachedViewForId -->quickRecycleScrapView
    预加载prefetchPositionWithDeadline
    removeView的时候

    recycleViewHolderInternal方法主要作用是回收ViewHolder到mCachedViews中,如果不能加入到mCachedViews中,则会将mCachedViews中的第一个加入到RecyclerViewPool中,然后将mCachedViews中的第一个出队,再将新加入的ViewHolder加入到mCachedViews中

    • 四级回收RecyclerViewPool都是调用的addViewHolderToRecycledViewPool,该方法会在回收阶段和复用阶段被调用。
      在复用阶段,即在tryGetViewHolderForPositionByDeadline中因为获取到的ViewHolder不可用,则可能会加入到ReyclcerViewPool中,一种是通过position获取ViewHolder的时候,不可用则直接调用Recycler.recycleViewHolderInternal,一种是通过ID获取ViewHolder,即getScrapOrCachedViewForId方法中,从mCachedViews中获取的ViewHolder是不可用的时候;
      在回收阶段,四级缓存的回收,最终都是调用Recycler.recycleViewHolderInternal回收四级缓存,

    参考:

    在自己写的时候,有参考这个作者写的一些关于RecyclerView的文章,这里就写出其中一篇作为入口
    其实RecyclerView的布局流程、滑动机制、缓存机制贯穿整个RecyclerView。
    RecyclerView 源码分析(一) - RecyclerView的三大流程
    https://www.jianshu.com/p/61fe3f3bb7ec
    https://phantomvk.github.io/2019/02/13/RecyclerView_cache/

    相关文章

      网友评论

          本文标题:Android-RecyclerView布局显示和回收复用流程

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