0%

RecyclerView 源码解析流程

主要方法

构造函数

  • 进行View相关配置属性设置,触摸范围、滑动速度等
  • 设置Item动画监听器
  • 初始化AdapterManager,创建AdapterHelper(负责Adapter里的数据集发生变化时的预处理操作)
  • 初始化ChildHelper(负责管理和访问 RecyclerView 的子视图)
  • 如果配置了LayoutManager 则通过反射方法创建它
    1
    public RecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        //设置为滚动容器        setScrollContainer(true);        setFocusableInTouchMode(true);        //View配置相关属性设置        final ViewConfiguration vc = ViewConfiguration.get(context);        mTouchSlop = vc.getScaledTouchSlop();        mScaledHorizontalScrollFactor =                ViewConfigurationCompat.getScaledHorizontalScrollFactor(vc, context);        mScaledVerticalScrollFactor =                ViewConfigurationCompat.getScaledVerticalScrollFactor(vc, context);        mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();        mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();        setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);        // 设置Item动画监听器        mItemAnimator.setListener(mItemAnimatorListener);        // 设置 AdapterManager        initAdapterManager();        // 设置 ChildrenHelper         initChildrenHelper();        initAutofill();        // If not explicitly specified this view is important for accessibility.        // 硬件加速相关属性设置        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        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,                defStyleAttr, 0);        if (Build.VERSION.SDK_INT >= 29) {            saveAttributeDataForStyleable(context, R.styleable.RecyclerView, attrs, a,                    defStyleAttr, 0);        }        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);        }        mClipToPadding = a.getBoolean(R.styleable.RecyclerView_android_clipToPadding, true);        mEnableFastScroller = a.getBoolean(R.styleable.RecyclerView_fastScrollEnabled, false);        if (mEnableFastScroller) {            StateListDrawable verticalThumbDrawable = (StateListDrawable) a                    .getDrawable(R.styleable.RecyclerView_fastScrollVerticalThumbDrawable);            Drawable verticalTrackDrawable = a                    .getDrawable(R.styleable.RecyclerView_fastScrollVerticalTrackDrawable);            StateListDrawable horizontalThumbDrawable = (StateListDrawable) a                    .getDrawable(R.styleable.RecyclerView_fastScrollHorizontalThumbDrawable);            Drawable horizontalTrackDrawable = a                    .getDrawable(R.styleable.RecyclerView_fastScrollHorizontalTrackDrawable);            initFastScroller(verticalThumbDrawable, verticalTrackDrawable,                    horizontalThumbDrawable, horizontalTrackDrawable);        }        a.recycle();        // 反射方法创建 LayoutManager        // Create the layoutManager if specified.        createLayoutManager(context, layoutManagerName, attrs, defStyleAttr, 0);        boolean nestedScrollingEnabled = true;        if (Build.VERSION.SDK_INT >= 21) {            // SDK >=21下 ,nestedScrollingEnabled状态支持变更            a = context.obtainStyledAttributes(attrs, NESTED_SCROLLING_ATTRS,                    defStyleAttr, 0);            if (Build.VERSION.SDK_INT >= 29) {                saveAttributeDataForStyleable(                        context, NESTED_SCROLLING_ATTRS, attrs, a, defStyleAttr, 0);            }            nestedScrollingEnabled = a.getBoolean(0, true);            a.recycle();        }        // 重置nestedScrollingEnabled状态 SDK 21以下默认true        setNestedScrollingEnabled(nestedScrollingEnabled);    }

setLayoutManager

  • 处理重新设置一个新的LayoutManager的一些逻辑
  • 设置this给到LayoutManager,并如果attach了则执行LayoutManger的attach分发实践
  • 更新缓存大小,并请求重新布局
1
public void setLayoutManager(@Nullable LayoutManager layout) {        //过滤LayoutManager        if (layout == mLayout) {            return;        }        //停止滚动        stopScroll();                //mLayout有值,则进行mLayout的解除关联、销毁操作        if (mLayout != null) {            // end all running animations            if (mItemAnimator != null) {                mItemAnimator.endAnimations();            }            mLayout.removeAndRecycleAllViews(mRecycler);            mLayout.removeAndRecycleScrapInt(mRecycler);            mRecycler.clear();            if (mIsAttached) {                mLayout.dispatchDetachedFromWindow(this, mRecycler);            }            mLayout.setRecyclerView(null);            mLayout = null;        } else {            mRecycler.clear();        }        // 对有缺陷的item animator一个防御措施        mChildHelper.removeAllViewsUnfiltered();        // 重新赋值        mLayout = layout;        if (layout != null) {            //如果layout已经关联一个Recyclerview对象,则抛出异常            if (layout.mRecyclerView != null) {                throw new IllegalArgumentException("LayoutManager " + layout                        + " is already attached to a RecyclerView:"                        + layout.mRecyclerView.exceptionLabel());            }            //LayoutManager关联当前RecyclerView            mLayout.setRecyclerView(this);            if (mIsAttached) {                mLayout.dispatchAttachedToWindow(this);            }        }                //重置Recycler的mCachedViews中的ViewHolder,并把其加入RecycledViewPool        mRecycler.updateViewCacheSize();        //请求刷新Layout        requestLayout();    }

setAdapter

  • 解除frozen状态
  • 设置新的Adapter,并触发一系列监听事件
1
public void setAdapter(@Nullable Adapter adapter) {        // bail out if layout is frozen        setLayoutFrozen(false);        setAdapterInternal(adapter, false, true);        processDataSetCompletelyChanged(false);        requestLayout();    }

setAdapter和swapAdapter实现方法相同,传的参数不同。 看看setAdapterInternal方法的传参

  • compatibleWithPrevious: 设置为true则表示新的Adapter和老的Adapter使用相同的ViewHolder和itemType(可以避免缓存失效)
  • removeAndRecycleViews: 如果为true,将会删除并回收所有现有的视图。如果compatibleWithPrevious为false,则忽略此参数。
1
private void setAdapterInternal(@Nullable Adapter adapter, boolean compatibleWithPrevious,boolean removeAndRecycleViews) {        //如果原先Adapter不为null,则解除关联        if (mAdapter != null) {            mAdapter.unregisterAdapterDataObserver(mObserver);            mAdapter.onDetachedFromRecyclerView(this);        }        //compatibleWithPrevious为false或者removeAndRecycleViewstruetrue则移除所有View        if (!compatibleWithPrevious || removeAndRecycleViews) {            removeAndRecycleViews();        }        //AdapterHelper类reset        mAdapterHelper.reset();        //重新赋值新的Adapter给mAdapter并建立关联        final Adapter oldAdapter = mAdapter;        mAdapter = adapter;        if (adapter != null) {            adapter.registerAdapterDataObserver(mObserver);            adapter.onAttachedToRecyclerView(this);        }        //LayoutManager进行Adapter更换        if (mLayout != null) {            mLayout.onAdapterChanged(oldAdapter, mAdapter);        }        //Recycler进行Adapter更换,并传入compatibleWithPrevious        mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);        mState.mStructureChanged = true;    }

processDataSetCompletelyChanged方法 -> markKnownViewsInvalid(将所有已知ViewHolder标志为无效) -> markItemDecorInsetsDirty(将当前的ChildView以及Recycler中的ChildView的mInsetsDirty设置为true)、mRecycler.markKnownViewsInvalid(从Recycler的mCachedViews中的ViewHolder标志位无效) -> recycleAndClearCachedViews(从mCachedViews中的ViewHolder移除添加进RecycledViewPool)

扩展方法

addOnChildAttachStateChangeListener、removeOnChildAttachStateChangeListener、clearOnChildAttachStateChangeListeners

监听子View的添加和释放,官方推荐使用过重的View资源可以在这个监听器里进行释放

1
public interface OnChildAttachStateChangeListener {        void onChildViewAttachedToWindow(@NonNull View view);        void onChildViewDetachedFromWindow(@NonNull View view);    }

setOnFlingListener、getOnFlingListener

监听RecyclerView的快速滑动的事件,可以获取横向滑动或者纵向滑动的速度,并且可以进行拦截处理。

1
public abstract static class OnFlingListener {        public abstract boolean onFling(int velocityX, int velocityY);    }

setRecycledViewPool(@Nullable RecycledViewPool pool)、getRecycledViewPool

如果有多个相同数据类型的Adapter,可以设置RecycledViewPool共享池。

setViewCacheExtension(@Nullable ViewCacheExtension extension)

自定义ViewCacheExtension

setItemViewCacheSize(int size)

设置加入RecycledViewPool之前可缓存的数量

addItemDecoration(@NonNull ItemDecoration decor, int index)、addItemDecoration(@NonNull ItemDecoration decor)、getItemDecorationAt(int index)、getItemDecorationCount()、removeItemDecorationAt(int index)、removeItemDecoration(@NonNull ItemDecoration decor)

添加ItemDecoration, ItemDecoration有层级关系, index值会影响ItemDecoration所在层级。index为-1,则添加到最后。

setChildDrawingOrderCallback(@Nullable ChildDrawingOrderCallback childDrawingOrderCallback)

可用来更改RecyclerView子项的绘制顺序

1
public interface ChildDrawingOrderCallback {        int onGetChildDrawingOrder(int childCount, int i);    }

addOnScrollListener(@NonNull OnScrollListener listener)、removeOnScrollListener(@NonNull OnScrollListener listener)、clearOnScrollListeners()

用来监听RecyclerView的滚动状态和滚动距离

1
public abstract static class OnScrollListener {                public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState){}        public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){}    }

scrollToPosition(int position)

滚动到指定位置

smoothScrollToPosition(int position)

带动画滚动到指定位置

setEdgeEffectFactory(@NonNull EdgeEffectFactory edgeEffectFactory)、getEdgeEffectFactory()

自定义边界UI

addOnItemTouchListener(@NonNull OnItemTouchListener listener)、removeOnItemTouchListener(@NonNull OnItemTouchListener listener)

处理Item触摸事件

1
public interface OnItemTouchListener {            boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);        void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);        void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);    }

View绘制三大流程

measure

1
@Override    protected void onMeasure(int widthSpec, int heightSpec) {        //第一种情况当前LayoutManager为null        if (mLayout == null) {            defaultOnMeasure(widthSpec, heightSpec);            return;        }        //第二种情况LayoutManager开启了自动测量        if (mLayout.isAutoMeasureEnabled()) {            final int widthMode = MeasureSpec.getMode(widthSpec);            final int heightMode = MeasureSpec.getMode(heightSpec);            //调用LayoutManager的onMeasure方法进行测量。            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);            final boolean measureSpecModeIsExactly =                    widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY;            if (measureSpecModeIsExactly || mAdapter == null) {                return;            }            //mLayoutStep为State.STEP_START执行dispatchLayoutStep1            if (mState.mLayoutStep == State.STEP_START) {                dispatchLayoutStep1();            }                        mLayout.setMeasureSpecs(widthSpec, heightSpec);            mState.mIsMeasuring = true;            //执行dispatchLayoutStep2            dispatchLayoutStep2();            // now we can get the width and height from the children.            mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);            // if RecyclerView has non-exact width and height and if there is at least one child            // which also has non-exact width & height, we have to re-measure.            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 {            //第三种情况 LayoutManager没有开启自动测量            if (mHasFixedSize) {                mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);                return;            }            // custom onMeasure            if (mAdapterUpdateDuringMeasure) {                startInterceptRequestLayout();                onEnterLayoutOrScroll();                processAdapterUpdatesAndSetAnimationFlags();                onExitLayoutOrScroll();                if (mState.mRunPredictiveAnimations) {                    mState.mInPreLayout = true;                } else {                    // consume remaining updates to provide a consistent state with the layout pass.                    mAdapterHelper.consumeUpdatesInOnePass();                    mState.mInPreLayout = false;                }                mAdapterUpdateDuringMeasure = false;                stopInterceptRequestLayout(false);            } else if (mState.mRunPredictiveAnimations) {                // If mAdapterUpdateDuringMeasure is false and mRunPredictiveAnimations is true:                // this means there is already an onMeasure() call performed to handle the pending                // adapter change, two onMeasure() calls can happen if RV is a child of LinearLayout                // with layout_width=MATCH_PARENT. RV cannot call LM.onMeasure() second time                // because getViewForPosition() will crash when LM uses a child to measure.                setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight());                return;            }            if (mAdapter != null) {                mState.mItemCount = mAdapter.getItemCount();            } else {                mState.mItemCount = 0;            }            startInterceptRequestLayout();            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);            stopInterceptRequestLayout(false);            mState.mInPreLayout = false; // clear        }    }

第一种情况

直接调用defaultOnMeasure方法

1
void defaultOnMeasure(int widthSpec, int heightSpec) {        // calling LayoutManager here is not pretty but that API is already public and it is better        // than creating another method since this is internal.        final int width = LayoutManager.chooseSize(widthSpec,                getPaddingLeft() + getPaddingRight(),                ViewCompat.getMinimumWidth(this));        final int height = LayoutManager.chooseSize(heightSpec,                getPaddingTop() + getPaddingBottom(),                ViewCompat.getMinimumHeight(this));        setMeasuredDimension(width, height);    }

直接调用LayoutManager.chooseSize来获取宽高,然后直接setMeasuredDimension

1
//通过RecyclerView的测量mode来获取不同的值        public static int chooseSize(int spec, int desired, int min) {            final int mode = View.MeasureSpec.getMode(spec);            final int size = View.MeasureSpec.getSize(spec);            switch (mode) {                case View.MeasureSpec.EXACTLY:                    return size;                case View.MeasureSpec.AT_MOST:                    return Math.min(size, Math.max(desired, min));                case View.MeasureSpec.UNSPECIFIED:                default:                    return Math.max(desired, min);            }        }

第二种情况 当LayoutManager开启了自动测量

Untitled

dispatchLayoutStep1

  • 第一步layout方法
  • 处理adapter变更
  • 确定需要执行的动画
  • 针对当前的Views进行信息缓存
  • 如有必要,则进行预布局并缓存信息
1
private void dispatchLayoutStep1() {        mState.assertLayoutStep(State.STEP_START);        fillRemainingScrollValues(mState);        mState.mIsMeasuring = false;        startInterceptRequestLayout();        mViewInfoStore.clear();        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            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) {            // 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;    }
1
private void processAdapterUpdatesAndSetAnimationFlags() {        if (mDataSetHasChangedAfterLayout) {            // Processing these items have no value since data set changed unexpectedly.            // Instead, we just reset it.            mAdapterHelper.reset();            if (mDispatchItemsChangedEvent) {                mLayout.onItemsChanged(this);            }        }        // simple animations are a subset of advanced animations (which will cause a        // pre-layout step)        // If layout supports predictive animations, pre-process to decide if we want to run them        if (predictiveItemAnimationsEnabled()) {            mAdapterHelper.preProcess();        } else {            mAdapterHelper.consumeUpdatesInOnePass();        }        boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged;        //判断是否是第一次加载布局        mState.mRunSimpleAnimations = mFirstLayoutComplete                && mItemAnimator != null                && (mDataSetHasChangedAfterLayout                || animationTypeSupported                || mLayout.mRequestedSimpleAnimations)                && (!mDataSetHasChangedAfterLayout                || mAdapter.hasStableIds());        mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations                && animationTypeSupported                && !mDataSetHasChangedAfterLayout                && predictiveItemAnimationsEnabled();    }

dispatchLayoutStep2

1
private void dispatchLayoutStep2() {        startInterceptRequestLayout();        onEnterLayoutOrScroll();        mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);        mAdapterHelper.consumeUpdatesInOnePass();        mState.mItemCount = mAdapter.getItemCount();        mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;        mState.mInPreLayout = false;        //调用LayoutManager的onLayoutChildren对children进行测量和布局        mLayout.onLayoutChildren(mRecycler, mState);        mState.mStructureChanged = false;        mPendingSavedState = null;        // onLayoutChildren may have caused client code to disable item animations; re-check        mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;        //设置mLayoutStep为State.STEP_ANIMATIONS        mState.mLayoutStep = State.STEP_ANIMATIONS;        onExitLayoutOrScroll();        stopInterceptRequestLayout(false);    }

没有开启自动测量

  • 如果mHasFixedSize为true(也就是调用了setHasFixedSize方法),将直接调用LayoutManager的onMeasure方法进行测量。
  • 如果mHasFixedSize为false,同时此时如果有数据更新,先处理数据更新的事务,然后调用LayoutManager的onMeasure方法进行测量

layout

1
@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;    }

dispatchLayout

这个方法保证RecyclerView必须经历三个过程–dispatchLayoutStep1、dispatchLayoutStep2、dispatchLayoutStep3。

1
void dispatchLayout() {        //这边如果mAdapter为null,就不进行展示了        if (mAdapter == null) {            Log.e(TAG, "No adapter attached; skipping layout");            // leave the state in START            return;        }        //这边如果mLayout为null,就不进行展示了        if (mLayout == null) {            Log.e(TAG, "No layout manager attached; skipping layout");            // leave the state in START            return;        }        //更改mState.mIsMeasuring        mState.mIsMeasuring = false;        //如果mState.mLayoutStep为State.STEP_START再次调用dispatchLayoutStep1和dispatchLayoutStep2        if (mState.mLayoutStep == State.STEP_START) {            dispatchLayoutStep1();            mLayout.setExactMeasureSpecsFrom(this);            dispatchLayoutStep2();        } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth()                || mLayout.getHeight() != getHeight()) {            // First 2 steps are done in onMeasure but looks like we have to run again due to            // changed size.            mLayout.setExactMeasureSpecsFrom(this);            dispatchLayoutStep2();        } else {            // always make sure we sync them (to ensure mode is exact)            mLayout.setExactMeasureSpecsFrom(this);        }        //执行dispatchLayoutStep3        dispatchLayoutStep3();    }

dispatchLayoutStep3

这是最后一步

1
private void dispatchLayoutStep3() {        mState.assertLayoutStep(State.STEP_ANIMATIONS);        startInterceptRequestLayout();        onEnterLayoutOrScroll();        //重新将mState.mLayoutStep的值赋值为State.STEP_START,保证下次dispatchLayout继续走3步        mState.mLayoutStep = State.STEP_START;        if (mState.mRunSimpleAnimations) {            // Step 3: Find out where things are now, and process change animations.            // traverse list in reverse because we may call animateChange in the loop which may            // remove the target view holder.            for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {                ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));                if (holder.shouldIgnore()) {                    continue;                }                long key = getChangedHolderKey(holder);                final ItemHolderInfo animationInfo = mItemAnimator                        .recordPostLayoutInformation(mState, holder);                ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);                if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {                    // run a change animation                    // If an Item is CHANGED but the updated version is disappearing, it creates                    // a conflicting case.                    // Since a view that is marked as disappearing is likely to be going out of                    // bounds, we run a change animation. Both views will be cleaned automatically                    // once their animations finish.                    // On the other hand, if it is the same view holder instance, we run a                    // disappearing animation instead because we are not going to rebind the updated                    // VH unless it is enforced by the layout manager.                    final boolean oldDisappearing = mViewInfoStore.isDisappearing(                            oldChangeViewHolder);                    final boolean newDisappearing = mViewInfoStore.isDisappearing(holder);                    if (oldDisappearing && oldChangeViewHolder == holder) {                        // run disappear animation instead of change                        mViewInfoStore.addToPostLayout(holder, animationInfo);                    } else {                        //ItemHolderInfo中保存ItemView的位置信息                        final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout(                                oldChangeViewHolder);                        // we add and remove so that any post info is merged.                        mViewInfoStore.addToPostLayout(holder, animationInfo);                        ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder);                        if (preInfo == null) {                            handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder);                        } else {                            animateChange(oldChangeViewHolder, holder, preInfo, postInfo,                                    oldDisappearing, newDisappearing);                        }                    }                } else {                    mViewInfoStore.addToPostLayout(holder, animationInfo);                }            }            // 执行动画            // Step 4: Process view info lists and trigger animations            mViewInfoStore.process(mViewInfoProcessCallback);        }        mLayout.removeAndRecycleScrapInt(mRecycler);        mState.mPreviousLayoutItemCount = mState.mItemCount;        mDataSetHasChangedAfterLayout = false;        mDispatchItemsChangedEvent = false;        mState.mRunSimpleAnimations = false;        mState.mRunPredictiveAnimations = false;        mLayout.mRequestedSimpleAnimations = false;        if (mRecycler.mChangedScrap != null) {            mRecycler.mChangedScrap.clear();        }        if (mLayout.mPrefetchMaxObservedInInitialPrefetch) {            // Initial prefetch has expanded cache, so reset until next prefetch.            // This prevents initial prefetches from expanding the cache permanently.            mLayout.mPrefetchMaxCountObserved = 0;            mLayout.mPrefetchMaxObservedInInitialPrefetch = false;            mRecycler.updateViewCacheSize();        }        mLayout.onLayoutCompleted(mState);        onExitLayoutOrScroll();        stopInterceptRequestLayout(false);        mViewInfoStore.clear();        if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) {            dispatchOnScrolled(0, 0);        }        recoverFocusFromState();        resetFocusInfo();    }

RecyclerView跟其他ViewGroup不同的地方在于,如果开启了自动测量,在measure阶段,已经将Children布局完成了;如果没有开启自动测量,则在layout阶段才布局Children

在LayoutManager中的绘制

LinearLayoutManager#onLayoutChildren

1
@Override    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {        // ...        // 找到锚点(具体过程等到分析 layout 时再说)        // (1)        detachAndScrapAttachedViews(recycler);                if (mAnchorInfo.mLayoutFromEnd) {            // ...        } else {            // (2)            fill(recycler, mLayoutState, state, false);            // ...        }           // ...    }

首先看(1)处,detachAndScrapAttachedViews 方法会根据情况将子 View 回收到相应缓存,具体过程之后再看,由于现在是第一次 layout,RecyclerView 中没有子 View,所以现在该方法没啥用。

接下来看(2)处,这里的 fill 方法比较重要,它的作用是填充布局。看一下该方法

LinearLayoutManager#fill

1
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,            RecyclerView.State state, boolean stopOnFocusable) {        // 进行 layout 时 layoutState.mScrollingOffset 的值被设置为        // LayoutState.SCROLLING_OFFSET_NaN,不会进入此 if 块        if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {            // ...            recycleByLayoutState(recycler, layoutState);        }                // 需要填充的空间        int remainingSpace = layoutState.mAvailable + layoutState.mExtra;        // 还有需要填充的空间并且 item 数未满        while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {            // ...                        // (1)            layoutChunk(recycler, state, layoutState, layoutChunkResult);            // 计算剩余空间            // 同上,在 layout 时不会进入 if 块中            if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {                // ...                recycleByLayoutState(recycler, layoutState);            }                        // ...        }    }

LinearLayoutManager#layoutChunk

1
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,            LayoutState layoutState, LayoutChunkResult result) {        // (1)        View view = layoutState.next(recycler);        // ...                // 默认情况下,layoutState.mScrapList 等于 null        if (layoutState.mScrapList == null) {            if (mShouldReverseLayout == (layoutState.mLayoutDirection                    == LayoutState.LAYOUT_START)) {                // (2)                addView(view);            } else {                addView(view, 0);            }        } else {            // ...        }    }

(2)处的 addView 方法就不多说了,该方法将得到的子 View 添加到 RecyclerView 中。主要看(1)处,看看子 View 从何而来

1
View next(RecyclerView.Recycler recycler) {        // ...                final View view = recycler.getViewForPosition(mCurrentPosition);        return view;    }

这个方法是不是很熟悉呢?没错,它就是之前分析的 Recycler 的 getViewForPosition 方法。

不过由于现在没有任何缓存,所以第一次 layout 的时候是通过 Adapter 的 createViewHolder 来创建子 View的,并且没有添加任何缓存。

draw

  • 调用super.draw方法。这里主要做了两件事:
    • 将Children的绘制分发给ViewGroup;
    • 将分割线的绘制分发给ItemDecoration。
  • 如果需要的话,调用ItemDecoration的onDrawOver方法。通过这个方法,我们在每个ItemView上面画上很多东西。
  • 如果RecyclerView调用了setClipToPadding,会实现一种特殊的滑动效果–每个ItemView可以滑动到padding区域。
1
@Override    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);        }        // 第三步        // TODO If padding is not 0 and clipChildrenToPadding is false, to draw glows properly, we        // need find children closest to edges. Not sure if it is worth the effort.        boolean needsInvalidate = false;        if (mLeftGlow != null && !mLeftGlow.isFinished()) {            final int restore = c.save();            final int padding = mClipToPadding ? getPaddingBottom() : 0;            c.rotate(270);            c.translate(-getHeight() + padding, 0);            needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c);            c.restoreToCount(restore);        }        if (mTopGlow != null && !mTopGlow.isFinished()) {            final int restore = c.save();            if (mClipToPadding) {                c.translate(getPaddingLeft(), getPaddingTop());            }            needsInvalidate |= mTopGlow != null && mTopGlow.draw(c);            c.restoreToCount(restore);        }        if (mRightGlow != null && !mRightGlow.isFinished()) {            final int restore = c.save();            final int width = getWidth();            final int padding = mClipToPadding ? getPaddingTop() : 0;            c.rotate(90);            c.translate(-padding, -width);            needsInvalidate |= mRightGlow != null && mRightGlow.draw(c);            c.restoreToCount(restore);        }        if (mBottomGlow != null && !mBottomGlow.isFinished()) {            final int restore = c.save();            c.rotate(180);            if (mClipToPadding) {                c.translate(-getWidth() + getPaddingRight(), -getHeight() + getPaddingBottom());            } else {                c.translate(-getWidth(), -getHeight());            }            needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c);            c.restoreToCount(restore);        }        // If some views are animating, ItemDecorators are likely to move/change with them.        // Invalidate RecyclerView to re-draw decorators. This is still efficient because children's        // display lists are not invalidated.        if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0                && mItemAnimator.isRunning()) {            needsInvalidate = true;        }        if (needsInvalidate) {            ViewCompat.postInvalidateOnAnimation(this);        }    }

关于Children的绘制和ItemDecoration的绘制,是在onDraw方法里面

1
@Override    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);        }    }

缓存机制

四级缓存

Untitled

ViewHolder的几个状态值

Untitled

mChangedScrap和mAttachedScrap的区别

首先,如果调用了Adapter的notifyItemChanged方法,会重新回调到LayoutManager的onLayoutChildren方法里面,而在onLayoutChildren方法里面,会将屏幕上所有的ViewHolder回收到mAttachedScrap和mChangedScrap。这个过程就是将ViewHolder分别放到mAttachedScrap和mChangedScrap,而什么条件下放在mAttachedScrap,什么条件放在mChangedScrap,这个就是他们俩的区别。

1
void scrapView(View view) {            final ViewHolder holder = getChildViewHolderInt(view);            //1.被同时标记为remove或invalid;2.完全没有改变的ViewHolder。这里还有第三个判断,这个跟RecyclerView的ItemAnimator有关,如果ItemAnimator为空或者ItemAnimator的canReuseUpdatedViewHolder方法为true,也会放入到mAttachedScrap。            if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)                    || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {                if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {                    throw new IllegalArgumentException("Called scrap view with an invalid view."                            + " Invalid views cannot be reused from scrap, they should rebound from"                            + " recycler pool." + exceptionLabel());                }                holder.setScrapContainer(this, false);                mAttachedScrap.add(holder);            } else {                //ViewHolder的isUpdated方法返回为true时,会放入到mChangedScrap里面去。                if (mChangedScrap == null) {                    mChangedScrap = new ArrayList<ViewHolder>();                }                holder.setScrapContainer(this, true);                mChangedScrap.add(holder);            }        }

复用

RecyclerView对ViewHolder的复用,我们得从LayoutState的next方法开始。LayoutManager在布局itemView时,需要获取一个ViewHolder对象,就是通过这个方法来获取,具体的复用逻辑也是在这个方面开始调用的。

1
View next(RecyclerView.Recycler recycler) {        final View view = recycler.getViewForPosition(mCurrentPosition);        mCurrentPosition += mItemDirection;        return view;    }

再来看Recycler的getViewForPosition

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

最后走到tryGetViewHolderForPositionByDeadline这个方法,RecyclerView真正复用的核心就在这个方法

1
@Nullable        ViewHolder tryGetViewHolderForPositionByDeadline(int position,                boolean dryRun, long deadlineNs) {            //position和mState.getItemCount对不上就会抛出异常            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里面去获取ViewHolder            if (mState.isPreLayout()) {                holder = getChangedScrapViewForPosition(position);                fromScrapOrHiddenOrCache = holder != null;            }            // 1) Find by position from scrap/hidden list/cache            //如果holder为null,分别从mAttachedScrap、 mHiddenViews、mCachedViews获取ViewHolder            if (holder == null) {                holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);                if (holder != null) {                    //检查ViewHolder是否有效                    if (!validateViewHolderForOffsetPosition(holder)) {                        // recycle holder (and unscrap if relevant) since it can't be used                        //做一些清理操作,然后重新放入到缓存里面                        if (!dryRun) {                            // we would like to recycle this but need to make sure it is not used by                            // animation logic etc.                            holder.addFlags(ViewHolder.FLAG_INVALID);                            if (holder.isScrap()) {                                removeDetachedView(holder.itemView, false);                                holder.unScrap();                            } else if (holder.wasReturnedFromScrap()) {                                holder.clearReturnedFromScrapFlag();                            }                            //做回收操作                            recycleViewHolderInternal(holder);                        }                        holder = null;                    } else {                        fromScrapOrHiddenOrCache = true;                    }                }            }                        //1. 如果Adapter的hasStableIds方法返回为true,优先通过ViewType和id两个条件来寻找。如果没有找到,那么就进行第2步。            //2. 如果Adapter的hasStableIds方法返回为false,在这种情况下,首先会在ViewCacheExtension里面找,如果还没有找到的话,最后会在RecyclerViewPool里面来获取ViewHolder。            //3. 如果以上的复用步骤都没有找到合适的ViewHolder,最后就会调用Adapter的onCreateViewHolder方法来创建一个新的ViewHolder。            if (holder == null) {                final int offsetPosition = mAdapterHelper.findPositionOffset(position);                //校验offsetPosition                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                //通过ViewType和Id来查找                if (mAdapter.hasStableIds()) {                    //分别从mAttachedScrap和mCachedViews数组寻找合适的ViewHolder                    holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),                            type, dryRun);                    if (holder != null) {                        // update position                        holder.mPosition = offsetPosition;                        fromScrapOrHiddenOrCache = true;                    }                }                //如果存在ViewCacheExtension,则从ViewCacheExtension中查找                这个玩意需要用户自定义,很少使用                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());                        }                    }                }                //还没找到再到RecycledViewPool中进行查找                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);                        }                    }                }                //实在没有只能通过createViewHolder进行创建                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);                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;        }

从RecyclerViewPool里面获取ViewHolder

在RecyclerViewPool的内部,使用SparseArray来存储每个ViewType对应的ViewHolder数组,其中每个数组的最大size为5。

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

回收

scrap数组

关于ViewHolder回收到scrap数组里面,其实我在前面已经简单的分析了,重点就在于Recycler的scrapView方法里面。我们来看看scrapView在哪里被调用了。有如下两个地方:

  • 在getScrapOrHiddenOrCachedHolderForPosition方法里面,如果从mHiddenViews获得一个ViewHolder的话,会先将这个ViewHolder从mHiddenViews数组里面移除,然后调用Recycler的scrapView方法将这个ViewHolder放入到scrap数组里面,并且标记FLAG_RETURNED_FROM_SCRAP和FLAG_BOUNCED_FROM_HIDDEN_LIST两个flag。
  • 在LayoutManager里面的scrapOrRecycleView方法也会调用Recycler的scrapView方法。而有两种情形下会出现如此情况:1. 手动调用了LayoutManager相关的方法;2. RecyclerView进行了一次布局(调用了requestLayout方法)

mCacheViews数组

mCacheViews数组作为二级缓存,回收的路径相较于一级缓存要多。关于mCacheViews数组,重点在于Recycler的recycleViewHolderInternal方法里面。 #### mHiddenViews数组 一个ViewHolder回收到mHiddenView数组里面的条件比较简单,如果当前操作支持动画,就会调用到RecyclerView的addAnimatingView方法,在这个方法里面会将做动画的那个View添加到mHiddenView数组里面去。通常就是动画期间可以会进行复用,因为mHiddenViews只在动画期间才会有元素。

RecyclerViewPool

RecyclerViewPool跟mCacheViews,都是通过recycleViewHolderInternal方法来进行回收,所以情景与mCacheViews差不多,只不过当不满足放入mCacheViews时,才会放入到RecyclerViewPool里面去。

为什么hasStableIds方法返回true会提高效率呢?

了解了RecyclerView的复用和回收机制之后,这个问题就变得很简单了。我从两个方面来解释原因。

A. 复用方面

1
if (mAdapter.hasStableIds()) {                    holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),                            type, dryRun);                    if (holder != null) {                        // update position                        holder.mPosition = offsetPosition;                        fromScrapOrHiddenOrCache = true;                    }                }

在前面通过Position方式来获取一个ViewHolder失败之后,如果Adapter的hasStableIds方法返回为true,在进行通过ViewType方式来获取ViewHolder时,会优先到1级或者二级缓存里面去寻找,而不是直接去RecyclerViewPool里面去寻找。从这里,我们可以看到,在复用方面,hasStableIds方法提高了效率。

B. 回收方面

1
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);                recycler.recycleViewHolderInternal(viewHolder);            } else {                detachViewAt(index);                recycler.scrapView(view);                mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);            }        }

从上面的代码中,我们可以看出,如果hasStableIds方法返回为true的话,这里所有的回收都进入scrap数组里面。这刚好与前面对应了。

Adapter源码解析

先看Adapter源码

1
public abstract static class Adapter<VH extends ViewHolder> {        private final AdapterDataObservable mObservable = new AdapterDataObservable();        private boolean mHasStableIds = false;        //创建一个ViewHolder对象,主要作用是将数据保存在ViewHolder,以供后面bind操作使用        @NonNull        public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType);        //数据绑定方法        public abstract void onBindViewHolder(@NonNull VH holder, int position);        public void onBindViewHolder(@NonNull VH holder, int position,                @NonNull List<Object> payloads) {            onBindViewHolder(holder, position);        }        @NonNull        public final VH createViewHolder(@NonNull ViewGroup parent, int viewType) {            try {                TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);                final VH holder = onCreateViewHolder(parent, viewType);                if (holder.itemView.getParent() != null) {                    throw new IllegalStateException("ViewHolder views must not be attached when"                            + " created. Ensure that you are not passing 'true' to the attachToRoot"                            + " parameter of LayoutInflater.inflate(..., boolean attachToRoot)");                }                holder.mItemViewType = viewType;                return holder;            } finally {                TraceCompat.endSection();            }        }        public final void bindViewHolder(@NonNull VH holder, int position) {            holder.mPosition = position;            if (hasStableIds()) {                holder.mItemId = getItemId(position);            }            holder.setFlags(ViewHolder.FLAG_BOUND,                    ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID                            | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);            TraceCompat.beginSection(TRACE_BIND_VIEW_TAG);            onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());            holder.clearPayload();            final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();            if (layoutParams instanceof RecyclerView.LayoutParams) {                ((LayoutParams) layoutParams).mInsetsDirty = true;            }            TraceCompat.endSection();        }        //该方法带一个Position,主要是返回当前位置的ViewType。这个方法通常用于一个RecyclerView需要加载不同的布局。        public int getItemViewType(int position) {            return 0;        }        //设置当前RecyclerView的ItemView是否拥有固定id,跟getItemId方法一起使用。如果设置为true,会提高RecyclerView的缓存效率。        public void setHasStableIds(boolean hasStableIds) {            if (hasObservers()) {                throw new IllegalStateException("Cannot change whether this adapter has "                        + "stable IDs while the adapter has registered observers.");            }            mHasStableIds = hasStableIds;        }        //该方法表示的意思是返回当前位置Item的id,此方法只在setHasStableIds设置为true才会生效        public long getItemId(int position) {            return NO_ID;        }        //当前Adapter拥有数据的数量,该方法必须被重写,否则RecyclerView展示不了任何数据        public abstract int getItemCount();        public final boolean hasStableIds() {            return mHasStableIds;        }        public void onViewRecycled(@NonNull VH holder) {        }        public boolean onFailedToRecycleView(@NonNull VH holder) {            return false;        }        public void onViewAttachedToWindow(@NonNull VH holder) {        }        public void onViewDetachedFromWindow(@NonNull VH holder) {        }        public final boolean hasObservers() {            return mObservable.hasObservers();        }        public void registerAdapterDataObserver(@NonNull AdapterDataObserver observer) {            mObservable.registerObserver(observer);        }        public void unregisterAdapterDataObserver(@NonNull AdapterDataObserver observer) {            mObservable.unregisterObserver(observer);        }        public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {        }        public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {        }        public final void notifyDataSetChanged() {            mObservable.notifyChanged();        }        public final void notifyItemChanged(int position) {            mObservable.notifyItemRangeChanged(position, 1);        }        public final void notifyItemChanged(int position, @Nullable Object payload) {            mObservable.notifyItemRangeChanged(position, 1, payload);        }        public final void notifyItemRangeChanged(int positionStart, int itemCount) {            mObservable.notifyItemRangeChanged(positionStart, itemCount);        }        public final void notifyItemRangeChanged(int positionStart, int itemCount,                @Nullable Object payload) {            mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);        }        public final void notifyItemInserted(int position) {            mObservable.notifyItemRangeInserted(position, 1);        }        public final void notifyItemMoved(int fromPosition, int toPosition) {            mObservable.notifyItemMoved(fromPosition, toPosition);        }                public final void notifyItemRangeInserted(int positionStart, int itemCount) {            mObservable.notifyItemRangeInserted(positionStart, itemCount);        }        public final void notifyItemRemoved(int position) {            mObservable.notifyItemRangeRemoved(position, 1);        }        public final void notifyItemRangeRemoved(int positionStart, int itemCount) {            mObservable.notifyItemRangeRemoved(positionStart, itemCount);        }    }

onCreateViewHolder

首先,我们来看一下onCreateViewHolder方法,从它的调用时机入手。 - 一级缓存:scrap数组 - 二级缓存:CachedView - 三级缓存:ViewCacheExtension - 四级缓存:RecyclerViewPool

LayoutManager会获取ViewHolder时,如果4级缓存都没有命中,就会调用Adapter的onCreateViewHolder方法来创建一个新的ViewHolder。

onBindViewHolder

在之前的tryGetViewHolderForPositionByDeadline方法中

1
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);                bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);            }

在tryBindViewHolderByDeadline中调用Adapter的bindViewHolder

1
private boolean tryBindViewHolderByDeadline(@NonNull ViewHolder holder, int offsetPosition,                int position, long deadlineNs) {            //...            mAdapter.bindViewHolder(holder, offsetPosition);            //...            return true;        }

在执行onBindViewHolder方法前后,各自做了一些不同的操作。比如,在执行onBindViewHolder方法之前,更新了ViewHolder的mPosition属性和给ViewHolder设置了一些flag;在执行onBindViewHolder方法之后,清理了ViewHolder的payload,并且还是给ItemView的LayoutParams的mInsetsDirty属性设置为true。

1
public final void bindViewHolder(@NonNull VH holder, int position) {            holder.mPosition = position;            if (hasStableIds()) {                holder.mItemId = getItemId(position);            }            holder.setFlags(ViewHolder.FLAG_BOUND,                    ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID                            | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);            TraceCompat.beginSection(TRACE_BIND_VIEW_TAG);            onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());            holder.clearPayload();            final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();            if (layoutParams instanceof RecyclerView.LayoutParams) {                ((LayoutParams) layoutParams).mInsetsDirty = true;            }            TraceCompat.endSection();        }

ViewHolder的position

这里主要分析两个方法,分别是getAdapterPosition和getLayoutPosition,对应着ViewHolder内部两个成员变量mPosition和mPreLayoutPosition两个属性。

先来看下getAdapterPosition方法

1
public final int getAdapterPosition() {            if (mOwnerRecyclerView == null) {                return NO_POSITION;            }            return mOwnerRecyclerView.getAdapterPositionFor(this);        }

别看getAdapterPosition方法比较麻烦,还调用了RecyclerView的getAdapterPositionFor方法进行位置的计算。但是它表达的意思是非常简单的,就是获取当前ViewHolder所绑定ItemView的真实位置。这里的真实位置说的比较笼统,这样来解释吧,当我们remove掉为position为0的item,正常来说,后面ViewHolder的position应该都减1。但是RecyclerView处理Adapter的更新采用的延迟处理策略,所以在正式处理之前获取ViewHolder的位置可能会出现误差,介于这个原因,getAdapterPosition方法就出现了。 getAdapterPosition方法是怎样保证每次计算都是正确的呢?包括在正式处理之前呢?我们知道,在RecyclerView中,延迟处理的实现是在notify阶段往一个叫mPendingUpdates数组里面添加Operation,分别在dispatchLayoutStep1阶段或者dispatchLayoutStep2阶段进行处理。通过追踪getAdapterPositionFor方法,我们知道getAdapterPosition方法在计算位置时,考虑到mPendingUpdates数组的存在,所以在notify阶段和dispatchLayoutStep1阶段之间(这里假设dispatchLayoutStep1就会处理),getAdapterPosition方法返回正确的位置。

再来看看getLayoutPosition方法

1
public final int getLayoutPosition() {            return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;        }

getLayoutPosition方法返回的是mPosition或者mPreLayoutPosition,但是在dispatchLayoutStep1阶段之前,还未更新每个ViewHolder的position,所以获得不一定的是正确(只有在处理mPendingUpdates的操作时,position才会被更新,对应着的代码就是执行AdapterHelper$Callback接口的方法)。 但是getLayoutPosition方法为什么还有存在的必要呢?我们发现getLayoutPosition方法不会每次都计算,也就是说,getLayoutPosition方法的效率比getAdapterPosition方法高。当我们在Adapter这种调用方法来获取ViewHolder的位置时,可以优先考虑getLayoutPosition方法,因为Adapter的方法回调阶段不在mPendingUpdates处理之前,所以此时getLayoutPosition方法跟getAdapterPosition方法没有任何区别了。 但是需要注意,如果我们在其他地方获取ViewHolder的position,要特别注意这种情况,因为其他地方不能保证与RecyclerView状态同步,这种情况为了保证结果的正确性,我们应该优先考虑getAdapterPosition方法。

notifyDataSetChanged

该方法最终调用了 RecyclerViewDataObserver 的 onChanged 方法

1
@Override    public void onChanged() {        // ...        // 该方法主要做了这两件事        // 1. 给所有 ViewHolder 添加了 FLAG_UPDATE 和 FLAG_INVALID        // 2. 默认情况下(mHasStableIds 为 false)清空 CacheViews        processDataSetCompletelyChanged(true);                if (!mAdapterHelper.hasPendingUpdates()) {            // 进行视图重绘            requestLayout();        }    }

该方法会进行视图重绘,又来到了 layout 过程,继续以 LinearLayoutManager 为例,从它的 onLayoutChildren 方法看起,由于分析第一次 layout 时已经看过一遍了,这次主要看下不同之处:

1
@Override    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {        // ...                detachAndScrapAttachedViews(recycler);                // ...    }

主要区别在于 detachAndScrapAttachedViews 方法,这次它开始起作用了,该方法在 RecyclerView 的 LayoutManager 中定义,看下它的实现:

LayoutManager#detachAndScrapAttachedViews

1
public void detachAndScrapAttachedViews(@NonNull Recycler recycler) {        final int childCount = getChildCount();        for (int i = childCount - 1; i >= 0; i--) {            final View v = getChildAt(i);            scrapOrRecycleView(recycler, i, v);        }    }

由于不是第一次 layout,RecyclerView 这时已经有子 View 了,该方法遍历子 View,调用 scrapOrRecycleView 方法:

1
private void scrapOrRecycleView(Recycler recycler, int index, View view) {        final ViewHolder viewHolder = getChildViewHolderInt(view);        // 不能回收添加了 FLAG_IGNORE 标记的 ViewHolder        // 可通过 LayoutManager 的 ignoreView 为相应的 View 添加该标记        if (viewHolder.shouldIgnore()) {            return;        }        // 这些条件都满足,进入 if 块        if (viewHolder.isInvalid() && !viewHolder.isRemoved()                && !mRecyclerView.mAdapter.hasStableIds()) {            removeViewAt(index);            recycler.recycleViewHolderInternal(viewHolder);        } else {            // ...        }    }

这里将子 View 移除并通过 Recycler 的 recycleViewHolderInternal 方法进行回收

Recycler#recycleViewHolderInternal

1
void recycleViewHolderInternal(ViewHolder holder) {            // ...            boolean cached = false;            boolean recycled = false;            if (forceRecycle || holder.isRecyclable()) {                // 由于此时的 ViewHolder 有 FLAG_INVALID 标记,不会进入此 if 块                if (mViewCacheMax > 0                        && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID                        | ViewHolder.FLAG_REMOVED                        | ViewHolder.FLAG_UPDATE                        | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {                    //...                }                // cached 仍为 false,进入此 if 块                if (!cached) {                    // 通过 RecycledViewPool 的 putRecycledView 方法缓存该 ViewHolder                    addViewHolderToRecycledViewPool(holder, true);                    recycled = true;                }            }                         // ...        }

最终被移除的子 View 缓存到了 RecycledViewPool 中。

后面在调用 fill 方法进行布局填充时,就可以从 RecycledViewPool 中拿取缓存的 View。

notifyItemChanged

该方法传入一个 int 参数,表示要数据有更新的 item 的 position。

1
public final void notifyItemChanged(int position) {        mObservable.notifyItemRangeChanged(position, 1);    }

最终调用 RecyclerViewDataObserver 的 onItemRangeChanged 方法

1
@Override    public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {        // 会在 mAdapterHelper 中创建一个 UpdateOp,将信息保存起来        if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {            // 如果可以进行更新操作,执行该方法            triggerUpdateProcessor();        }    }

继续看 triggerUpdateProcessor 方法

1
void triggerUpdateProcessor() {        // 判断条件默认为 false,执行 else 块        if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {            // ...        } else {            mAdapterUpdateDuringMeasure = true;            requestLayout();        }    }

在保存了一些信息后,还是进行视图重绘。来到了 layout 过程后,还是以 LinearLayoutManager 为例,这次先看下布局过程的 step1,也就是 dispatchLayoutStep1 方法

1
private void dispatchLayoutStep1() {        // ...                processAdapterUpdatesAndSetAnimationFlags();                // ...    }

主要看 processAdapterUpdatesAndSetAnimationFlags 方法,从名字也可以看出,它负责更新 adapter 的信息

1
private void processAdapterUpdatesAndSetAnimationFlags() {        // ...        if (predictiveItemAnimationsEnabled()) {            mAdapterHelper.preProcess();        } else {            mAdapterHelper.consumeUpdatesInOnePass();        }        // ...    }

这里借助了 mAdapterHelper,它最终又通过接口回调(回调了 markViewHoldersUpdated 方法)调用了 RecyclerView 的 viewRangeUpdate 方法

1
void viewRangeUpdate(int positionStart, int itemCount, Object payload) {        // ...        for (int i = 0; i < childCount; i++) {            // ...                        if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {                // (1)                holder.addFlags(ViewHolder.FLAG_UPDATE);                // ...            }        }    }

该方法就是遍历所有子 View,找到所有发生了改变的子 View,进行相关操作。这里重点看注释(1),为改变的 ViewHolder 添加了 FLAG_UPDATE 标记。先记住这点,在后面会用到。

接下来看 onLayoutChildren 方法,和 notifyDataSetChanged 一样,主要的不同之处也是在于 detachAndScrapAttachedViews 方法,该方法遍历子 View,调用 scrapOrRecycleView 方法,下面看一下该方法

LayoutManager#scrapOrRecycleView

1
private void scrapOrRecycleView(Recycler recycler, int index, View view) {        final ViewHolder viewHolder = getChildViewHolderInt(view);        // ...                // 这次 ViewHolder 没有添加 FLAG_INVALID 标记,进入 else 块        if (viewHolder.isInvalid() && !viewHolder.isRemoved()                && !mRecyclerView.mAdapter.hasStableIds()) {            // ...        } else {            detachViewAt(index);            recycler.scrapView(view);            mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);        }    }

这里就和 notifyDataSetChanged 时不一样了,由于在视图重绘前没有给 ViewHolder 添加 FLAG_INVALID 标记,这次进入的是 else 块。

首先将 View 从 RecyclerView 中 detach 掉(而不是 remove 掉)。然后在回收时,调用的是 Recycler 的 scrapView 方法。该方法在前面也分析过了,这里再看一次

1
void scrapView(View view) {        final ViewHolder holder = getChildViewHolderInt(view);                // 满足这几个条件中的一个就可以进入 if 循环        // 1. ViewHolder 设置了 FLAG_REMOVED 或 FLAG_INVALID         // 2. ViewHolder 没有设置 FLAG_UPDATE         // 3. 没有设置动画或者动画可以重用该 ViewHolder         if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)                || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {            // ...                        mAttachedScrap.add(holder);        }         // 不满足上述任意一个条件时,将 View 缓存到 mChangedScrap 中        else {            if (mChangedScrap == null) {                mChangedScrap = new ArrayList<ViewHolder>();            }            holder.setScrapContainer(this, true);            mChangedScrap.add(holder);        }    }

重点看判断里面的条件 2,从前面的分析可以得知,对于发生改变的 ViewHolder,给它设置了 FLAG_UPDATE,所以它现在三个条件都不满足,进入 else 块,而对于其他的 ViewHolder,由于没有设置 FLAG_UPDATE,所以满足条件 2,进入 if 循环。

所以通过 notifyItemChanged 方法更新列表时,发生了改变的子 View 将被缓存到 ChangedScrap 中,而没有发生改变的子 View 则缓存到 AttachedScrap 中,之后通过填充布局的时候对于不同 item 就可以从相应的 Scrap 缓存中得到子 View。

另外,Scrap 缓存只作用于布局阶段,在 layout 的 step3 中将会清空 mAttachedScrap 和 mChangedScrap。

动画机制

滑动机制