1.观察者模式模式简介

定义:

观察者模式(又被称为发布-订阅(Publish/Subscribe)模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。

观察者模式结构图:

  • Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
  • ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
  • Observer:抽象观察者,是观察者者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。
  • ConcrereObserver:具体观察者,是实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。

2.观察者模式简单实现

观察者模式这种发布-订阅的形式我们可以拿微信公众号来举例,假设微信用户就是观察者,微信公众号是被观察者,有多个的微信用户关注了程序猿这个公众号,当这个公众号更新时就会通知这些订阅的微信用户。好了我们来看看用代码如何实现:

抽象观察者(Observer)

里面定义了一个更新的方法:

  1. public interface Observer {
  2. public void update(String message);
  3. }

具体观察者(ConcrereObserver)

微信用户是观察者,里面实现了更新的方法:

  1. public class WeixinUser implements Observer {
  2. // 微信用户名
  3. private String name;
  4. public WeixinUser(String name) {
  5. this.name = name;
  6. }
  7. @Override
  8. public void update(String message) {
  9. System.out.println(name + "-" + message);
  10. }
  11.  
  12. }

抽象被观察者(Subject)

抽象主题,提供了attach、detach、notify三个方法:

  1. public interface Subject {
  2. /**
  3. * 增加订阅者
  4. * @param observer
  5. */
  6. public void attach(Observer observer);
  7. /**
  8. * 删除订阅者
  9. * @param observer
  10. */
  11. public void detach(Observer observer);
  12. /**
  13. * 通知订阅者更新消息
  14. */
  15. public void notify(String message);
  16. }

具体被观察者(ConcreteSubject)

微信公众号是具体主题(具体被观察者),里面存储了订阅该公众号的微信用户,并实现了抽象主题中的方法:

  1. public class SubscriptionSubject implements Subject {
  2. //储存订阅公众号的微信用户
  3. private List<Observer> weixinUserlist = new ArrayList<Observer>();
  4.  
  5. @Override
  6. public void attach(Observer observer) {
  7. weixinUserlist.add(observer);
  8. }
  9.  
  10. @Override
  11. public void detach(Observer observer) {
  12. weixinUserlist.remove(observer);
  13. }
  14.  
  15. @Override
  16. public void notify(String message) {
  17. for (Observer observer : weixinUserlist) {
  18. observer.update(message);
  19. }
  20. }
  21. }

客户端调用

  1. public class Client {
  2. public static void main(String[] args) {
  3. SubscriptionSubject mSubscriptionSubject=new SubscriptionSubject();
  4. //创建微信用户
  5. WeixinUser user1=new WeixinUser("杨影枫");
  6. WeixinUser user2=new WeixinUser("月眉儿");
  7. WeixinUser user3=new WeixinUser("紫轩");
  8. //订阅公众号
  9. mSubscriptionSubject.attach(user1);
  10. mSubscriptionSubject.attach(user2);
  11. mSubscriptionSubject.attach(user3);
  12. //公众号更新发出消息给订阅的微信用户
  13. mSubscriptionSubject.notify("刘望舒的专栏更新了");
  14. }
  15. }
  16.  
  17. 结果:
  18.  
  19. 杨影枫-刘望舒的专栏更新了
  20. 月眉儿-刘望舒的专栏更新了
  21. 紫轩-刘望舒的专栏更新了

3.使用观察者模式的场景和优缺点

使用场景

  • 关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系。
  • 事件多级触发场景。
  • 跨系统的消息交换场景,如消息队列、事件总线的处理机制。

优点

解除耦合,让耦合的双方都依赖于抽象,从而使得各自的变换都不会影响另一边的变换。

缺点

在应用观察者模式时需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂,而且在Java中消息的通知一般是顺序执行,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步实现。

4.Android中的观察者模式

android源码中也有很多使用了观察者模式,比如OnClickListener、ContentObserver、android.database.Observable等;还有组件通讯库RxJava、RxAndroid、EventBus;在这里将拿我们最常用的Adapter的notifyDataSetChanged()方法来举例:
当我们用ListView的时候,数据发生变化的时候我们都会调用Adapter的notifyDataSetChanged()方法,这个方法定义在BaseAdaper中,我们来看看BaseAdaper的部分源码:

  1. public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
  2.  
  3. //数据集观察者
  4. private final DataSetObservable mDataSetObservable = new DataSetObservable();
  5.  
  6. public boolean hasStableIds() {
  7. return false;
  8. }
  9.  
  10. public void registerDataSetObserver(DataSetObserver observer) {
  11. mDataSetObservable.registerObserver(observer);
  12. }
  13.  
  14. public void unregisterDataSetObserver(DataSetObserver observer) {
  15. mDataSetObservable.unregisterObserver(observer);
  16. }
  17.  
  18. /**
  19. * 当数据集变化时,通知所有观察者
  20. */
  21. public void notifyDataSetChanged() {
  22. mDataSetObservable.notifyChanged();
  23. }

很明显BaseAdapter用的是观察者模式,BaseAdapter是具体被观察者,接下来看看mDataSetObservable.notifyChanged():

  1. public class DataSetObservable extends Observable<DataSetObserver> {
  2. public void notifyChanged() {
  3. synchronized(mObservers) {
  4. for (int i = mObservers.size() - ; i >= ; i--) {
  5. mObservers.get(i).onChanged();
  6. }
  7. }
  8. }

我们看到了mObservers,这就是观察者的集合,这些观察者是在ListView通过setAdaper()设置Adaper时产生的:

  1. @Override
  2. public void setAdapter(ListAdapter adapter) {
  3. if (mAdapter != null && mDataSetObserver != null) {
  4. mAdapter.unregisterDataSetObserver(mDataSetObserver);
  5. }
  6. ...
  7. super.setAdapter(adapter);
  8.  
  9. if (mAdapter != null) {
  10. mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
  11. mOldItemCount = mItemCount;
  12. mItemCount = mAdapter.getCount();
  13. checkFocus();
  14. //创建数据观察者
  15. mDataSetObserver = new AdapterDataSetObserver();
  16. //注册观察者
  17. mAdapter.registerDataSetObserver(mDataSetObserver);
  18.  
  19. ...
  20. }

接下来看看观察者AdapterDataSetObserver中处理了什么:

  1. class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
  2. @Override
  3. public void onChanged() {
  4. super.onChanged();
  5. if (mFastScroller != null) {
  6. mFastScroller.onSectionsChanged();
  7. }
  8. }
  9.  
  10. @Override
  11. public void onInvalidated() {
  12. super.onInvalidated();
  13. if (mFastScroller != null) {
  14. mFastScroller.onSectionsChanged();
  15. }
  16. }
  17. }

从上面的代码看不出什么,再看看AdapterDataSetObserver的父类AdapterView的AdapterDataSetObserver:

  1. class AdapterDataSetObserver extends DataSetObserver {
  2. private Parcelable mInstanceState = null;
  3. @Override
  4. public void onChanged() {
  5. mDataChanged = true;
  6. mOldItemCount = mItemCount;
  7. mItemCount = getAdapter().getCount();
  8. if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
  9. && mOldItemCount == && mItemCount > ) {
  10. AdapterView.this.onRestoreInstanceState(mInstanceState);
  11. mInstanceState = null;
  12. } else {
  13. rememberSyncState();
  14. }
  15. checkFocus();
  16. //重新布局
  17. requestLayout();
  18. }
  19.  
  20. ...
  21.  
  22. public void clearSavedState() {
  23. mInstanceState = null;
  24. }
  25. }

我们看到在onChanged()方法中调用了requestLayout()方法来重新进行布局。好了,看到这里我们都明白了,当ListView的数据发生变化时,我们调用Adapter的notifyDataSetChanged()方法,这个方法又会调用观察者们(AdapterDataSetObserver)的onChanged()方法,onChanged()方法又会调用requestLayout()方法来重新进行布局。

5.回调函数和观察者模式的区别?
“标准答案”:观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。观察者模式完美的将观察者和被观察的对象分离开,一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动刷新。
回调函数其实也算是一种观察者模式的实现方式,回调函数实现的观察者和被观察者往往是一对一的依赖关系。
所以最明显的区别是观察者模式是一种设计思路,而回调函数式一种具体的实现方式;另一明显区别是一对多还是多对多的依赖关系方面。

Android 手机卫士15--程序锁的更多相关文章

  1. [android] 手机卫士设备管理权限锁屏

    设备管理员 Device Admin 获取DevicePolicyManager对象,通过getSystemService(DEVICE_POLICY_MANAGER),设备策略管理器 调用Devic ...

  2. [android] 手机卫士应用程序更新和签名

    弹出升级对话框 获取AlertDialog.Builder对象,通过new出来 调用Builder对象的setTitle()方法,参数:文本 调用Builder对象的setMessage()方法,参数 ...

  3. Android 手机卫士--弹出对话框

    在<Android 手机卫士--解析json与消息机制发送不同类型消息>一文中,消息机制发送不同类型的信息还没有完全实现,在出现异常的时候,应该弹出吐司提示异常,代码如下: private ...

  4. android手机卫士、3D指南针、动画精选、仿bilibli客户端、身份证银行卡识别等源码

    Android精选源码 android身份证.银行卡号扫描源码 android仿bilibili客户端 android一款3D 指南针 源码 android手机卫士app源码 android提醒应用, ...

  5. Android 手机卫士--参照文档编写选择器

    本文来实现<Android 手机卫士--导航界面1的布局编写>中的图片选择器部分的代码. 本文地址:http://www.cnblogs.com/wuyudong/p/5944356.ht ...

  6. Android 手机卫士--设置界面&功能列表界面跳转逻辑处理

    在<Android 手机卫士--md5加密过程>中已经实现了加密类,这里接着实现手机防盗功能 本文地址:http://www.cnblogs.com/wuyudong/p/5941959. ...

  7. Android 手机卫士--确认密码对话框编写

    本文接着实现“确认密码”功能,也即是用户以前设置过密码,现在只需要输入确认密码 本文地址:http://www.cnblogs.com/wuyudong/p/5940718.html,转载请注明出处. ...

  8. Android 手机卫士--签名文件说明&包名说明

    在<Android 手机卫士--打包生成apk维护到服务器>一文中,实现了新版本的apk到服务器,当打开客户端apk的时候,发现有新版本,提示更新.还实现了利用xutils工具实现了从服务 ...

  9. Android项目实战--手机卫士24--程序锁的实现以及逻辑

    昨天我们已经把程序全部读取出来,显示到界面上了,那今天,我们就来讲一下那个程序锁的实现啦.其实也很简单啦,我们主要就是把用户要锁定的程序放到一个表里面,然后就监听手机里面的任务栈,如果发现任务栈里面出 ...

随机推荐

  1. 为Eclipse添加Java和Android SDK源代码

    1.添加jdk源码进入eclipse Ctrl + Click -->Attached Source 路径:D:\Program Files\Java\jdk1.8.0_45\src.zip 2 ...

  2. [转载]堆排序(HeapSort) Java实现

    堆排序的思想是利用数据结构--堆.具体的实现细节: 1. 构建一个最大堆.对于给定的包含有n个元素的数组A[n],构建一个最大堆(最大堆的特性是,某个节点的值最多和其父节点的值一样大.这样,堆中的最大 ...

  3. Web Uploader文件上传插件

    http://www.jq22.com/jquery-info2665   插件描述:WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主,FLASH为辅的现 ...

  4. NPM 与 left-pad 事件:我们是不是早已忘记该如何好好地编程?

    转自:http://www.techug.com/npm-left-pad-have-we-forgotten-how-to-program 开发者朋友们,我们该谈一谈这个问题了.你们应该知道本周的  ...

  5. [转]mac 10.9.4下配置apache

    出处:http://yjmyzz.cnblogs.com mac 10.9.x已经自带了apache,可按如下步骤开启: 1.启动 sudo apachectl start 启动后,访问 http:/ ...

  6. mysql 慢查询日志记录

    环境: 操作系统: CentOS  6.5 数据库:    mysql-5.1.73 1.查看当前慢查询配置 mysql> show variables like 'slow%'; +----- ...

  7. ERB预处理ruby代码

    cucumber.yml 文件可以用erb预处理,这样允许你在cucumber.yml文件中使用ruby代码生成值.所以如果你有几个配置要用相同值时,你可以这样写 # config/cucumber. ...

  8. NGUI 动态添加控件

    本文链接地址: Unity3D NGUI动态创建按钮 本例仅以熟悉NGUI组件功能为目的,想快捷简便的创建按钮或其它游戏物体请参考 “Unity3D 动态实例化Prefab” 以动态创建服务器列表为例 ...

  9. 创意设计展示:折叠效果在移动 App 中的应用

    在今天在移动 App 界面设计中,你可以看到不同创意类型的视觉效果.特别是在 Dribbble 上面,有有很多应用程序的 UI 概念设计,让你惊叹.当然,他们大多只是作为一个概念设计,可能永远也不会成 ...

  10. 一个简单的3DTouch、Peek和Pop手势Demo,附github地址

    参考文章:http://www.jianshu.com/p/74fe6cbc542b 下载链接:https://github.com/banchichen/3DTouch-PeekAndPopGest ...