目录
- 引言
- 源码分析
- RecyclerViewDataObserver
- AdapterDataObservable
- Adapter
- AdapterHelper
- notifyDataSetChanged
- 最后
引言
在使用 RecyclerView 时 Adapter 也是必备的,在对其进行增删改操作时会用到以下方法:
recyclerView.setAdapter(adapter) adapter.notifyItemInserted(index) adapter.notifyItemChanged(index) adapter.notifyItemRemoved(index) adapter.notifyItemMoved(fromIndex, toIndex) adapter.notifyDataSetChanged()
本篇博客就以此为切入点,分析这些方法的调用流程,以及 notifyDataSetChanged 和 notifyItemXXX 的区别
源码分析
先从最先调用的 setAdapter 入手看一下其源码:
public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild, NestedScrollingChild3 {
Adapter mAdapter;
// ...
public void setAdapter(@Nullable Adapter adapter) {
// ...
// 核心代码
setAdapterInternal(adapter, false, true);
// ...
}
private void setAdapterInternal(@Nullable Adapter adapter, boolean compatibleWithPrevious, boolean removeAndRecycleViews) {
// 设置新的 adapter 之前做一些清理工作
if (mAdapter != null) {
mAdapter.unregisterAdapterDataObserver(mObserver);
mAdapter.onDetachedFromRecyclerView(this); // detach 回调
}
// 清理 item 缓存
if (!compatibleWithPrevious || removeAndRecycleViews) {
removeAndRecycleViews();
}
// 工具类重置
mAdapterHelper.reset();
final Adapter oldAdapter = mAdapter;
mAdapter = adapter; // 赋值
if (adapter != null) {
// 注册
adapter.registerAdapterDataObserver(mObserver);
// attach 回调
adapter.onAttachedToRecyclerView(this);
}
if (mLayout != null) {
// LayoutManager 中 adapter 改变回调
mLayout.onAdapterChanged(oldAdapter, mAdapter);
}
// recycler adapter 改变回调
mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
mState.mStructureChanged = true;
}
}
可以看出上面源码中有两个重要的点:mObserver,mAdapterHelper;
先看一下 adapter.registerAdapterDataObserver 源码:
public void registerAdapterDataObserver(@NonNull AdapterDataObserver observer) {
mObservable.registerObserver(observer);
}
mObserver 和 mObservable 定义如下:
public class RecyclerView {
private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
// ...
public abstract static class Adapter<VH extends ViewHolder> {
private final AdapterDataObservable mObservable = new AdapterDataObservable();
// ...
}
// ...
}
RecyclerViewDataObserver
RecyclerViewDataObserver 继承自 AdapterDataObserver 重写了其全部方法,看一下其核心部分:
private class RecyclerViewDataObserver extends AdapterDataObserver {
// ...
@Override
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
assertNotInLayoutOrScroll(null);
if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
triggerUpdateProcessor();
}
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
assertNotInLayoutOrScroll(null);
if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
triggerUpdateProcessor();
}
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
assertNotInLayoutOrScroll(null);
if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
triggerUpdateProcessor();
}
}
@Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
assertNotInLayoutOrScroll(null);
if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
triggerUpdateProcessor();
}
}
// ...
}
可以看出这几个 onItemRangerXXX 方法都是调用 mAdapterHelper 的同名方法。
AdapterDataObservable
AdapterDataObservable 继承自抽象类 Observable 并且泛型为 AdapterDataObserver (上一节提到的 RecyclerViewDataObserver 就是 AdapterDataObserver 子类),Observable 是 sdk 中给我们提供的一个观察者模式基类 Observable 意为可观察对象,其内部维护一个 mObservers 容器(泛型 ArrayList)用于存放“观察者”,并对外提供了注册、解注册方法;
Observable 源码比较简单就不贴了,来看一下 AdapterDataObservable 的核心源码:
static class AdapterDataObservable extends Observable<AdapterDataObserver> {
public boolean hasObservers() {
// 判断 mObservers 容器中是否有 “观察者”
return !mObservers.isEmpty();
}
public void notifyChanged() {
// 遍历 mObservers 调用 onChanged
for (int i = mObservers.size() -; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
public void notifyStateRestorationPolicyChanged() {
for (int i = mObservers.size() -; i >= 0; i--) {
mObservers.get(i).onStateRestorationPolicyChanged();
}
}
public void notifyItemRangeChanged(int positionStart, int itemCount) {
notifyItemRangeChanged(positionStart, itemCount, null);
}
public void notifyItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {
for (int i = mObservers.size() -; i >= 0; i--) {
mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
}
}
public void notifyItemRangeInserted(int positionStart, int itemCount) {
for (int i = mObservers.size() -; i >= 0; i--) {
mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
}
}
public void notifyItemRangeRemoved(int positionStart, int itemCount) {
for (int i = mObservers.size() -; i >= 0; i--) {
mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
}
}
public void notifyItemMoved(int fromPosition, int toPosition) {
for (int i = mObservers.size() -; i >= 0; i--) {
mObservers.get(i).onItemRangeMoved(fromPosition, toPosition,);
}
}
}
可以看出 notifyXXX 方法均为遍历 mObservers 中对应的方法,在这里也就是调用 RecyclerViewDataObserver 中的方法;
Adapter
到这里可以看出,setAdapter 中的 registerAdapterDataObserver 是将 RecyclerView 与 Adapter 用观察者模式相关联,那么先来看一下 Adapter 的相关源码:
public abstract static class Adapter<VH extends ViewHolder> {
private final AdapterDataObservable mObservable = new AdapterDataObservable();
// ...
// 注册
public void registerAdapterDataObserver(@NonNull AdapterDataObserver observer) {
mObservable.registerObserver(observer);
}
// 解注册
public void unregisterAdapterDataObserver(@NonNull AdapterDataObserver observer) {
mObservable.unregisterObserver(observer);
}
public final void notifyDataSetChanged() {
mObservable.notifyChanged();
}
public final void notifyItemChanged(int position) {
mObservable.notifyItemRangeChanged(position,);
}
// 剩下的 notifyItemXXX 方法同上 都是调用 mObservable 同名方法 就不贴代码了
// ...
}
Adapter 中的 notifyXXX 都调用了 mObservable 的同名方法,那么经过上面的分析这就相当于调用到了 RecyclerViewDataObserver 中的方法,RecyclerViewDataObserver 的源码上面的小节部分已经提到,都是调用 mAdapterHelper 中的方法,接下来就来看看 AdapterHelper 的源码;
AdapterHelper
先看一下其在 RecyclerView 中的初始化:
public class RecyclerView {
AdapterHelper mAdapterHelper;
// ...
public RecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
// ...
initAdapterManager();
// ...
}
void initAdapterManager() {
mAdapterHelper = new AdapterHelper(new AdapterHelper.Callback() {
// 篇幅原因 方法实现就省略了
});
}
// ...
}
在构造方法中,对 mAdapterHelper 进行了初始化,上述 RecyclerViewDataObserver 中调用的 onItemRangeXXX 方法很多这里就以 onItemRangeChanged 为例看下源码:
boolean onItemRangeChanged(int positionStart, int itemCount, Object payload) {
if (itemCount <) {
return false;
}
// 注意这里是两步操作
// obtainUpdateOp 构建 UpdateOp 对象
// 添加到 mPendingUpdates 容器
mPendingUpdates.add(obtainUpdateOp(UpdateOp.UPDATE, positionStart, itemCount, payload));
// 记录操作类型
mExistingUpdateTypes |= UpdateOp.UPDATE;
return mPendingUpdates.size() ==;
}
mPendingUpdates 存放 UpdateOp 对象,UpdateOp 中记录 item 变化的相关信息;
到这里再回到 RecyclerViewDataObserver 中的 onItemRangeXXX 方法,如果返回 ture 还会调用 triggerUpdateProcessor(),看一下这个方法源码:
RecyclerViewDataObserver.java
void triggerUpdateProcessor() {
// mHasFixedSize 通过 setHasFixedSize 设置 默认是 false
if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
// 执行 mUpdateChildViewsRunnable
// 这个 runable 相比于 else 中直接调用 requestLayout() 增加了一些判断 算是性能上的一个优化
ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
} else {
mAdapterUpdateDuringMeasure = true;
// 调用 requestLayout 重新布局
requestLayout();
}
}
看到这里基本可以了解到,当我们调用 adapter.notifyItemXXX 后会触发 requestLayout() 重新调用布局流程 dispatchLayoutStep1、2、3 ,如果设置 mHasFixedSize 为 true 性能应该会更佳;
notifyDataSetChanged
当我们调用 notifyDataSetChanged 时编译器会给出提示:
提示最好使用更具体的变更事件,也就是调用 notifyItemXXX 更好。那么我们来看一下 notifyDataSetChanged 为什么不如 notifyItemXXX。通过上面的源码流程,直接看 RecyclerViewDataObserver 的 onChanged 方法源码:
RecyclerViewDataObserver.java
public void onChanged() {
assertNotInLayoutOrScroll(null);
mState.mStructureChanged = true;
// 注意这一行
processDataSetCompletelyChanged(true);
if (!mAdapterHelper.hasPendingUpdates()) {
requestLayout();
}
}
onChanged 内部直接调用了 requestLayout,和 onItemRangeXXX 类似(上面分析 onItemRangeXXX 内部调用 triggerUpdateProcessor 最终也会调用 requestLayout),但是注意 processDataSetCompletelyChanged 这个方法:
void processDataSetCompletelyChanged(boolean dispatchItemsChanged) {
mDispatchItemsChangedEvent |= dispatchItemsChanged;
mDataSetHasChangedAfterLayout = true;
// 方法名的大概意思:标记已知view为无效
markKnownViewsInvalid();
}
void markKnownViewsInvalid() {
final int childCount = mChildHelper.getUnfilteredChildCount();
// 循环每个 viewhodler
for (int i =; i < childCount; i++) {
final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
if (holder != null && !holder.shouldIgnore()) {
// 给 viewholder 添加了 FLAG_INVALID
holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
}
}
markItemDecorInsetsDirty();
mRecycler.markKnownViewsInvalid();
}
添加这个标记有什么作用呢?这里就不卖关子了,回想一下之前博客讲述的回收复用流程,Recycler 负责获取 ViewHolder,通过 getViewForPosition 最终调用到 tryGetViewHolderForPositionByDeadline 方法从多级缓存中获取 ViewHolder,获取完了之后在绑定数据时有这么一个判断:
Recycler.java
ViewHolder tryGetViewHolderForPositionByDeadline(int position,
boolean dryRun, long deadlineNs) {
//...
if (mState.isPreLayout() && holder.isBound()) {
holder.mPreLayoutPosition = position;
}
// 注意这里的 else if 分支
else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
// 如果 viewholder 有 FLAG_INVALID 标记会调用 tryBindViewHolderByDeadline
final int offsetPosition = mAdapterHelper.findPositionOffset(position);
bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
}
//...
}
而 tryBindViewHolderByDeadline 中又调用了 bindViewHolder,源码如下:
RecyclerView.java
private boolean tryBindViewHolderByDeadline(@NonNull ViewHolder holder, int offsetPosition,
int position, long deadlineNs) {
// ...
mAdapter.bindViewHolder(holder, offsetPosition);
// ...
}
bindViewHolder 中又调用了 onBindViewHolder 重新进行了数据绑定设置;所以,使用 notifyDataSetChanged 会将所有的 itemView 进行无效化标记,布局时会全部走一次数据绑定,所以推荐使用 notifyItemXXX 来对 RecyclerView 进行更新。
最后
本篇 Adapter 的分析略显粗糙,仅对关键源码进行了分析,主要是觉得这部分内容在日常开发或者面试中最常遇到的问题就是 notifyDataSetChanged 和 notifyItemXXX 的区别。本系列也是对源码的浅析,点到为止。