Android开发模式之观察者模式
Posted 小树熊
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android开发模式之观察者模式相关的知识,希望对你有一定的参考价值。
目录
一、定义
1.观察者模式
定义对象一种一对多的依赖关系,使得当一个对象改变状态的时候,所有依赖它的对象都会得到通知并自动更新。观察者模式是一种使用频率非常高的设计模式,最常用的地方就是订阅-发布系统。
2.UML类图
3.观察者模式中的角色
- Subject(抽象主题): 又叫抽象被观察者,把所有的观察者对象引用保存到一个集合里,每个主题都可以由任何数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
- ConcreteSubject(具体主题):又叫具体被观察者,将有关的状态存入具体观察者对象,在内部状态改变时,给所有的登记过的观察者发出通知。
- Observer(抽象观察者):为所有的观察者定义一个接口,在收到主题通知时进行更新。
- ConcrereObserver(具体观察者):实现抽象观察者定义的更新接口,在得知主题更改通知时更新自身的状态。
二、使用场景
- 当一个对象的数据更新了需要通知其他对象,但是又不希望和被通知的对象形成耦合;
- 当一个对象数据更新了,这个对象需要让其他对象也更新数据但是不知道有多少个对象需要更新。
- 跨系统的消息交换场景,如消息队列、事件总线的处理机制。
三、简单实现
1.被观察者抽象类
public interface Observable<T>
//绑定观察者
void addObserver(Observer observer);
//移除观察者
void removeObserver(Observer observer);
//被观察者发出了改变
void notifyObservers(String message);
2.被观察者实现类
public class ServerObservable implements Observable<String>
private List<Observer> observers = new ArrayList<>();
private String message;
@Override
public void addObserver(Observer observer)
//添加观察者
observers.add(observer);
@Override
public void removeObserver(Observer observer)
//删除观察者
observers.remove(observer);
@Override
public void notifyObservers(String message)
//通知观察者有更新的数据
for (Observer observer:observers)
observer.update(message);
3.观察者抽象类
public interface Observer
//被观察者改变接受消息,做响应
void update(String message);
4.观察者实现类
public class User implements Observer
String info;
public User(String s)
info = s;
//用于更新数据
@Override
public void update(String message)
System.out.println(info + message);
5.测试代码
//创建观察者
User user1 = new User("第一位观察者");
User user2 = new User("第二位观察者");
User user3 = new User("第三位观察者");
//创建被观察者
ServerObservable observable = new ServerObservable();
observable.add(user1);
observable.add(user2);
observable.add(user3);
//通知更新
observable.notifyObservers("需要更新数据");
运行,从结果看当被观察者发出更新数据的通知后所有注册的观察者都会立刻响应到:
四、观察者模式在java.util
包中的应用
观察者模式在JDK中就有典型应用,比如java.util.Observable和java.util.Observer类。在使用时,被观察者需要继承java.util.Observable类,观察者需要实现java.util.Observer接口。下面通过一个简单的Demo看一下如何使用JDK中的观察者模式,UML图如下。
观察者Student的代码
import java.util.Observable;
import java.util.Observer;
public class Student implements Observer
private String name;
public Student(String name)
this.name = name;
@Override
public void update(Observable o, Object arg)
System.out.println(name + (String)arg);
被观察者Teacher的代码
import java.util.Observable;
public class Teacher extends Observable
public void publishMessage(String message)
// mark as value changed
setChanged();
// trigger notification
notifyObservers(message);
输出结果如下:
JDK中的Observable基类和Observer接口为我们的简单使用观察者模式提供了方便,但它有一个非常明显的缺点:被观察者Teacher需要继承Observable这个基类,但是如果Teacher已经继承其他的类就不能同时再继承Observable类。解决这个矛盾的思路有两种:一是自定义观察者模式,将add、delete、notify等方法写进Teacher类;二是使用代理模式,在Teacher中维护一个Observable类的对象,并且实现同名的方法。
五、观察者模式在Button中的应用
在android中我们遇到的最常见的观察者模式就是各种控件的监听。
//注册观察者
button.setOnClickListener(new View.OnClickListener()
//观察者实现类
@Override
public void onClick(View view)
);
在这段代码中,Button就是主题也就是被观察者,通过new出的View.OnClickListenerd对象就是具体的观察者;onClickListener就是抽象的观察者接口;最后通过setOnClickListener把观察者注册到被观察者中,源码如下:
//在单击视图时调用的回调的接口定义。
public interface OnClickListener
//在单击视图时调用。
void onClick(View v);
//注册观察者
public void setOnClickListener(@Nullable OnClickListener l)
if (!isClickable())
setClickable(true);
getListenerInfo().mOnClickListener = l;
//执行点击事件
public boolean performClick()
// We still need to call this method to handle the cases where performClick() was called
// externally, instead of through performClickInternal()
notifyAutofillManagerOnClick();
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null)
playSoundEffect(SoundEffectConstants.CLICK);
//调用观察者执行onClick
li.mOnClickListener.onClick(this);
result = true;
else
result = false;
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
notifyEnterOrExitForAutoFillIfNeeded(true);
return result;
六、观察者模式在ListView中的应用
在使用ListView时,如果数据改变了需要调用setDataSetChanged()方法来通知ListView更新UI。换一种说法,ListView的UI是观察者,ListView对应的adapter中的数据就是被观察者。它的实现方式就是注册一个观察者到adapter中,以实现监听adapter的数据变化的目的。
首先看一下listView.setAdapter(adapter);在这个方法中ListView会创建一个
AdapterDataSetObserver类的观察者并且调用adapter的registerDataSetObserver()。
@Override
public void setAdapter(ListAdapter adapter)
//首先确保移除了所有的观察者
if (mAdapter != null && mDataSetObserver != null)
mAdapter.unregisterDataSetObserver(mDataSetObserver);
.....
if (mAdapter != null)
....
//创建AdapterDataSetObserver观察者
mDataSetObserver = new AdapterDataSetObserver();
//将观察者注册到adapter
mAdapter.registerDataSetObserver(mDataSetObserver);
....
继续看Adapter的registerDataSetObserver方法,发现最终的结果就是把观察者注册到DataSetObservable这个发布者里。
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter
@UnsupportedAppUsage
//被观察者
private final DataSetObservable mDataSetObservable = new DataSetObservable();
public void registerDataSetObserver(DataSetObserver observer)
//注册观察者
mDataSetObservable.registerObserver(observer);
public void unregisterDataSetObserver(DataSetObserver observer)
//移除
mDataSetObservable.unregisterObserver(observer);
...
DataSetObservable继承于android.database.Observable,在DataSetObservable中实现了notifyChanged()方法。
package android.database;
public class DataSetObservable extends Observable<DataSetObserver>
public void notifyChanged()
synchronized(mObservers)
//通知所有的观察者,调用onChanged
for (int i = mObservers.size() - 1; i >= 0; i--)
mObservers.get(i).onChanged();
public void notifyInvalidated()
synchronized (mObservers)
for (int i = mObservers.size() - 1; i >= 0; i--)
mObservers.get(i).onInvalidated();
现在我们看一下adapter.notifyDataSetChanged(),其实就是调用了被观察者的notifyChanged()通知观察者onChanged刷新UI。
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter
public void notifyDataSetChanged()
mDataSetObservable.notifyChanged();
七、观察者模式的优缺点
观察者模式的优点
解除观察值与主题之间的耦合,让耦合的双方都依赖于抽象,而不是依赖于具体。从而使各自的变化不会影响另一边的变化。
易于扩展,对于同一主题新增观察者时无需修改原有的代码。
观察者模式的缺点
1、依赖关系并未完全解除,抽象主题依然依赖抽象观察者。
2、使用观察者模式需要考虑开发效率和运行效率的问题,程序中包含一个被观察者、多个观察者,开发、调试等会比较复杂,而且java中消息的通知是顺序执行的,如果一个观察者卡顿,会影响整体的执行效率。
3、可能会引起多余的数据通知。
参考资源
Android 源码中的观察者模式 - 掘金 (juejin.cn)
Android设计模式03-观察者模式 - 简书 (jianshu.com)
设计模式之GOF23观察者模式
观察者模式Observer
广播机制
场景:多个观察者--被通知改变
CS的时候,人物移动坐标变化,更新每个人地图上的坐标
核心:当目标对象(Subject)的状态值改变时,需要及时告知所有观察者(Observer),使他们做出响应
开发中常见的场景:
-聊天室程序中,服务器转发给所有客户端
-网络游戏(多人联机)中,服务器将各个玩家的状态分发
-邮件订阅
-Servlet中,监听机制
-Android中的广播机制
-JDK的AWT中事件处理模型,基于观察者模式的委派事件模型(Delegation Event Model)(事件源------目标对象,事件监听器------观察者)
-京东商城中,群发商品打折信息
public class Subject
//被观察者队伍
protected List<Observer> observers=new ArrayList<>();
public void addObserver(Observer obs)
observers.add(obs);
public void removeObserver(Observer obs)
observers.remove(obs);
//通知所有被观察者的状态更新
public void notifyAllObserve()
for(Observer obs:observers)
obs.update();
class ConcreteSub extends Subject
private int state;
public int getState()
return state;
public void setState(int state)
this.state = state;
this.notifyAllObserve();
void update();
class ObserverA implements Observer
private int myState;
private Subject sub;
public ObserverA(Subject sub)
this.sub = sub;
public void update()
myState=((ConcreteSub)sub).getState();
System.out.println("状态已更新");
public static void main(String[] args)
ConcreteSub sub=new ConcreteSub();
ObserverA obs1=new ObserverA(sub);
ObserverA obs2=new ObserverA(sub);
ObserverA obs3=new ObserverA(sub);
//添加到被观察者队伍
sub.addObserver(obs1);
sub.addObserver(obs2);
sub.addObserver(obs3);
sub.setState(1000);
private int state;
public void set(int s)
state=s;
setChanged();
this.notifyObservers();
public int getState()
return state;
public void setState(int state)
this.state = state;
private int myState;
@Override
public void update(Observable o, Object arg)
myState=((ConcreteSubject)o).getState();
public int getMyState()
return myState;
public void setMyState(int myState)
this.myState = myState;
public static void main(String[] args)
ConcreteSubject cs=new ConcreteSubject();
ObserverA obs1=new ObserverA();
ObserverA obs2=new ObserverA();
ObserverA obs3=new ObserverA();
//添加至队伍中
cs.addObserver(obs1);
cs.addObserver(obs2);
cs.addObserver(obs3);
cs.set(100);
System.out.println(obs1.getMyState());
System.out.println(obs2.getMyState());
System.out.println(obs2.getMyState());
以上是关于Android开发模式之观察者模式的主要内容,如果未能解决你的问题,请参考以下文章