RecyclerView notifyDataSetChanged 之后的源码分析

源码版本:androidx1.3.2

分析场景:

使用线性布局,方向为竖直方向,布局从上到下。宽高都是MATCH_PARENT。首次设置适配器以后,更改数据(注意,数据的数量没有变化),然后调用notifyDataSetChanged。没有手动去滚动RecyclerView。

先说下结论:

  1. notifyDataSetChanged 的时候,会将所有 ViewHolder 直接回收到 RecycledViewPool 中,每种 ItemViewType 默认缓存5个,超过5个直接丢弃。
  2. 不会放入 mCachedViews中,因为我们给 ViewHolder 添加了标记位 FLAG_INVALID | FLAG_UPDATE。
  3. 复用的时候,是从 RecyclerViewPool 中获取的。 如果 RecyclerViewPool 中的ViewHolder 数量不够,还会新创建ViewHolder。 无论是从 RecyclerViewPool 中获取的,还有新创建的ViewHolder,对应的 View 返回之前,都会执行 ViewHolder 的 onBindViewHolder 方法。
  4. notifyDataSetChanged 的时候,没有动画效果。可以认为 dispatchLayoutStep1 和 dispatchLayoutStep3 方法 都没有作用。只有 dispatchLayoutStep2 方法做了回收和复用的工作。

调用 Adapter 的 notifyDataSetChanged 方法

binding.btnNotifyItemChanged.setOnClickListener {
    //还是8条数据
    for (i in 0 until 8) {
        arrayList.add(CheckBoxModel("notifyDataSetChanged 新的数据$i", false))
    }
    rv.adapter?.notifyDataSetChanged()
}

调用 notifyDataSetChanged 方法后,会触发 RecyclerView 的 mObserver 的 onChanged 方法。

// RecyclerView.java
private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();

RecyclerView.RecyclerViewDataObserver 的 onChanged 方法。

@Override
public void onChanged() {
    assertNotInLayoutOrScroll(null);
    //注释1处,将mStructureChanged置为true。
    mState.mStructureChanged = true;
    //注释2处,调用processDataSetCompletelyChanged方法。
    processDataSetCompletelyChanged(true);
    if(!mAdapterHelper.hasPendingUpdates()) {
        //注释3处,requestLayout
        requestLayout();
    }
}
void processDataSetCompletelyChanged(boolean dispatchItemsChanged) {
    //置为true
    mDispatchItemsChangedEvent |= dispatchItemsChanged;
    //将mDataSetHasChangedAfterLayout置为true。
    mDataSetHasChangedAfterLayout = true;
    //注释1处,将所有ViewHolder标记为invalid。
    markKnownViewsInvalid();
}

注释1处,将所有ViewHolder标记为invalid。

/**
 * Mark all known views as invalid. Used in response to a, "the whole world might have changed"
 * data change event.
 */
void markKnownViewsInvalid() {
    final int childCount = mChildHelper.getUnfilteredChildCount();
    for(int i = 0; i < childCount; i++) {
        final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
        if(holder != null && !holder.shouldIgnore()) {
            //注释0处,给ViewHolder设置标记 FLAG_INVALID | FLAG_UPDATE
            holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
        }
    }
    
    markItemDecorInsetsDirty();
    //注释1处,调用RecyclerView的mRecycler.markKnownViewsInvalid方法。
    mRecycler.markKnownViewsInvalid();
}

注释0处,给ViewHolder设置标记 FLAG_INVALID | FLAG_UPDATE

注释1处,调用RecyclerView的mRecycler.markKnownViewsInvalid方法。

void markKnownViewsInvalid() {
    final int cachedCount = mCachedViews.size();
    for(int i = 0; i < cachedCount; i++) {
        final ViewHolder holder = mCachedViews.get(i);
        if(holder != null) {
            //如果 mCachedViews 中存在缓存的 ViewHolder ,就将 ViewHolder 添加标记 FLAG_INVALID | FLAG_UPDATE
            holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
            holder.addChangePayload(null);
        }
    }

    if(mAdapter == null || !mAdapter.hasStableIds()) {
        // we cannot re-use cached views in this case. Recycle them all
        //将 mCachedViews 中的 ViewHolder 回收到 RecycledViewPool 中,并将 mCachedViews 清空
        recycleAndClearCachedViews();
    }
}

如果 mCachedViews 中存在缓存的 ViewHolder ,就将 ViewHolder 添加标记 FLAG_INVALID | FLAG_UPDATE。

然后将 mCachedViews 中的 ViewHolder 回收到 RecycledViewPool 中,并将 mCachedViews 清空。(在我们这个分析场景中,没有发生滚动,所以其实 mCachedViews 中是没有缓存 ViewHolder 的。)

回到 RecyclerView.RecyclerViewDataObserver 的 onChanged 方法注释3处,调用 requestLayout() 后,会触发 onMeasure 和 onLayout 方法。

在我们的分析场景中, RecyclerView 宽高都是 MATCH_PARENT ,可以认为onMeasure 方法会直接 return。

onLayout 方法内部会调用dispatchLayout方法。

void dispatchLayout() {
    //...
    mState.mIsMeasuring = false;
    if (mState.mLayoutStep == State.STEP_START) {
        //注释1处,调用dispatchLayoutStep1方法。
        dispatchLayoutStep1();
        mLayout.setExactMeasureSpecsFrom(this);
        //注释2处,调用dispatchLayoutStep2方法。
        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 {
        mLayout.setExactMeasureSpecsFrom(this);
    }
    //注释3处,调用dispatchLayoutStep3方法。
    dispatchLayoutStep3();
}

注释1处,调用dispatchLayoutStep1方法。在 notifyDataSetChanged 的时候,不会有动画,可以认为 dispatchLayoutStep1 方法只是将 mState.mLayoutStep 置为了 State.STEP_LAYOUT。

private void dispatchLayoutStep1() {
    //...
    //将当前布局步骤赋值为State.STEP_LAYOUT
    mState.mLayoutStep = State.STEP_LAYOUT;
}

注释2处,调用dispatchLayoutStep2方法。会调用LinearLayoutManager的onLayoutChildren方法。

@Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
    //...
    //注释1处,如果当前存在attach到RecyclerView的View,则临时detach,后面再复用。
    detachAndScrapAttachedViews(recycler);
    //...
    //注释2处,调用fill方法。填充RecyclerView    
    fill(recycler, mLayoutState, state, false);
    
}

注释1处, 调用RecyclerView.LayoutManager的detachAndScrapAttachedViews方法。

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

RecyclerView.LayoutManager的scrapOrRecycleView方法。

private void scrapOrRecycleView(Recycler recycler, int index, View view) {
    final ViewHolder viewHolder = getChildViewHolderInt(view);
    if (viewHolder.shouldIgnore()) {
        return;
    }
    //注释0处,notifyDataSetChanged 的时候,
    //我们前面给 ViewHolder 添加了标记位 FLAG_INVALID | FLAG_UPDATE,这里条件满足。
    if (viewHolder.isInvalid() && !viewHolder.isRemoved()
            && !mRecyclerView.mAdapter.hasStableIds()) {
        //注释1处,移除View
        removeViewAt(index);
        //注释2处,回收ViewHolder
        recycler.recycleViewHolderInternal(viewHolder);
    } else {
        //注释3处,detachView
        detachViewAt(index);
        //注释4处,回收View
        recycler.scrapView(view);
        mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
    }
}

注释0处,notifyDataSetChanged 的时候,我们前面给 ViewHolder 添加了标记位 FLAG_INVALID | FLAG_UPDATE,这里条件满足。

注释1处,移除View,这里会真正把子View从RecyclerView中移除 RecyclerView.this.removeViewAt(index);

注释2处,回收ViewHolder,调用Recycler的recycleViewHolderInternal方法。

void recycleViewHolderInternal(ViewHolder holder) {
    //...
    if(forceRecycle || holder.isRecyclable()) {
        //注释0处,notifyDataSetChanged 的时候,我们前面给 ViewHolder 添加了标记位 FLAG_INVALID | FLAG_UPDATE,这里条件不满足。
        if(mViewCacheMax > 0 && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID 
                                | ViewHolder.FLAG_REMOVED 
                                | ViewHolder.FLAG_UPDATE 
                                | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
            //...
            //添加到 mCachedViews
            mCachedViews.add(targetCacheIndex, holder);
            cached = true;
        }
        if(!cached) {
            //注释1处,直接加入到RecycledViewPool中。
            addViewHolderToRecycledViewPool(holder, true);
            recycled = true;
        }
    } 
    //...
    mViewInfoStore.removeViewHolder(holder);
    
}

注释0处,notifyDataSetChanged 的时候,我们前面给 ViewHolder 添加了标记位 FLAG_INVALID | FLAG_UPDATE,这里条件不满足。不会把 ViewHolder 缓存到 mCachedViews

注释1处,直接回收到到RecycledViewPool,每种 ItemViewType 默认缓存5个,超过5个直接丢弃。

回到 dispatchLayoutStep2 方法注释2处,调用 LinearLayoutManager 的 fill方法。填充RecyclerView。

/**
 * @param recycler        当前关联到RecyclerView的recycler。
 * @param layoutState     该如何填充可用空间的配置信息。
 * @param state           Context passed by the RecyclerView to control scroll steps.
 * @param stopOnFocusable 如果为true的话,遇到第一个可获取焦点的View则停止填充。
 * @return 返回添加的像素。
 */
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
        RecyclerView.State state, boolean stopOnFocusable) {
    // max offset we should set is mFastScroll + available
    final int start = layoutState.mAvailable;
    //...
    //注释0处,获取可以填充的空间 remainingSpace。
    int remainingSpace = layoutState.mAvailable + layoutState.mExtraFillSpace;
    LayoutChunkResult layoutChunkResult = mLayoutChunkResult;
    //注释1处,如果还有可用空间并且还有更多的数据的话,就继续循环填充。
    while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
        layoutChunkResult.resetInternal();
        //注释2处,调用 layoutChunk 方法 获取并添加子View,然后测量、布局子View并将分割线考虑在内。
        layoutChunk(recycler, state, layoutState, layoutChunkResult);
        //条件满足的话,跳出循环
        if (layoutChunkResult.mFinished) {
            break;
        }
        layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection;
        /**
         * 消耗可用的填充空间remainingSpace。
         * Consume the available space if:
         * * layoutChunk did not request to be ignored
         * * OR we are laying out scrap children
         * * OR we are not doing pre-layout
         * 
         * 注意一下,被notifyItemRemoved的View的空间是不会被消耗的。`layoutChunkResult.mIgnoreConsumed = true`。不过在我们这个例子中用不到。
         * 
         */
        if (!layoutChunkResult.mIgnoreConsumed || layoutState.mScrapList != null
            || !state.isPreLayout()) {
                layoutState.mAvailable -= layoutChunkResult.mConsumed;
                //注释3处,减去填充的View消耗的空间
                remainingSpace -= layoutChunkResult.mConsumed;
        }
        if (stopOnFocusable && layoutChunkResult.mFocusable) {
            break;
        }
    }
    
    return start - layoutState.mAvailable;
}

注释0处,获取可以填充的空间 remainingSpace。
注释1处,如果还有可用空间并且还有更多的数据的话,就继续循环填充。
注释2处,调用 layoutChunk 方法 获取并添加子View,然后测量、布局子View并将分割线考虑在内。

void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
    LayoutState layoutState, LayoutChunkResult result) {
    //注释1处,获取下一个View。可能是从缓存获取的,也可能是从新创建的。
    View view = layoutState.next(recycler);
    
    RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
    if(layoutState.mScrapList == null) {
        if(mShouldReverseLayout == (layoutState.mLayoutDirection == LayoutState.LAYOUT_START)) {
            //添加到RecyclerView中 RecyclerView.this.addView(child, index);
            addView(view);
        } else {
            addView(view, 0);
        }
    } else {
        if(mShouldReverseLayout == (layoutState.mLayoutDirection == LayoutState.LAYOUT_START)) {
            addDisappearingView(view);
        } else {
            addDisappearingView(view, 0);
        }
    }
    //测量子View
    measureChildWithMargins(view, 0, 0);
    //记录消耗的空间,就是子View的高度包括decorationd等。
    result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);
    int left, top, right, bottom;
    if(mOrientation == VERTICAL) {
        if(isLayoutRTL()) {
            right = getWidth() - getPaddingRight();
            left = right - mOrientationHelper.getDecoratedMeasurementInOther(view);
        } else {
            left = getPaddingLeft();
            right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
        }
        if(layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
            bottom = layoutState.mOffset;
            top = layoutState.mOffset - result.mConsumed;
        } else {
            top = layoutState.mOffset;
            bottom = layoutState.mOffset + result.mConsumed;
        }
    } 
    // 布局子View
    layoutDecoratedWithMargins(view, left, top, right, bottom);
    if(params.isItemRemoved() || params.isItemChanged()) {
        //如果是 remove 或者 change 的话,就标记为true,表示不消耗空间。
        result.mIgnoreConsumed = true;
    }
    result.mFocusable = view.hasFocusable();
}

注释1处,获取下一个View。在我们这个例子中,是从 RecyclerViewPool 中获取的,还有新创建的。无论是从 RecyclerViewPool 中获取的,还有新创建的ViewHolder,对应的 View 返回之前,会执行 ViewHolder 的 onBindViewHolder 方法。

回到dispatchLayout方法的注释3处,调用dispatchLayoutStep3方法。在 notifyDataSetChanged 的时候, 可以认为 dispatchLayoutStep3 方法只是将 mState.mLayoutStep 置为了 State.STEP_START,并标记布局完成。

然后:就完了!

参考链接:

  • 20
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值