观察者模式及在Android源码中的应用
观察者模式
观察者模式是一种行为类模式,它定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
观察者模式是一个使用率非常高的模式,它最常用在gui、订阅–发布系统。因为这个模式的一个重要作用就是解耦,将被观察者和观察者解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。比如安卓的开源项目eventbus、otto、androideventbus等事件总线类的和rxjava响应式其核心都是使用观察者模式。
使用场景
- 关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系。
- 事件多级触发场景。
- 跨系统的消息交换场景,如消息队列、事件总线的处理机制。
栗子
这里举一个追剧的例子,平常为了不错过最新的电视剧我们会订阅或关注这个电视剧,当电视剧更新后会第一时间推送给我们,下来就简单实现一下。
抽象观察者类
/** * 抽象观察者类,为所有具体观察者定义一个接口,在得到通知时更新自己 */ public interface observer { /** * 有更新 * * @param message 消息 */ public void update(string message); }
抽象被观察者类
/** * 抽象被观察者类 */ public interface observable { /** * 推送消息 * * @param message 内容 */ void push(string message); /** * 订阅 * * @param observer 订阅者 */ void register(observer observer); }
具体的观察者类
/** * 具体的观察者类,也就是订阅者 */ public class user implements observer { // 订阅者的名字 private string name; public user(string name) { this.name = name; } @override public void update(string message) { system.out.println(name + "," + message + "更新了!"); } }
具体的被观察者类
/** * 具体的被观察者类,也就是订阅的节目 */ public class teleplay implements observable{ private list list = new arraylist();//储存订阅者 @override public void push(string message) { for(observer observer:list){ observer.update(message); } } @override public void register(observer observer) { list.add(observer); } }
实现
public class client { public static void main(string[] args) { //被观察者,这里就是用户订阅的电视剧 teleplay teleplay = new teleplay(); //观察者,这里就是订阅用户 user user1 = new user("小明"); user user2 = new user("小光"); user user3 = new user("小兰"); //订阅 teleplay.register(user1); teleplay.register(user2); teleplay.register(user3); //推送新消息 teleplay.push("xxx电视剧"); } }
结果
小明,xxx电视剧更新了! 小光,xxx电视剧更新了! 小兰,xxx电视剧更新了!
由上面的代码可以看出实现了一对多的消息推送,推送消息都是依赖observer和observable这些抽象类,而user和teleplay完全没有耦合,保证了订阅系统的灵活性和可扩展性。
源码中的应用">android源码中的应用
在以前,我们最常用到的控件就是listview了,而listview最重要的一个点就是adapter,在我们往listview添加数据后,我们都会调用一个方法: notifydatasetchanged(), 这个方法就是用到了我们所说的观察者模式。
跟进这个方法notifydatasetchanged方法,这个方法定义在baseadapter中,代码如下:
public abstract class baseadapter implements listadapter, spinneradapter { // 数据集观察者 private final datasetobservable mdatasetobservable = new datasetobservable(); // 代码省略 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(); } }
可以发现,当数据发生变化时候,notifydatasetchanged中会调用mdatasetobservable.notifychanged()方法
public class datasetobservable extends observable { /** * invokes onchanged on each observer. called when the data set being observed has * changed, and which when read contains the new state of the data. */ public void notifychanged() { synchronized(mobservers) { // 调用所有观察者的onchanged方式 for (int i = mobservers.size() - 1; i >= 0; i--) { mobservers.get(i).onchanged(); } } } }
mdatasetobservable.notifychanged()中遍历所有观察者,并且调用它们的onchanged方法。
那么这些观察者是从哪里来的呢?首先listview通过setadapter方法来设置adapter
@override public void setadapter(listadapter adapter) { // 如果已经有了一个adapter,那么先注销该adapter对应的观察者 if (madapter != null && mdatasetobserver != null) { madapter.unregisterdatasetobserver(mdatasetobserver); } // 代码省略 if (mheaderviewinfos.size() > 0|| mfooterviewinfos.size() > 0) { madapter = wrapheaderlistadapterinternal(mheaderviewinfos, mfooterviewinfos, adapter); } else { madapter = adapter; } super.setadapter(adapter); if (madapter != null) { mareallitemsselectable = madapter.areallitemsenabled(); molditemcount = mitemcount; // 获取数据的数量 mitemcount = madapter.getcount(); checkfocus(); // 注意这里 : 创建一个数据集观察者 mdatasetobserver = new adapterdatasetobserver(); // 将这个观察者注册到adapter中,实际上是注册到datasetobservable中 madapter.registerdatasetobserver(mdatasetobserver); // 代码省略 } else { // 代码省略 } requestlayout(); }
在设置adapter时会构建一个adapterdatasetobserver,最后将这个观察者注册到adapter中,这样我们的被观察者、观察者都有了。
adapterdatasetobserver定义在listview的父类abslistview中,代码如下 :
class adapterdatasetobserver extends adapterview.adapterdatasetobserver { @override public void onchanged() { super.onchanged(); if (mfastscroll != null) { mfastscroll.onsectionschanged(); } } @override public void oninvalidated() { super.oninvalidated(); if (mfastscroll != null) { mfastscroll.onsectionschanged(); } } }
从代码中可看出,它继承于adapterview的内部类adapterdatasetobserver,代码如下:
class adapterdatasetobserver extends datasetobserver { private parcelable minstancestate = null; // 调用adapter的notifydatasetchanged的时候会调用所有观察者的onchanged方法,核心实现就在这里 @override public void onchanged() { mdatachanged = true; molditemcount = mitemcount; // 获取adapter中数据的数量 mitemcount = getadapter().getcount(); // detect the case where a cursor that was previously invalidated has // been repopulated with new data. if (adapterview.this.getadapter().hasstableids() && minstancestate != null && molditemcount == 0 && mitemcount > 0) { adapterview.this.onrestoreinstancestate(minstancestate); minstancestate = null; } else { remembersyncstate(); } checkfocus(); // 重新布局listview、gridview等adapterview组件 requestlayout(); } // 代码省略 public void clearsavedstate() { minstancestate = null; } }
可见该类确实继承于观察者抽象类datasetobserver。
当listview的数据发生变化时,调用adapter的notifydatasetchanged函数,这个函数又会调用datasetobservable的notifychanged函数,这个函数会调用所有观察者 (adapterdatasetobserver) 的onchanged方法。这就是一个观察者模式!
**总结:**adapterview中有一个内部类adapterdatasetobserver,在listview设置adapter时会构建一个adapterdatasetobserver,并且注册到adapter中,这个就是一个观察者。而adapter中包含一个数据集可观察者datasetobservable,在数据数量发生变更时开发者手动调用adapter.notifydatasetchanged,而notifydatasetchanged实际上会调用datasetobservable的notifychanged函数,该函数会遍历所有观察者的onchanged函数。在adapterdatasetobserver的onchanged函数中会获取adapter中数据集的新数量,然后调用listview的requestlayout()方法重新进行布局,更新用户界面。