观察者模式是一种使用效率非常高的模式,我们在源码中也可以初创看到他的身影,如在我们经常使用的listview中的adapter的notifydatasetchanged方法就是观察者模式的一种实现方法,以及现在非常流行的RxJava中的实现过程也是基于这种模式的,下面来学习一下观察者模式。
定义
观察这模式是定义对象间一种一对多的依赖关系,使得每当一个状态改变时,所有依赖与他的对象都会得到通知,并且得到相应的改变,在观察者模式中,主要有两个名字定义:观察者和被观察者,当被观察者的状态发生改变时,观察者会得到通知并且进行相对应的行为变化。
举个例子:被观察者好比技术总监,而观察者就像程序猿一样(= ̄ω ̄=),当总监得到相关任务后,会发送一个邮件给程序猿,程序猿根据这封邮件进行相关的工作,在这两者之间,通信的媒介在于邮件,在代码中相对应于一种接口回调的思想。
使用场景
- 关联行为场景,需要注意的是,关联行为是可以拆分的,而不是组合的关系
- 事件多级触发场景
- 跨系统的消息交换场景,如消息队列,ADIL之间的观察者模式,以及事件总线的处理(EventBus)机制。
观察者模式的UML图
Subject:只是一个抽象主题类,相当于被观察者的角色,它把所有的观察者对象的引用保存在一个集合里,每个主题可以拥有任意数量的观察者,抽象主题提供一个接口,可以增加或者删除对象。
ConcreteObject:具体的被观察者的实现类,该角色将有关状态存入具体观察者对象,在其内部的状态发生改变时候,发送消息给所有观察者。
Observer:抽象观察者,它定义一个更新接口,是的在得到主题更改的通知后更新自己
ConcreteObserver:具体观察者实现类,该角色实现抽象观察者所定义的更新接口,以便在主题发生改变时更新自己的状态。
简单实现
//定义一个被观察者接口
public interface MyObservser {
void register(MyListenser listenser);
void unregister(MyListenser listenser);
}
------
//总监实现类,即被观察者具体实现
public class ZongJian implements MyObservser {
private List<MyListenser> list=new ArrayList<>();
@Override
public void register(MyListenser listenser) {
if (!list.contains(listenser)) {
list.add(listenser);
}
}
@Override
public void unregister(MyListenser listenser) {
if (list.contains(listenser)) {
list.remove(listenser);
}
}
public void setData(String string){
System.out.println("总监发布任务:"+string);
for(int i=0;i<list.size();i++){
list.get(i).getData(string);
}
}
}
-----
//观察者接口
public interface MyListenser {
void getData(String str);
}
----
//程序猿具体实现类
public class ChengXuYuan {
private MyListenser listenser;
public void setListener(MyListenser listenser){
this.listenser=listenser;
}
public MyListenser getListener(){
return listenser;
}
}
-----
public class Main {
public static void main(String[] args) {
ZongJian observser=new ZongJian();
ChengXuYuan chengXuYuan=new ChengXuYuan();
chengXuYuan.setListener(new MyListenser() {
@Override
public void getData(String str) {
System.out.println("程序猿1要开始工作了,任务是:"+str);
}
});
ChengXuYuan chengXuYuan2=new ChengXuYuan();
chengXuYuan2.setListener(new MyListenser() {
@Override
public void getData(String str) {
System.out.println("程序猿2要开始工作了,任务是:"+str);
}
});
observser.register(chengXuYuan.getListener());
observser.register(chengXuYuan2.getListener());
observser.setData("总监我给的任务是:没有蛀牙!");
}
}
结果如下图所示:
可以看到,当总监发出任务后,程序猿们都可以接受到这个消息,我们就完成了一个简单的观察者模式的实现。
notifyDataChange源码分析
下面我们来研究研究adapter中的notifyDataChange的实现过程,首先我们看看Adapter这个接口,所有的adapter子类都需要实现这个接口,在adapter源码中,有两个如下的方法:
/**
* Register an observer that is called when changes happen to the data used by this adapter.
*
* @param observer the object that gets notified when the data set changes.
*/
void registerDataSetObserver(DataSetObserver observer);
/**
* Unregister an observer that has previously been registered with this
* adapter via {@link #registerDataSetObserver}.
*
* @param observer the object to unregister.
*/
void unregisterDataSetObserver(DataSetObserver observer);
/**
* How many items are in the data set represented by this Adapter.
*
* @return Count of items.
*/
我们可以看到,这就是相当于我们上面讲的被观察者注册与取消注册接口的过程,我们在点到BaseAdapter中,查看代码如下:
private final DataSetObservable mDataSetObservable = new DataSetObservable();
public boolean hasStableIds() {
return false;
}
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
public void unregisterDataSetObserver(DataSetObserver observer) {
mDataSetObservable.unregisterObserver(observer);
}
/**
* Notifies the attached observers that the underlying data has been changed
* and any View reflecting the data set should refresh itself.
*/
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
/**
* Notifies the attached observers that the underlying data is no longer valid
* or available. Once invoked this adapter is no longer valid and should
* not report further data set changes.
*/
public void notifyDataSetInvalidated() {
mDataSetObservable.notifyInvalidated();
}
DataSetObservable是被观察者模型,BaseAdapter中notifyDataSetChanged在实质上是调用了mDataSetObservable.notifyChanged()方法,继续点进去,我们看看DataSetObservable这个类,发现他是继承于Observable这个官方接口的,此官方接口主要提供我们简单实现观察者模式,简化我们要书写的代码量的过程,与之相对应的就是Observer接口,notifyChange方法:
public void notifyChanged() {
synchronized(mObservers) {
// since onChanged() is implemented by the app, it could do anything, including
// removing itself from {@link mObservers} - and that could cause problems if
// an iterator is used on the ArrayList {@link mObservers}.
// to avoid such problems, just march thru the list in the reverse order.
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
看到最终会调用观察者的onChange方法,那这个观察者到底是谁呢?就是DataSetObserver,那么这两个值到底是什么时候会被赋值的呢,这就要看listView中的setAdapter实现过程了:
public void setAdapter(ListAdapter adapter) {
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
resetList();
mRecycler.clear();
if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
} else {
mAdapter = adapter;
}
mOldSelectedPosition = INVALID_POSITION;
mOldSelectedRowId = INVALID_ROW_ID;
// AbsListView#setAdapter will update choice mode states.
super.setAdapter(adapter);
if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
checkFocus();
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());
int position;
if (mStackFromBottom) {
position = lookForSelectablePosition(mItemCount - 1, false);
} else {
position = lookForSelectablePosition(0, true);
}
setSelectedPositionInt(position);
setNextSelectedPositionInt(position);
if (mItemCount == 0) {
// Nothing selected
checkSelectionChanged();
}
} else {
mAreAllItemsSelectable = true;
checkFocus();
// Nothing selected
checkSelectionChanged();
}
requestLayout();
}
我们可以看到:
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
由于源码不允许继续跟进,所以这里进行一个猜测Listview中进行了观察者与被观察者数据的整合,最终实现了当我们更新数据的时候实现了视图上面的更新操作。