前言
读源码常常陷入繁复的细节中,结果看了半天,感觉自己懂了点什么,又好像什么也没弄懂。所以要带着目的性去看源码,理清主干部分的思路。本文的目的是梳理源码中RecyclerView怎么根据数据源的变更,让Item更新UI的。由于要以观察者模式为解析刀,所以先来看看观察者模式的前世今生。还是那句话:希望能简单点把问题说清楚,避免高大上的定义,虚无缥缈的遣词造句。
1
JDK源码中的观察者模式
观察者模式可以简单理解成定报纸,如果你订阅了某报纸,等有新的报纸时,该报就会把新的报纸投递给所有的订阅报纸的人。即你订阅,有更新就会给到你。这个订阅者和报纸之间就构成了观察者和被观察者的关系。JDK有2个类是帮助开发者快速实现观察者模式的—Observable(抽象被观察者)和Observer(抽象观察者)。
public interface Observer {
void update(Observable o, Object arg);
}
发现JDK实现的抽象观察者很简单,就是个接口,有个叫update的方法。这个方法是用来接收被观察者发过来的新消息,消息的内容是参数arge。
public class Observable {
private boolean changed = false;
private Vector
obs; public Observable() { obs = new Vector<>(); } public synchronized void addObserver(Observer o) { if (o == null) throw new NullPointerException(); if (!obs.contains(o)) { obs.addElement(o); } } public synchronized void deleteObserver(Observer o) { obs.removeElement(o); } public void notifyObservers() { notifyObservers(null); } public void notifyObservers(Object arg) { Object[] arrLocal; synchronized (this) { if (!changed) return; arrLocal = obs.toArray(); clearChanged(); } for (int i = arrLocal.length-1; i>=0; i--) ((Observer)arrLocal[i]).update(this, arg); } public synchronized void deleteObservers() { obs.removeAllElements(); } protected synchronized void setChanged() { changed = true; } protected synchronized void clearChanged() { changed = false; } public synchronized boolean hasChanged() { return changed; } public synchronized int countObservers() { return obs.size(); } }
梳理下这个类
- 有个Vector 类型的集合用来存放观察者对象,有相关的方法addObserver,deleteObserver来操作删减集合中的对象。即订阅了这个被观察者,我就调用addObserver添加一个观察者。
- 变参的通知方法notifyObservers,关键点就是取出集合中的观察者对象,然后用一个for循环不断的调用每个订阅者的update方法,通知他们有新的消息。
- 全局变量changed,就是一个标识位,看数据是否有更新
是不是突然感觉好简单,想徒手撸个源码一样的观察者模式?
2
用JDK提供的类型实现观察者模式
既然了解了原理,立即手痒难耐的撸个例子。先来一个报纸(NewspaperObservable)
public class NewspaperObservable extends Observable{
public void notifyAllMan(String info)
{
setChanged();
notifyObservers(info);
}
}
再来个定报纸的人(ManObserver),收到新报纸,就打印报纸的内容
public class ManObserver implements Observer {
@Override
public void update(Observable o, Object arg) {
System.out.print(arg);
}
}
最后试试,订报纸的人能不能及时收到新报纸。
public class Test {
public static void main(String[] arge)
{
NewspaperObservable newspaperObservable = new NewspaperObservable();
newspaperObservable.addObserver(new ManObserver());
newspaperObservable.addObserver(new ManObserver());
newspaperObservable.notifyAllMan("have a new paper");
}
}
3
Android源码中的观察者模式
Android Framework没有直接使用java中的观察者模式,而是有自己的实现,看懂了第一节中关键几点,相信这很简单。
public abstract class Observable
{ protected final ArrayList
mObservers = new ArrayList
(); public void registerObserver(T observer) { if (observer == null) { throw new IllegalArgumentException("The observer is null."); } synchronized(mObservers) { if (mObservers.contains(observer)) { throw new IllegalStateException("Observer " + observer + " is already registered."); } mObservers.add(observer); } } public void unregisterObserver(T observer) { if (observer == null) { throw new IllegalArgumentException("The observer is null."); } synchronized(mObservers) { int index = mObservers.indexOf(observer); if (index == -1) { throw new IllegalStateException("Observer " + observer + " was not registered."); } mObservers.remove(index); } } public void unregisterAll() { synchronized(mObservers) { mObservers.clear(); } } }
Android中的被观察者维护了一个观察者的集合,通过registerObserver,unregisterObserver方法管理订阅者,跟JDK中的做法如出一辙。可是最最重要的通知订阅者更新的方法呢??一脸懵逼的接着往下看。。。
public class DataSetObservable extends Observable
{ public void notifyChanged() { synchronized(mObservers) { for (int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onChanged(); } } } public void notifyInvalidated() { synchronized (mObservers) { for (int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onInvalidated(); } } } }
发现类DataSetObservable继承了Observable,并实现了通知方法。那为什么不在Observable中实现这些方法呢,跟JDK中的实现一样。我的理解是,这里的Observable使用了泛型,而交给子类去实现通知方法,子类可以灵活的定义观察者类型,并使用观察者中的方法。所以DataSetObservable已经深入到业务中了。观察者变化不大。
public abstract class DataSetObserver {
public void onChanged() {
// Do nothing
}
public void onInvalidated() {
// Do nothing
}
}
4
RecyclerView源码
终于可以开始我们最原始的问题:源码中RecyclerView怎么根据数据源的变更,让Item更新UI的?秘密应该在RecyclerView,RecyclerView.Adapter这两个类中。根据我们的使用经验,有重大嫌疑的方法有RecyclerView.setAdapter(),Adapter.notifyDataSetChanged()。。。本文正式从技术文变推理文。。。RecyclerView源码一看11090行,再看RecyclerView.Adapter少很多哇(内部类当然的),果断放上源码
public static abstract class Adapter
{ private final AdapterDataObservable mObservable = new AdapterDataObservable(); private boolean mHasStableIds = false; public abstract VH onCreateViewHolder(ViewGroup parent, int viewType); public abstract void onBindViewHolder(VH holder, int position); public void onBindViewHolder(VH holder, int position, List
payloads) { onBindViewHolder(holder, position); } public final VH createViewHolder(ViewGroup parent, int viewType) { TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG); final VH holder = onCreateViewHolder(parent, viewType); holder.mItemViewType = viewType; TraceCompat.endSection(); return holder; } public final void bindViewHolder(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(); TraceCompat.endSection(); } public int getItemViewType(int position) { return 0; } 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; } public long getItemId(int position) { return NO_ID; } public abstract int getItemCount(); public final boolean hasStableIds() { return mHasStableIds; } public void onViewRecycled(VH holder) { } public boolean onFailedToRecycleView(VH holder) { return false; } public void onViewAttachedToWindow(VH holder) { } public void onViewDetachedFromWindow(VH holder) { } public final boolean hasObservers() { return mObservable.hasObservers(); } public void registerAdapterDataObserver(AdapterDataObserver observer) { mObservable.registerObserver(observer); } public void unregisterAdapterDataObserver(AdapterDataObserver observer) { mObservable.unregisterObserver(observer); } public void onAttachedToRecyclerView(RecyclerView recyclerView) { } public void onDetachedFromRecyclerView(RecyclerView recyclerView) { } public final void notifyDataSetChanged() { mObservable.notifyChanged(); } public final void notifyItemChanged(int position) { mObservable.notifyItemRangeChanged(position, 1); } public final void notifyItemChanged(int position, 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, 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); } }
一眼就看到了全局变量mObservable赤果果的站在第一行,然后看到方法registerAdapterDataObserver,unregisterAdapterDataObserver,我可以大胆的推测每个Item都是数据变更的观察者,而被观察者赫然就是AdapterDataObservable。而后面的代码
public final void notifyDataSetChanged() {
mObservable.notifyChanged();
}
public final void notifyItemChanged(int position) {
mObservable.notifyItemRangeChanged(position, 1);
}
public final void notifyItemChanged(int position, 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, 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);
}
这些通知数据变更的方法,包括节点的更新,全局的更新,无一例外的是由被观察者AdapterDataObservable发送更新通知后完成。那么下面只需要了解以下问题,就能回答最初的问题。
- AdapterDataObserver 和 AdapterDataObservable什么时候完成绑定的?
- AdapterDataObserver 怎么更新UI的?
那么是时候看RecyclerView.setAdapter()方法了。
public void setAdapter(Adapter adapter) {
// bail out if layout is frozen
setLayoutFrozen(false);
setAdapterInternal(adapter, false, true);
requestLayout();
}
接着看setAdapterInternal()
private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
boolean removeAndRecycleViews) {
if (mAdapter != null) {
mAdapter.unregisterAdapterDataObserver(mObserver);
mAdapter.onDetachedFromRecyclerView(this);
}
if (!compatibleWithPrevious || removeAndRecycleViews) {
// end all running animations
if (mItemAnimator != null) {
mItemAnimator.endAnimations();
}
// Since animations are ended, mLayout.children should be equal to
// recyclerView.children. This may not be true if item animator's end does not work as
// expected. (e.g. not release children instantly). It is safer to use mLayout's child
// count.
if (mLayout != null) {
mLayout.removeAndRecycleAllViews(mRecycler);
mLayout.removeAndRecycleScrapInt(mRecycler);
}
// we should clear it here before adapters are swapped to ensure correct callbacks.
mRecycler.clear();
}
mAdapterHelper.reset();
final Adapter oldAdapter = mAdapter;
mAdapter = adapter;
if (adapter != null) {
adapter.registerAdapterDataObserver(mObserver);
adapter.onAttachedToRecyclerView(this);
}
if (mLayout != null) {
mLayout.onAdapterChanged(oldAdapter, mAdapter);
}
mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
mState.mStructureChanged = true;
markKnownViewsInvalid();
}
代码中很明确的调用了
adapter.registerAdapterDataObserver(mObserver);
完成了绑定。而mObserver是RecyclerViewDataObserver类型,RecyclerViewDataObserver继承了AdapterDataObserver 。那么更新UI的方法应该在RecyclerViewDataObserver中。
private class RecyclerViewDataObserver extends AdapterDataObserver {
@Override
public void onChanged() {
assertNotInLayoutOrScroll(null);
if (mAdapter.hasStableIds()) {
// TODO Determine what actually changed.
// This is more important to implement now since this callback will disable all
// animations because we cannot rely on positions.
mState.mStructureChanged = true;
setDataSetChangedAfterLayout();
} else {
mState.mStructureChanged = true;
setDataSetChangedAfterLayout();
}
if (!mAdapterHelper.hasPendingUpdates()) {
requestLayout();
}
}
@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();
}
}
void triggerUpdateProcessor() {
if (mPostUpdatesOnAnimation && mHasFixedSize && mIsAttached) {
ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
} else {
mAdapterUpdateDuringMeasure = true;
requestLayout();
}
}
}
最后看AdapterDataObservable发送通知的方法,通过这些notify方法通知观察者更新UI。
static class AdapterDataObservable extends Observable
{ public boolean hasObservers() { return !mObservers.isEmpty(); } public void notifyChanged() { for (int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onChanged(); } } public void notifyItemRangeChanged(int positionStart, int itemCount) { notifyItemRangeChanged(positionStart, itemCount, null); } public void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) { for (int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload); } } public void notifyItemRangeInserted(int positionStart, int itemCount) { for (int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onItemRangeInserted(positionStart, itemCount); } } public void notifyItemRangeRemoved(int positionStart, int itemCount) { for (int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onItemRangeRemoved(positionStart, itemCount); } } public void notifyItemMoved(int fromPosition, int toPosition) { for (int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1); } } }
RecyclerViewDataObserver 接到通知就妥妥的更新UI了。
后记
基本上回答了文章开始的问题,没有在源码中迷路。。。。喜欢的童鞋帮忙戳喜欢,有问题也可以评论。