摘要:观察者模式是一种行为类模式,它定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。 观察者模式是一个使用率非常高的模式,它最常用在GUI系统、订阅–发布系统。因为这个模式的一个重要作用就是解耦,将被观察者和观察者解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。比如安卓的开源项目EventBus、Otto、AndroidEventBus等事件总线类的和RxJava响应式编程其核心都是使用观察者模式。 希望本文对大家学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<observer> list = new ArrayList<observer>();//储存订阅者 @Override public void push(String message) { for(Observer observer:list){ observer.update(message); } } @Override public void register(Observer observer) { list.add(observer); } }</observer></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<datasetobserver> { /** * 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(); } } } }</datasetobserver> 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<listadapter>.AdapterDataSetObserver { @Override public void onChanged() { super.onChanged(); if (mFastScroll != null) { mFastScroll.onSectionsChanged(); } } @Override public void onInvalidated() { super.onInvalidated(); if (mFastScroll != null) { mFastScroll.onSectionsChanged(); } } }</listadapter> 从代码中可看出,它继承于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()方法重新进行布局,更新用户界面。
本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之Android频道!
您输入的评论内容中包含违禁敏感词
我知道了
请输入正确的手机号码
请输入正确的验证码
您今天的短信下发次数太多了,明天再试试吧!
我们会在第一时间安排职业规划师联系您!
您也可以联系我们的职业规划师咨询:
版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号