前言
读源码常常陷入繁复的细节中,结果看了半天,感觉自己懂了点什么,又好像什么也没弄懂。所以要带着目的性去看源码,理清主干部分的思路。本文的目的是梳理源码中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<Observer> 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<Observer>类型的集合用来存放观察者对象,有相关的方法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<T> {
protected final ArrayList<T> mObservers = new ArrayList<T>();
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<DataSetObserver> {
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<VH extends ViewHolder> {
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<Object> 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<AdapterDataObserver> {
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了。
后记
基本上回答了文章开始的问题,没有在源码中迷路。。。。喜欢的童鞋帮忙戳喜欢,有问题也可以评论。
网友评论