概念
对象间的一种一个对多的依赖关系,当一个对象的状态发送改变时,所以依赖于它的对象都得到通知并被自动更新。
下面来看UML图,
1622601092(1).pngSubject(抽象主题):又叫抽象被观察者(Observable),把所有观察者对象的引用保存到一个集合里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
ConcreteSubject(具体主题):又叫具体被观察者,将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。
Observer (抽象观察者):为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
ConcrereObserver(具体观察者):实现抽象观察者定义的更新接口,当得到主题更改通知时更新自身的状态。
具体使用
介绍完其原理,我们来引入一个场景:
在两个类中,如果一个类的变化需要在另一个类中进行一些状态的更新,有哪些方法可以实现?
1、回调
2、让另一个类直接持有该类的对象
3、观察者模式
回调
用高德地图获取位置为例:
首先我们创建一个接口,
public interface OnMapLocationListener {
void onLocation(AMapLocation amapLocation);
}
当我们获取到定位之后,
@Override
public void onLocationChanged(AMapLocation aMapLocation) {
if (aMapLocation != null && listener != null){
listener.onLocation(aMapLocation);
}
}
然后我们可以在activity中直接采用匿名内部类的方式去得到这个回调。
GaoDeMapUtil.getInstance().setListener(new OnMapLocationListener() {
@Override
public void onLocation(AMapLocation amapLocation) {
if (amapLocation != null) {
if (amapLocation.getErrorCode() == 0) {
//定位成功回调信息,设置相关消息
//tvLocation.setText(amapLocation.getCity());
set.setLocation(amapLocation.getCity());
} else {
//显示错误信息ErrCode是错误码,errInfo是错误信息,详见错误码表。
Log.e("AmapError", "location Error, ErrCode:"
+ amapLocation.getErrorCode() + ", errInfo:"
+ amapLocation.getErrorInfo());
}
}
}
});
可以看到,这种方式十分简单,分三步就好,但是有一个缺点,如果回调嵌套过多,则会阅读的时候十分不便,比如Glide中有四层回调嵌套,看源码的时候痛不欲生。
让另一个类直接持有该类的对象
直接在该类中持有另一个类的对象,那样就可以直接得到另一个类的方法进行操作,但是这种方式耦合太高,不符合高内聚低耦合这一性质。
观察者模式
讲解观察者模式我们是结合RecyclerView 的源码来讲解。
在recyclerView中,当我们数据改变的时候我们会调用notifyDataSetChanged()、notifyItemChanged()、notifyItemRangeChanged()等方法去刷新,下面我们进入notifyDataSetChanged()中。
public final void notifyDataSetChanged() {
mObservable.notifyChanged();
}
我们可以看到调用的是mObservable的notifyChanged(),这里的mObservable就是被观察者。
public void notifyChanged() {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
protected final ArrayList<T> mObservers = new ArrayList<T>();
其中mObservers是 一个ArrayList.T则是我们的观察者observer,notifyChanged()中则是遍历观察者,然后调用observer的onChanged()。这个稍后分析。
既然是将观察者加入到一个list中,那什么时候加入?点击mObservers可以进入public abstract class Observable<T>类中可以看到,在这里面有两个重要的方法。在registerObserver()中将observer添加进去,在unregisterObserver()将observer移除。
Observable<T>是一个抽象类,其具体实现类是:AdapterDataObservable()然后早Adapter中持有AdapterDataObservable对象,
public void registerAdapterDataObserver(@NonNull AdapterDataObserver observer) {
mObservable.registerObserver(observer);
}
public void unregisterAdapterDataObserver(@NonNull AdapterDataObserver observer) {
mObservable.unregisterObserver(observer);
}
那我们可以假设一下,当我们在设置adapter的时候,会将观察者加入到集合中,下面来验证下。
public void setAdapter(@Nullable Adapter adapter) {
// bail out if layout is frozen
setLayoutFrozen(false);
setAdapterInternal(adapter, false, true);
processDataSetCompletelyChanged(false);
requestLayout();
}
private void setAdapterInternal(@Nullable Adapter adapter, boolean compatibleWithPrevious,
boolean removeAndRecycleViews) {
if (mAdapter != null) {
mAdapter.unregisterAdapterDataObserver(mObserver);
mAdapter.onDetachedFromRecyclerView(this);
}
final Adapter oldAdapter = mAdapter;
mAdapter = adapter;
if (adapter != null) {
adapter.registerAdapterDataObserver(mObserver);
adapter.onAttachedToRecyclerView(this);
}
......
}
可以看到,setAdapter中,会调用setAdapterInternal(),在setAdapterInternal会先判断mAdapter是否不为null的时候,先移除,然后将我们赋值的adapter重新进行赋值,并进行注册添加到集合中。
private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
private class RecyclerViewDataObserver extends AdapterDataObserver {
RecyclerViewDataObserver() {
}
@Override
public void onChanged() {
assertNotInLayoutOrScroll(null);
mState.mStructureChanged = true;
processDataSetCompletelyChanged(true);
if (!mAdapterHelper.hasPendingUpdates()) {
requestLayout();
}
}
......
}
mObserver的具体实现类是RecyclerViewDataObserver,上面我们分析到notifyChanged()中则是遍历观察者,然后调用observer的onChanged(),其实就是调用RecyclerViewDataObserver的onChanged()。在onChanged中,会调用requestLayout()进行重绘。
所以整个流程就是,在我们setAdapter的时候,会进行注册将观察者加入到集合中,然后我们数据改变的时候,调用notifyDataSetChanged,则会进行对观察者进行遍历,然后调用onChanged方法,里面会调用RequestLayout(),进行重新测量,摆放和重绘。
网友评论