美文网首页安卓
RecyclerView 源码分析

RecyclerView 源码分析

作者: afree_ | 来源:发表于2020-03-11 00:31 被阅读0次

    本文基于 V7-25.3.1

    图文总结

    RecyclerView优点

    • 更好的灵活配置能力,在LayoutManager(布局)、Adapter(数据适配)和动画的兼容上都更优雅
    • 缓存能力增强,离屏缓存相对较优,另增加了一层缓存池缓存
    • 支持局部刷新,对于一些交互处理多的情况下,会带来更好的性能

    RecyclerView类图

    RecyclerView绘制流程图

    RecyclerView滑动流程图

    RecyclerView缓存介绍图

    源码阅读

    RecyclerView使用方法分分析

    RecyclerView 构造方法

    • 进行View相关配置属性设置,触摸范围、滑动速度等
    • 设置Item动画监听器
    • 初始化AdapterManager,创建AdapterHelper(负责Adapter里的数据集发生变化时的预处理操作)
    • 初始化ChildHelper(负责管理和访问 RecyclerView 的子视图)
    • 如果配置了LayoutManager 则通过反射方法创建它
     public RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            ...
            // View配置相关属性设置
            final ViewConfiguration vc = ViewConfiguration.get(context);
            mTouchSlop = vc.getScaledTouchSlop();
            mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
            mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
            setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
            
            // 设置Item动画监听器
            mItemAnimator.setListener(mItemAnimatorListener);
            // 设置 AdapterManager
            initAdapterManager();
            // 设置 ChildrenHelper 
            initChildrenHelper();
            // 硬件加速相关属性设置
            if (ViewCompat.getImportantForAccessibility(this)
                    == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
                ViewCompat.setImportantForAccessibility(this,
                        ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
            }
            mAccessibilityManager = (AccessibilityManager) getContext()
                    .getSystemService(Context.ACCESSIBILITY_SERVICE);
            setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
            
            // 如果attrs指定了LayoutManager,则创建LayoutManager
            boolean nestedScrollingEnabled = true;
            
            if (attrs != null) {
                int defStyleRes = 0;
                TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
                        defStyle, defStyleRes);
                String layoutManagerName = a.getString(R.styleable.RecyclerView_layoutManager);
                int descendantFocusability = a.getInt(
                        R.styleable.RecyclerView_android_descendantFocusability, -1);
                if (descendantFocusability == -1) {
                    setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
                }
                a.recycle();
                // 反射方法创建 LayoutManager
                createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes);
    
                if (Build.VERSION.SDK_INT >= 21) {
                    // SDK >=21下 ,nestedScrollingEnabled状态支持变更
                    a = context.obtainStyledAttributes(attrs, NESTED_SCROLLING_ATTRS,
                            defStyle, defStyleRes);
                    nestedScrollingEnabled = a.getBoolean(0, true);
                    a.recycle();
                }
            } else {
                setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            }
    
            // 重置nestedScrollingEnabled状态 SDK 21以下默认true
            setNestedScrollingEnabled(nestedScrollingEnabled);
        }
    
    

    setLayoutManager

    • 处理重新设置一个新的LayoutManager的一些逻辑
    • 设置this给到LayoutManager,并如果attach了则执行LayoutManger的attach分发实践
    • 跟新缓存大小,并请求重新布局
    public void setLayoutManager(LayoutManager layout) {
            if (layout == mLayout) {
                return;
            }
            stopScroll();
            // 设置新的layout情况下的一些处理逻辑
            ... 
            
            mChildHelper.removeAllViewsUnfiltered();
            mLayout = layout;
            if (layout != null) {
                // layout只能绑定一个mRecyclerView 
                if (layout.mRecyclerView != null) {
                    throw new IllegalArgumentException("LayoutManager " + layout +
                            " is already attached to a RecyclerView: " + layout.mRecyclerView);
                }
                // 设置this引用给LayoutManager
                mLayout.setRecyclerView(this);
                if (mIsAttached) {
                    // 分发attach事件
                    mLayout.dispatchAttachedToWindow(this);
                }
            }
            // 重新更新缓存大小 及请求重新布局
            mRecycler.updateViewCacheSize();
            requestLayout();
        }
    
    

    setAdapter

    • 接触frozen状态
    • 设置新的Adapter,并触发一系列监听事件
    public void setAdapter(Adapter adapter) {
       // 解除frozen状态
       setLayoutFrozen(false);
       // 替换到当前Adapter,并触发监听
       setAdapterInternal(adapter, false, true);
       requestLayout();
    }
    
    private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
                boolean removeAndRecycleViews) {
            // 旧Adapter进行解绑数据监听 和 RecyclerView的引用
            if (mAdapter != null) {
                mAdapter.unregisterAdapterDataObserver(mObserver);
                mAdapter.onDetachedFromRecyclerView(this);
            }
            if (!compatibleWithPrevious || removeAndRecycleViews) {
                removeAndRecycleViews(); // 移除缓存的View
            }
            mAdapterHelper.reset();
            final Adapter oldAdapter = mAdapter;
            mAdapter = adapter;
            if (adapter != null) {
                // 处理新设置的Adapter的关联监听和RecyclerView
                adapter.registerAdapterDataObserver(mObserver);
                adapter.onAttachedToRecyclerView(this);
            }
            // 通知LayoutManager Adapter变更
            if (mLayout != null) {
                mLayout.onAdapterChanged(oldAdapter, mAdapter);
            }
            // 触发Recycler Adapter变更事件
            mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
            // 状态置为 mStructureChanged 
            mState.mStructureChanged = true;
            markKnownViewsInvalid();
        }
    
    
    

    RecyclerView绘制方法分析

    onMeasure

    • 未赋值layoutManager情况下,走默认measure,结果是无展示
    • 系统提供的LayoutManager默认AutoMeasure。执行LayoutManger的onMeasure方法
    • 如果未指定确定宽高的尺寸规格,则会进行布局,继而获得子View的大小。此过程可能执行两次
    
    protected void onMeasure(int widthSpec, int heightSpec) {
       if (mLayout == null) {// 无LayoutManger
           defaultOnMeasure(widthSpec, heightSpec); 
           return;
       }
       // Android提供的三个LayoutManger,都是AutoMeasure
       if (mLayout.mAutoMeasure) {
           // 获取测量规格
           final int widthMode = MeasureSpec.getMode(widthSpec);
           final int heightMode = MeasureSpec.getMode(heightSpec);
           final boolean skipMeasure = widthMode == MeasureSpec.EXACTLY
                   && heightMode == MeasureSpec.EXACTLY;
           // 执行LayoutManager的onMeasure方法
           mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
           
           if (skipMeasure || mAdapter == null) {
               return;
           }
           // 如果测量规格不确定 且设置了Adapter,则先执行一次layout
           if (mState.mLayoutStep == State.STEP_START) {
               dispatchLayoutStep1();
           }
           // 设置测量规格
           mLayout.setMeasureSpecs(widthSpec, heightSpec);
           mState.mIsMeasuring = true;
           dispatchLayoutStep2();
    
           // 设置 获取到子View的宽高
           mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
    
           //二次测量,宽高不确定情况下
           if (mLayout.shouldMeasureTwice()) {
               mLayout.setMeasureSpecs(
                       MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
                       MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
               mState.mIsMeasuring = true;
               dispatchLayoutStep2();
               // now we can get the width and height from the children.
               mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
           }
       } else {
           ...
       }
    }
    
    
    

    RecyclerView.onLayout

    • 执行DispatchLayout方法
    • 根据不同State状态,分别执行Step1、Step2、Step3方法
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
       TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG);
       dispatchLayout();
       TraceCompat.endSection();
       mFirstLayoutComplete = true;
    }
        void dispatchLayout() {
            mState.mIsMeasuring = false;
            // 如果State状态是 State.STEP_START 则执行 Step1 和Step2
            if (mState.mLayoutStep == State.STEP_START) {
                dispatchLayoutStep1();
                mLayout.setExactMeasureSpecsFrom(this);
                dispatchLayoutStep2();
            } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth() 
                // 直接执行 step2
                mLayout.setExactMeasureSpecsFrom(this);
                dispatchLayoutStep2();
            } else {
                mLayout.setExactMeasureSpecsFrom(this);
            }
            // 执行Step3 ,主要保存一些View信息和动画执行
            dispatchLayoutStep3();
        }
    
    

    dispatchLayoutStep1

    • 第一步layout方法
    • 处理adapter变更
    • 确定需要执行的动画
    • 针对当前的Views进行信息缓存
    • 如有必要,则进行预布局并缓存信息
    private void dispatchLayoutStep1() {
       //  State状态断言 
       mState.assertLayoutStep(State.STEP_START);
       mState.mIsMeasuring = false;
       // 是否过滤掉 RequestLayout执行,需要过滤的时候 执行该方法,会使过滤次数+1
       eatRequestLayout();
       // 清楚 ViewInfo 所有状态和其存在的数据
       mViewInfoStore.clear();
       // 执行进入 layout或者scroll行为标志
       onEnterLayoutOrScroll();
       // 执行Adapter变更及计算那些需要执行的动画
       processAdapterUpdatesAndSetAnimationFlags();
       // 存储焦点信息
       saveFocusInfo();
       // state状态信息设置
       mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;
       mItemsAddedOrRemoved = mItemsChanged = false;
       mState.mInPreLayout = mState.mRunPredictiveAnimations;
       mState.mItemCount = mAdapter.getItemCount();
       // 寻找 layout过程中position的最大和最小值
       findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
       
       if (mState.mRunSimpleAnimations) {
           ...  
           int count = mChildHelper.getChildCount();
           for (int i = 0; i < count; ++i) {
                // 遍历VieHolder
               final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
               if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
                   continue;
               }
               // 创建 ItemHolderInfo
               final ItemHolderInfo animationInfo = mItemAnimator
                       .recordPreLayoutInformation(mState, holder,
                               ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
                               holder.getUnmodifiedPayloads());
               // mViewInfoStore存储 holder及其对应animation信息
               mViewInfoStore.addToPreLayout(holder, animationInfo);
               if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
                   // 如果holder确定要更新,就把它添加到 oldChangeHolders 集合中
                   mViewInfoStore.addToOldChangeHolders(key, holder);
               }
           }  
       }
       if (mState.mRunPredictiveAnimations) {
          ... // 运行预布局
       }
       // 执行退出 layout或者scroll行为标志
       onExitLayoutOrScroll();
       // 对应 mEatRequestLayout -1
       resumeRequestLayout(false);
       // 状态进入 State.STEP_LAYOUT
       mState.mLayoutStep = State.STEP_LAYOUT;
    }
    
    
    private void processAdapterUpdatesAndSetAnimationFlags() {
       if (predictiveItemAnimationsEnabled()) {
           mAdapterHelper.preProcess(); // 预处理
       } else {
           mAdapterHelper.consumeUpdatesInOnePass();
       }
       boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged;
       // 计算 mRunSimpleAnimations 和 mRunPredictiveAnimations
       // mDataSetHasChangedAfterLayout 数据是否变化
       mState.mRunSimpleAnimations = mFirstLayoutComplete
               && mItemAnimator != null
               && (mDataSetHasChangedAfterLayout
               || animationTypeSupported
               || mLayout.mRequestedSimpleAnimations)
               && (!mDataSetHasChangedAfterLayout
               || mAdapter.hasStableIds());
       mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations
               && animationTypeSupported
               && !mDataSetHasChangedAfterLayout
               && predictiveItemAnimationsEnabled();
    }
    
    

    dispatchLayoutStep2

    • 执行最终的View布局操作,该过程由LayoutManager完成
    • 该方法可能会被多次执行
    private void dispatchLayoutStep2() {
       // 过滤掉 RequestLayout执行,需要过滤的时候 执行该方法,会使过滤次数+1。对应resumeRequestLayout方法进行消费
       eatRequestLayout();
       // 对应 onExitLayoutOrScroll
       onEnterLayoutOrScroll();
       mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);
       // 跳过预处理过程,一次性执行完所有的update
       mAdapterHelper.consumeUpdatesInOnePass();
       mState.mItemCount = mAdapter.getItemCount(); // 赋值 itemCOunt
       mState.mDeletedInvisibleItemCountSincePreviousLayout = 0; 
    
       
       mState.mInPreLayout = false;
       // 执行 layout (执行 LayoutManager 布局)
       mLayout.onLayoutChildren(mRecycler, mState);
    
       mState.mStructureChanged = false;
       mPendingSavedState = null;
       mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
       // State状态进入 State.STEP_ANIMATIONS
       mState.mLayoutStep = State.STEP_ANIMATIONS;
       // 对应 onExitLayoutOrScroll
       onExitLayoutOrScroll();
       // 对应eatRequestLayout方法
       resumeRequestLayout(false);
    }
    

    dispatchLayoutStep3

    layout过程最后一步,执行相关动画和一些清理事项

    private void dispatchLayoutStep3() {
       ...
       if (mState.mRunSimpleAnimations) {
           // 执行相关动画
           ... 
       }
       // 一些清理动作   
    }
    
    

    draw

    主要涉及Item装饰的绘制和动画

    public void draw(Canvas c) {
       super.draw(c);
    
       final int count = mItemDecorations.size();
       for (int i = 0; i < count; i++) {
           mItemDecorations.get(i).onDrawOver(c, this, mState);
       }
      
       boolean needsInvalidate = false;
       ...
       if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0 &&
               mItemAnimator.isRunning()) {
           needsInvalidate = true;
       }
    
       if (needsInvalidate) {
           ViewCompat.postInvalidateOnAnimation(this);
       }
    }
        
    public void onDraw(Canvas c) {
       super.onDraw(c);
    
       final int count = mItemDecorations.size();
       for (int i = 0; i < count; i++) {
           mItemDecorations.get(i).onDraw(c, this, mState);
       }
    }
    
    

    LinearLayoutManager 填充子View过程

    LinearLayoutManager.onLayoutChildren

    • Child布局执行核心方法
    • 布局方式,通过确定锚点,首先以锚点为基准上到下布局,在以锚点为基准从下往上布局。如果还有空间,继续从上到下布局。最后确认整个间隙是正确的。(反向布局及横向反之则可)
    • 该方法为LayoutManager布局核心执行方法,Child的测量和添加工作在fill这个重要方法执行,接下来会阐述
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        
           // 确定是否需要反向布局
           // 确定锚点及偏移量 (1. 优先焦点child 2. 如果是反向布局,则找recycler里面最最接近尾部的child 3. 如果是正向,则找最接近头部的child)
           // 计算额外的偏移量(RecyclerView padding) 
           ...
           // 锚点准备ready        
           onAnchorReady(recycler, state, mAnchorInfo, firstLayoutDirection);
           // 临时 detach和回收当前的view 第一次 measure 的时候不会产生效果,因为此时 RecyclerView 还没有子 View。 而在第二第三次 layout 时,它会把子 View 从 RecyclerView 中 remove 或 detach ,并缓存子 View,以便之后重新 add 回来或 attach 回来,避免重复加载相同的子 View
           detachAndScrapAttachedViews(recycler); 
           mLayoutState.mInfinite = resolveIsInfinite(); 
           mLayoutState.mIsPreLayout = state.isPreLayout();
           
           // 开始填充view
           if (mAnchorInfo.mLayoutFromEnd) {
              ... // 反向填充
           } else { // 正向填充
               // (基于锚点位置先 由上到下||由左到右)更新锚点信息
               updateLayoutStateToFillEnd(mAnchorInfo);
               mLayoutState.mExtra = extraForEnd; // 额外的尾部偏移量
               // 开始填充 View布局主要方法
               fill(recycler, mLayoutState, state, false);
               // 尾部位移
               endOffset = mLayoutState.mOffset;
               final int lastElement = mLayoutState.mCurrentPosition;
               if (mLayoutState.mAvailable > 0) {
                   extraForStart += mLayoutState.mAvailable;
               }
               // (基于锚点位置 由下到上||由右到左)更新锚点信息
               updateLayoutStateToFillStart(mAnchorInfo);
               mLayoutState.mExtra = extraForStart;
               mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
               // 二次填充
               fill(recycler, mLayoutState, state, false);
               startOffset = mLayoutState.mOffset;
               // 仍有可用空间
               if (mLayoutState.mAvailable > 0) {
                   extraForEnd = mLayoutState.mAvailable;
                   // 继续 (基于锚点位置先 由上到下||由左到右)更新信息并填充View
                   updateLayoutStateToFillEnd(lastElement, endOffset);
                   mLayoutState.mExtra = extraForEnd;
                   fill(recycler, mLayoutState, state, false);
                   endOffset = mLayoutState.mOffset;
               }
           }
        
           // 有滑动位置导致的gap间隙修复处理
           ...
           // 预布局动画处理
           ...
        }
            
    
    

    LinearLayoutManager.fill

    • 如果是滑动流程,则根据情况进行回收流程
    • LayoutState中部分成员变量含义,mOffset:填充起始坐标,mCurrentPosition:填充起始数据的position,mAvailable:本次滑动可填充的距离,mScrollingOffset:滑动过的总量循环依次加载子View
    • 确定可布局大小,直至布局大小消费完成
    • 加载子View在 layoutChunk 中执行
     int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
                RecyclerView.State state, boolean stopOnFocusable) {
                
       // 可布局的位移
       final int start = layoutState.mAvailable;
       // 滑动偏移的情况下
       if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
           if (layoutState.mAvailable < 0) {
               layoutState.mScrollingOffset += layoutState.mAvailable;
           }
           // 执行回收流程
           recycleByLayoutState(recycler, layoutState);
       }
       // 余量大小
       int remainingSpace = layoutState.mAvailable + layoutState.mExtra;
       // 每次布局结果中间记录 方便运算  
       LayoutChunkResult layoutChunkResult = mLayoutChunkResult;
       while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
           layoutChunkResult.resetInternal();
           // 加载子View
           layoutChunk(recycler, state, layoutState, layoutChunkResult);
    
           if (layoutChunkResult.mFinished) {
               break;
           }
           
           layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection;
           // 计算布局使用过的大小值
           if (!layoutChunkResult.mIgnoreConsumed || mLayoutState.mScrapList != null
                   || !state.isPreLayout()) {
               layoutState.mAvailable -= layoutChunkResult.mConsumed;
               remainingSpace -= layoutChunkResult.mConsumed;
           }
            // 如果当前正在滚动屏幕
           if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
               layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
               if (layoutState.mAvailable < 0) {
                   layoutState.mScrollingOffset += layoutState.mAvailable;
               }
               // 把移出屏幕的 View 缓存到 mCachedViews 里面
               recycleByLayoutState(recycler, layoutState);
           }
           if (stopOnFocusable && layoutChunkResult.mFocusable) {
               break;
           }
       }
       
       return start - layoutState.mAvailable;
    }
    
    

    LinearLayoutManager.layoutChunk

    • 通过layoutState.next(recycler)获取目标布局View
    • 获取目标View完毕后,进行含装饰的Margin计算,并执行布局
    void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
                LayoutState layoutState, LayoutChunkResult result) {
            // 获取下一个布局View (核心方法)
            View view = layoutState.next(recycler);
            
            LayoutParams params = (LayoutParams) view.getLayoutParams();
            if (layoutState.mScrapList == null) { // 除非特殊指定,否则mScrapList为null
                // 执行 addView 
                if (mShouldReverseLayout == (layoutState.mLayoutDirection
                        == LayoutState.LAYOUT_START)) {
                        // 添加到末尾
                    addView(view); 
                } else {
                    addView(view, 0); // 添加到第一个位置
                }
            } else {
                ...      
            }
            measureChildWithMargins(view, 0, 0); // 测量子View的Margins
            // 计算 含装饰的Margin值的大小
            result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);
            int left, top, right, bottom;
            // 计算 r、l、t、b的值
            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 {
                ...
            }
            // 对View进行布局
            layoutDecoratedWithMargins(view, left, top, right, bottom);
            // 部分状态改变 
            if (params.isItemRemoved() || params.isItemChanged()) {
                result.mIgnoreConsumed = true;
            }
            result.mFocusable = view.hasFocusable();
    }
    
    

    LinearLayoutManager.next

    通过RecyclerView.Recycler获取对应Pos的View

    View next(RecyclerView.Recycler recycler) {
      if (mScrapList != null) { // 除非定制View,不然为null
          return nextViewFromScrapList();
      }
      通过RecyclerView.Recycler 获取目标position对应的View 
      final View view = recycler.getViewForPosition(mCurrentPosition);
      mCurrentPosition += mItemDirection; // 当前pos 增加
      return view;
    }
    

    Recycler获取VH的缓存和创建过程

    Recycler.getViewForPosition

    根据Pos获取View方法,最终执行tryGetViewHolderForPositionByDeadline获取View

    public View getViewForPosition(int position) {
      return getViewForPosition(position, false);
    }
    
    View getViewForPosition(int position, boolean dryRun) {
      return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
    }
    
    

    Recycler.tryGetViewHolderForPositionByDeadline

    • 获取ViewHolder方法
    • 如果是预布局,线通过ChangeScrap中获取
    • 第一次尝试获取VH,依次从Scrap、Hidden、Cache中获取VH
    • 第二次尝试获取VH,针对具有StableId的Adapter,根据id依次从Scrap和Cache获取
    • 第三次尝试从自定义缓存中获取VH
    • 第四次尝试从Recycler获取VH
    • 最后直接创建VH
    ViewHolder tryGetViewHolderForPositionByDeadline(int position,
                    boolean dryRun, long deadlineNs) {
      
      boolean fromScrapOrHiddenOrCache = false;
      ViewHolder holder = null;
      // 0) 如果是预布局, 从mChangedScrap中获取 
      if (mState.isPreLayout()) {
          holder = getChangedScrapViewForPosition(position);
          fromScrapOrHiddenOrCache = holder != null;
      }
      // 1) 第一次尝试获取,依次从Scrap、Hidden、Cache中获取VH
      if (holder == null) {
            // 依次从Scrap、Hidden、Cache中获取VH
          holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
          ... 
      }
      if (holder == null) {
          final int offsetPosition = mAdapterHelper.findPositionOffset(position);
          final int type = mAdapter.getItemViewType(offsetPosition);
          // 2) 第二次尝试获取,当Adapter具备StableIds情况
          if (mAdapter.hasStableIds()) {
              holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
                      type, dryRun);
              if (holder != null) {
                  // update position
                  holder.mPosition = offsetPosition;
                  fromScrapOrHiddenOrCache = true;
              }
          }
          // 3) 第三次尝试从 自定义缓存获取
          if (holder == null && mViewCacheExtension != null) {
                       final View view = mViewCacheExtension
                      .getViewForPositionAndType(this, position, type);
              if (view != null) {
                  holder = getChildViewHolder(view);
                  ...
              }
          }
          // 4) 第四次尝试 从 RecyclerPool中获取
          if (holder == null) { // fallback to pool
              holder = getRecycledViewPool().getRecycledView(type);
              ...
          }
          // 5) 开始创建
          if (holder == null) {
              long start = getNanoTime();
              // 创建VH
              holder = mAdapter.createViewHolder(RecyclerView.this, type);
              ... 
          }
          
           boolean bound = false;
           if (mState.isPreLayout() && holder.isBound()) {
               holder.mPreLayoutPosition = position;
           } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
                // 为bind过,执行bind方法
               final int offsetPosition = mAdapterHelper.findPositionOffset(position);
               bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
           }
    }
    
            private boolean tryBindViewHolderByDeadline(ViewHolder holder, int offsetPosition,
                    int position, long deadlineNs) {
                ...
                // 执行Adapter bindViewHolder方法
                mAdapter.bindViewHolder(holder, offsetPosition);
                ...
                return true;
            }
    

    00 从ChangeScrap获取

    针对的是预布局状态,从mChangedScrap中获取目标ViewHolder
    ScrapView:View仍然attach在其父RecyclerView上且可以被重复绑定数据及重复使用。将View标记为Scrap过程中分为两大类mAttachedScrap 和 mChangedScrap。
    mAttachedScrap:VH有ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID这两个Flag,或者VH是没有被更新过的,或者是可以被重新更新的VH。
    其它则是mChangedScrap

    ViewHolder getChangedScrapViewForPosition(int position) {
      // 必须是预布局状态,取mChangedScrap中的ViewHolder
      final int changedScrapSize;
      if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
          return null;
      }
      // 通过position获取
      for (int i = 0; i < changedScrapSize; i++) {
          final ViewHolder holder = mChangedScrap.get(i);
          if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
              holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
              return holder;
          }
      }
      // 如果Adapter是固定id,尝试从Adapter获取
      if (mAdapter.hasStableIds()) {
          final int offsetPosition = mAdapterHelper.findPositionOffset(position);
          if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
              final long id = mAdapter.getItemId(offsetPosition);
              for (int i = 0; i < changedScrapSize; i++) {
                  final ViewHolder holder = mChangedScrap.get(i);
                  if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
                      holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
                      return holder;
                  }
              }
          }
      }
      return null;
    }
    
    // Mark an attached view as scrap.
    void scrapView(View view) {
           final ViewHolder holder = getChildViewHolderInt(view);
           if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
                   || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
               holder.setScrapContainer(this, false);
               mAttachedScrap.add(holder);
           } else {
               if (mChangedScrap == null) {
                   mChangedScrap = new ArrayList<ViewHolder>();
               }
               holder.setScrapContainer(this, true);
               mChangedScrap.add(holder);
           }
    }
    
    
    

    第一次尝试获取VH(AttachScrap、Hidden、CacheView)

    • 先从 mAttachedScrap中获取VH
    • 从隐藏且未移出的View中获取 View
    • 从一级缓存CacheView中获取
    ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
           final int scrapCount = mAttachedScrap.size();
    
           // 先从 mAttachedScrap中获取VH
           for (int i = 0; i < scrapCount; i++) {
               final ViewHolder holder = mAttachedScrap.get(i);
               // 验证VH是否可用,若可用,则直接返回该VH
               if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
                       && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
                   holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
                   return holder;
               }
           }
           // dryRun 传递是false(代表VH在scrap、cache中可以被Removed)
           if (!dryRun) {
               // 从隐藏且未移出的View中获取 View
               View view = mChildHelper.findHiddenNonRemovedView(position);
               if (view != null) {
                   // View可用,则进行可视、detach、scrap缓存
                   final ViewHolder vh = getChildViewHolderInt(view);
                   mChildHelper.unhide(view);
                   int layoutIndex = mChildHelper.indexOfChild(view);
                   mChildHelper.detachViewFromParent(layoutIndex);
                   scrapView(view);
                   vh.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP
                           | ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
                   return vh;
               }
           }
    
           // 从第一级缓存View中获取
           final int cacheSize = mCachedViews.size();
           for (int i = 0; i < cacheSize; i++) {
               final ViewHolder holder = mCachedViews.get(i);
               // VH是有效的
               if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
                   if (!dryRun) {
                       mCachedViews.remove(i); // 移出获取的cache
                   }
                   return holder; // 返回VH
               }
           }
           return null;
    }
    
    

    第二次尝试获取VH(Adapter有稳定id情况)

    • Adapter配置的id是稳定的,稳定指数据集变化的时候,对于同一数据对应的id是唯一的
    • 先尝试从Scrap获取VH,非dryRun下,将未命中的从Scrap中移出,并加入到Cache或Pool缓存
    • 在尝试从Cache获取VH,将未命中的从Cache中移出,并加入到Pool缓存
    ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
           // 从AttachedScrap中 尝试获取VH
           final int count = mAttachedScrap.size();
           for (int i = count - 1; i >= 0; i--) {
               final ViewHolder holder = mAttachedScrap.get(i);
               // id 相等 且 holder 非Scrap返回
               if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
                   if (type == holder.getItemViewType()) {
                       holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
                       if (holder.isRemoved()) {
                            // 从事
                           if (!mState.isPreLayout()) {
                               holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE
                                       | ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
                           }
                       }
                       return holder;
                   } else if (!dryRun) {
                       // 从AttachedScrap移除
                       mAttachedScrap.remove(i);
                       removeDetachedView(holder.itemView, false);
                       // 回收加入至 cache 或者 pool
                       quickRecycleScrapView(holder.itemView);
                   }
               }
           }
    
           // 从CacheView中尝试获取
           final int cacheSize = mCachedViews.size();
           for (int i = cacheSize - 1; i >= 0; i--) {
               final ViewHolder holder = mCachedViews.get(i);
               if (holder.getItemId() == id) {
                   if (type == holder.getItemViewType()) {
                       if (!dryRun) {
                           mCachedViews.remove(i); // 从Cache中移出
                       }
                       return holder;
                   } else if (!dryRun) {
                       // 从Cache中移出,放到pool中
                       recycleCachedViewAt(i);
                       return null;
                   }
               }
           }
           return null;
    }
    
    
    

    RecyclerView滑动机制分析

    根据View事件机制可以直接来看onTouchEvent方法。
    重点查看move事件。move事件执行了scrollByInternal方法。该方法最后会执行LayoutManager的Scroll方法,以LinearLayoutManager为例,它的ScrollBy方法最终执行到fill方法。也就是上文提到的ItemView填充方法,滑动过程中会不断执行获取对应位置的ViewHolder,然后进行View的展示。从而实现RecyclerView的滑动

    public boolean onTouchEvent(MotionEvent e) {
       ...
    
       switch (action) {
           case MotionEvent.ACTION_DOWN: {
              ...
           case MotionEventCompat.ACTION_POINTER_DOWN: 
              ...
    
           case MotionEvent.ACTION_MOVE: { // 触摸时间-move
               ...
               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);
                   }
                   ...           }
           } break;
    
           case MotionEventCompat.ACTION_POINTER_UP: {
               onPointerUp(e);
           } break;
    
           case MotionEvent.ACTION_UP: { // 触摸事件-up
               // 执行 fling方法 ,主要做一些item和scroller动画等操作
               if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
                   setScrollState(SCROLL_STATE_IDLE);
               }
               resetTouch();
           } break;
    
           case MotionEvent.ACTION_CANCEL: {
               cancelTouch();
           } break;
       }
       ...
       return true;
    }
    
    

    scrollByInternal

    • 内部Scroll执行方法,此处会执行LayoutManager的Scroll方法
    • 其它处罚Nested、OnScroll等事件
    
    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) {
               consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
               unconsumedX = x - consumedX;
           }
           if (y != 0) {
                // LinearLayout 竖向布局为例,走LayoutManager滑动放啊放
               consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);
               unconsumedY = y - consumedY;
           }
           TraceCompat.endSection();
           repositionShadowingViews();
           onExitLayoutOrScroll();
           resumeRequestLayout(false);
       }
       if (!mItemDecorations.isEmpty()) {
           invalidate();
       }
        // 分发 NestedScroll事件
       if (dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset)) {
        ...
       }
       if (consumedX != 0 || consumedY != 0) {
           dispatchOnScrolled(consumedX, consumedY); // 分发onScrolled事件
       }
       if (!awakenScrollBars()) {
           invalidate();
       }
       return consumedX != 0 || consumedY != 0;
    }
    
    

    LinearLayoutManager执行滑动处理

    • 执行scrollBy方法
    • scrollBy方法最终走到 fill方法(上面提到的填充子View方法)
    • 该方法则会进行 ItemView的填充。从而完成Recycler滑动时,View的重新创建或者重新绑定一系列过程
    • 平移整个View的child,实现滑动效果
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
           RecyclerView.State state) {
       if (mOrientation == HORIZONTAL) {
           return 0;
       }
       return scrollBy(dy, recycler, state);
    }
    
    
    int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
       ...
       mLayoutState.mRecycle = true;
       ensureLayoutState();
       final int layoutDirection = dy > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;
       final int absDy = Math.abs(dy);
       // 更新LayoutState,布局方向和偏移值。目的是让LayoutManager知道从开始还是末尾进行回收和填充
       updateLayoutState(layoutDirection, absDy, true, state);
       
       // 执行 LinearLayout的fill 方法
       final int consumed = mLayoutState.mScrollingOffset
               + fill(recycler, mLayoutState, state, false);
       ...
       // 平移整个view的child
       mOrientationHelper.offsetChildren(-scrolled);
       
       return scrolled;
    }
    
    int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
                RecyclerView.State state, boolean stopOnFocusable) {
        ...
        // 执行回收流程
        recycleByLayoutState(recycler, layoutState);
        ...
        // 执行填充流程(参考上面layoutChunk方法)
        layoutChunk(recycler, state, layoutState,layoutChunkResult);
    }
    

    LinearLayoutManager回收流程

    • 根据不同的布局方向进行不同方向的回收。以Start为例介绍
    • 计算位移limit值,根据limit
    private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) {
       // 假设是 初始方向布局,则开始末尾View回收。反之亦然
       if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
           recycleViewsFromEnd(recycler, layoutState.mScrollingOffset);
       } else {
           recycleViewsFromStart(recycler, layoutState.mScrollingOffset);
       }
    }
    
    private void recycleViewsFromStart(RecyclerView.Recycler recycler, int dt) {
       final int limit = dt;
       final int childCount = getChildCount();
       if (mShouldReverseLayout) {
           ...
       } else {
            for (int i = 0; i < childCount; i++) {
               View child = getChildAt(i);
               // 遍历child,当超过限制大小时候,开始回收
               if (mOrientationHelper.getDecoratedEnd(child) > limit
                       || mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {
                   recycleChildren(recycler, 0, i); // 执行Children回收流程
                   return;
               }
           }        }
    }
        
    private void recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex) {
       if (endIndex > startIndex) {
           for (int i = endIndex - 1; i >= startIndex; i--) {
               removeAndRecycleViewAt(i, recycler); // 执行RecyclerView的移出和回收方法
           }
       } else {
           for (int i = startIndex; i > endIndex; i--) {
               removeAndRecycleViewAt(i, recycler);
           }
       }
    }
    
    

    RecyclerView.removeAndRecycleViewAt

    • 移出和回收View方法
    • 执行ChildHelper的移出View方法。内部Bucket移出和回掉CallBack进行View移出
    • 执行Recycler回收方法
    public void removeAndRecycleViewAt(int index, Recycler recycler) {
      final View view = getChildAt(index); // 获取目标View
      removeViewAt(index); // 执行ChildHelper移出
      recycler.recycleView(view); // 回收View
    }
    
    public void removeViewAt(int index) {
      final View child = getChildAt(index); 
      if (child != null) {
          mChildHelper.removeViewAt(index); // 执行ChildHelper移出
      }
    }
    
    public void recycleView(View view) {
      ViewHolder holder = getChildViewHolderInt(view); // 获取VH
      // ViewHolder 回收前,需要完全detach、且不是Scrap
      if (holder.isTmpDetached()) {
          removeDetachedView(view, false); 
      }
      if (holder.isScrap()) {
          holder.unScrap();
      } else if (holder.wasReturnedFromScrap()){
          holder.clearReturnedFromScrapFlag();
      }
      recycleViewHolderInternal(holder); // 执行回收
    }
    
    
    
    

    RecyclerView.recycleViewHolderInternal

    • 内部缓存VH方法
    • 如果CacheView满了,则移出一个Cache到Pool中
    • 将目标VH缓存到Cache末尾
    • 如果没有Cache成功,则直接缓存到Pool中
     void recycleViewHolderInternal(ViewHolder holder) {  
          if (mViewCacheMax > 0
                  && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
                  | ViewHolder.FLAG_REMOVED
                  | ViewHolder.FLAG_UPDATE
                  | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
              // Cache缓存个数超了,则直接回收CacheView到RecyclerPool
              int cachedViewSize = mCachedViews.size();
              if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
                  recycleCachedViewAt(0);
                  cachedViewSize--;
              }
    
              int targetCacheIndex = cachedViewSize;
              // 将VH缓存到CacheView中
              mCachedViews.add(targetCacheIndex, holder);
              cached = true;
          }
          // 如果未CacheView缓存,则直接缓存RecyclerViewPool中
          if (!cached) {
              addViewHolderToRecycledViewPool(holder, true);
              recycled = true;
          }
           ...
    }
    

    局部刷新

    Adapter数据操作对外API

    RecyclerView.Adapter提供局部数据变化通知方法,然后执行到RecyclerViewDataObserver对应的各种数据操作方法上。

    RecyclerViewDataObserver

    • 通过mAdapterHelper进行数据变化处理操作
    • 然后触发更新处理
    • 下面介绍下 ItemChanged操作
       public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
           assertNotInLayoutOrScroll(null);
           if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
               triggerUpdateProcessor();
           }
       }
    
       public void onItemRangeInserted(int positionStart, int itemCount) {
           assertNotInLayoutOrScroll(null);
           if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
               triggerUpdateProcessor();
           }
       }
    
       public void onItemRangeRemoved(int positionStart, int itemCount) {
           assertNotInLayoutOrScroll(null);
           if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
               triggerUpdateProcessor();
           }
       }
    
    

    AdapterHelper.onItemRangeChanged

    boolean onItemRangeChanged(int positionStart, int itemCount, Object payload) {
       // 添加一个更新操作 ,标志为update、记录pos、item相关信息
       mPendingUpdates.add(obtainUpdateOp(UpdateOp.UPDATE, positionStart, itemCount, payload));
       mExistingUpdateTypes |= UpdateOp.UPDATE;
       // 如果只有一个待处理操作则为true,true则执行后续更新处理。如果是多个,则会忽略,因为在第一次出发后,就会集中处理
       return mPendingUpdates.size() == 1;
    }
    
    

    RecyclerViewDataObserver.triggerUpdateProcessor

    1. 当RecyclerView有固定大小,且已经Attached了。则走Runnable更新
    2. 否则直接走requestLayout方式更新,即重新走绘制流程 onMeasure、onLayout等
    void triggerUpdateProcessor() {
      if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
          // RecyclerView有固定大小的时候 会执行mUpdateChildViewsRunnable 来处理更新
          ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
      } else {
          // 直接走 requestLayout方式来处理
          mAdapterUpdateDuringMeasure = true;
          requestLayout();
      }
    }
    
    

    triggerUpdateProcessor下requestLayout

    requestLayout下 onMeasure -> dispatchLayout -> dispatchLayoutStep2 -> layoutChildren -> fill -> layoutChunk -> next -> tryGetViewHolderForPositionByDeadline
    最终对Item进行重新绑定 实现局部刷新逻辑

    ViewHolder tryGetViewHolderForPositionByDeadline(int position,
                    boolean dryRun, long deadlineNs) {
                    
               if (mState.isPreLayout() && holder.isBound()) {
                    holder.mPreLayoutPosition = position;
                } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
                    // 执行数据变化的Holder的重新bind,从而实现局部刷新              
                    final int offsetPosition = mAdapterHelper.findPositionOffset(position);
                    bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
                }
          
                    
    }
    
    

    triggerUpdateProcessor下mUpdateChildViewsRunnable

    当RecyclerView有固定大小时,则不需要Measure,直接走dispatchLayout方法进行刷新操作

    final Runnable mUpdateChildViewsRunnable = new Runnable() {
       @Override
       public void run() {
            ...
            // 消费 等待执行的操作 
            consumePendingUpdateOperations();
       }
    }
    
    void consumePendingUpdateOperations() {
            
          if (mAdapterHelper.hasAnyUpdateTypes(AdapterHelper.UpdateOp.UPDATE) && !mAdapterHelper
               .hasAnyUpdateTypes(AdapterHelper.UpdateOp.ADD | AdapterHelper.UpdateOp.REMOVE
                       | AdapterHelper.UpdateOp.MOVE)) {
            // update 情况下 逻辑
           
           eatRequestLayout();
           onEnterLayoutOrScroll();
           // 数据预处理 
           mAdapterHelper.preProcess();
           if (!mLayoutRequestEaten) {
                // 执行 dispatchLayout 进行局部刷新处理
               if (hasUpdatedView()) {
                   dispatchLayout();
               } else {
                   // no need to layout, clean state
                   mAdapterHelper.consumePostponedUpdates();
               }
           }
           ...
       } else if (mAdapterHelper.hasPendingUpdates()) {
           // add、remove等操作,直接执行dispatchLayout
           dispatchLayout();
           TraceCompat.endSection();
       }
    }
    

    相关文章

      网友评论

        本文标题:RecyclerView 源码分析

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