概述
1.观察者模式
观察者模式是关于多个对象想知道一个对象中数据变化情况的一种成熟的模式。
观察者模式中有一个称作“主题(被观察)”的对象和若干个称作“观察者”的对象,“主题”和“观察者”间是一种一对多的依赖关系,当“主题”的状态发生变化时,所有“观察者”都得到通知。
2.观察者模式的结构
观察者模式的结构中包含了四种角色:
①主题(Subject):一般情况下主题是一个接口,该接口规定了具体主题需要实现的方法,一般都有添加,删除和更新内容等基本方法,当然可以适当扩充,一般主题也叫做被观察者。
②观察者(Observer):观察者一般也是一个接口,主要方法为更新内容的接口方法。
③具体主题(ConcreteSubject):具体主题是实现主题接口类的一个实例,该实例包含有可以经常发生变化的数据。具体主题需使用一个集合,比如ArrayList,存放观察者的引用,以便数据变化时通知具体观察者。
④具体观察者(ConcreteObserver):具体观察者是实现观察者接口类的一个实例。具体观察者包含有可以存放具体主题引用的主题接口变量,以便具体观察者让具体主题将自己的引用添加到具体主题的集合中,使自己成为它的观察者,或让这个具体主题将自己从具体主题的集合中删除,使自己不再是它的观察者,所以一般观察者的方法里面都有一个删除的接口。
3.观察者模式的优点
①具体主题和具体观察者是松耦合关系。由于主题接口仅仅依赖于观察者接口,因此具体主题只是知道它的观察者是实现观察者接口的某个类的实例,但不需要知道具体是哪个类。同样,由于观察者仅仅依赖于主题接口,因此具体观察者只是知道它依赖的主题是实现主题接口的某个类的实例,但不需要知道具体是哪个类。
②观察者模式满足设计模式中的“开-闭原则”。主题接口仅仅依赖于观察者接口,这样,就可以让创建具体主题的类也仅仅是依赖于观察者接口,因此,如果增加新的实现观察者接口的类,不必修改创建具体主题的类的代码。同样,创建具体观察者的类仅仅依赖于主题接口,如果增加新的实现主题接口的类,也不必修改创建具体观察者类的代码。
4.使用场景
①当一个对象的数据更新时需要通知其他对象,但这个对象又不希望和被通知的那些对象形成紧耦合。
②当一个对象的数据更新时,这个对象需要让其他对象也各自更新自己的数据,但这个对象不知道具体有多少对象需要更新数据。
5.举例
①首先定义一个主题接口和一个观察者接口,代码如下:
//主题接口
public interface Subject {
void registerObserver(Observer o);//增加观察者
void removeObserver(Observer o);//移除观察者
void notifyObservers(float temprature);//通知观察者去更新
}
//观察者接口
public interface Observer {
void update(float temprature);//收到通知,开始更新
void remove(Subject mSubject);//不想再观察的时候删除
}
②然后定义实现主题的类和实现抽象观察者的类:
public class ConcreteSubject implements Subject {
private static final List observers;
static {
observers = new ArrayList<>();
observers.clear();
}
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
@Override
public void removeObserver(Observer o) {
if (observers.indexOf(o) >= 0) {
observers.remove(o);
}
}
@Override
public void notifyObservers(float temprature) {
if (observers.size() <= 0) {
System.out.println(“没有观察者”);
return;
}
for (final Observer o : observers) {
o.update(temprature);//循环调用每个观察者的更新方法,让所有观察者去更新
}
}
}
这个实现了所有的主题接口,方便添加、删除和通知所有的观察者。
具体的观察者如下:
public class ConcreteObserver implements Observer {
@Override
public void update(float temprature) { //主题调用更新方法时,会调用每个观察者的这个方法,让观察者更新数据
System.out.println(“温度为:” + temprature);
}
@Override
public void remove(Subject mSubject) {
mSubject.removeObserver(this);//在不需要观察的时候,调用主题的删除方法,删掉该观察者
}
}
下面测试一下代码:
public class ObserverClient {
public static void main(String[] args){
final Subject sb = new ConcreteSubject();
final Observer observer = new ConcreteObserver();
sb.registerObserver(observer);
sb.notifyObservers(21.0f);
observer.remove(sb);
sb.notifyObservers(23.0f);
}
}
测试代码很简单,先注册一个具体的观察者,然后主题发布一个温度,接着具体的观察者就收到通知了,而当具体的观察者删掉了主题之后,就再也收不到通知了,就好比你取消了杂志的订阅,便再也收不到杂志了。
这只是一个主题和一个抽象观察者,实际上不管多少个主题或者抽象观察者,都是一样的,基本原理都是"订阅-发布-更新"系统而已。
6.观察者模式在Android源码中的应用
实际在Android系统的源码中,观察者模式应用很广泛,比如Adapter中数据的更新机制,一般情况下,有数据改变的时候,大部分人喜欢一句话解决问题:
mAdapter.notifyDataSetChanged();
我们来看一下调用的过程:
public final void notifyDataSetChanged() {
mObservable.notifyChanged();
}
这个mObservable是一个AdapterDataObservable对象,代码如下:
private final AdapterDataObservable mObservable = new AdapterDataObservable();
可见又调用了mObservable的更新方法,代码如下:
public void notifyChanged() {
// 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();
}
}
这里是重点了,取出所有的观察者,然后再调用onChanged()方法, 有个问题,是什么时候添加那些具体的观察者的呢?是在setAdapter()方法里面,代码如下:
public void setAdapter(Adapter adapter) {
// bail out if layout is frozen
setLayoutFrozen(false);
setAdapterInternal(adapter, false, true);
requestLayout();
}
其中又调用了:
private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
boolean removeAndRecycleViews) {
if (mAdapter != null) {
//先移除掉旧的观察者 mAdapter.unregisterAdapterDataObserver(mObserver); mAdapter.onDetachedFromRecyclerView(this);
}
if (!compatibleWithPrevious || removeAndRecycleViews) {
removeAndRecycleViews();
}
mAdapterHelper.reset();
final Adapter oldAdapter = mAdapter;
mAdapter = adapter;
if (adapter != null) {
//这里是重点,注册 adapter.registerAdapterDataObserver(mObserver);
adapter.onAttachedToRecyclerView(this);
}
if (mLayout != null) {
mLayout.onAdapterChanged(oldAdapter, mAdapter);
}
mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
mState.mStructureChanged = true;
markKnownViewsInvalid();
}
我们可以看到注册的时候调用的是mAdapter的registerAdapterDataObserver方法,代码如下:
public void registerAdapterDataObserver(AdapterDataObserver observer) {
mObservable.registerObserver(observer);
}
继续跟踪
public void registerObserver(T observer) {
if (observer == null) {
throw new IllegalArgumentException(“The observer is null.”);
}
synchronized(mObservers) {
if (mObservers.contains(observer)) {
throw new IllegalStateException(“Observer " + observer + " is already registered.”);
}
mObservers.add(observer);
}
}
可以看到这里才是真正注册的地方。
下面分析真正刷新的地方;代码如下:
static class AdapterDataObservable extends Observable {
public boolean hasObservers() {
return !mObservers.isEmpty();
}
public void notifyChanged() {
// 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();
}
}
是取出依次更新调用了onChanged()方法,AdapterDataObserver是一个抽象类,那么真正的实现类是RecyclerViewDataObserver,代码如下:
private class RecyclerViewDataObserver extends AdapterDataObserver {
RecyclerViewDataObserver() {
}
@Override
public void onChanged() {
assertNotInLayoutOrScroll(null);
mState.mStructureChanged = true;
setDataSetChangedAfterLayout();
if (!mAdapterHelper.hasPendingUpdates()) {
requestLayout();//这里是重点了,真正触发刷新
}
}
可以看到终于来到了真正更新的地方了,就是这里的requestLayout()方法,根据View的原理,requestLayout默认情况下会一层一层往父亲类的requestLayout()调用,最终会调用顶层类的刷新,来到最后就是大名鼎鼎的ViewRootImpl的performTraversals()的方法,从而刷新界面,至此刷新流程结束。
7.观察者模式在Android实际中的应用
我们前面说了,观察者模式在一对多,并且在一个对象的数据改变引起其他对象的数据同时改变的时候特别有用,在Android项目中,这种需求是很多的,比如广播,广播发送出去以后,谁注册了广播,那么便会接收到广播的消息,又比如EventBus事件总线,只要发送出去消息事件,不管在哪里,谁注册了消息事件,也会同时收到事件,从而进行逻辑的处理,在实际的项目中,有时候也会碰到在不同的界面,需要同时改变UI的情况,比如在B界面处理了一个逻辑,然后需要在前面的A界面中,一些UI要求改变等等,假如有2个界面,分别是A界面和B界面,要求点击TestActivity2中的改变全部文本按钮,然后TestActivity2和TestActivity1中的TextView都要求改为预先设置好的文本,这个例子来源于实际例子中的,我们首先来分析一下怎么实现比较好,当然EventBus发送消息可以解决问题,但发送消息之后需要一个个去手动设置新的文本,在这里我们不用EventBus来发送事件,我们利用观察者模式来解决,我们知道了点击"更改全部文本"之后会触发了一大堆的UI改变,首先我们定义了一个更新文本的方法,代码如下:
public interface Observer {
void updateText(String text);//更新TextView的文本
void remove(Observable observable);//删除自己作为观察者
}
然后这些TextView为自定义的,并且实现了观察者接口,代码如下:
public class AutoUpdatetextView extends android.support.v7.widget.AppCompatTextView implements Observer {
public AutoUpdatetextView(Context context) {
super(context);
}
public AutoUpdatetextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public AutoUpdatetextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//在更新文本的接口方法里面实现了自动更新文本
@Override
public void updateText(String text) {
if (!TextUtils.isEmpty(text)) {
setText(text);
}
}
@Override
public void remove(Observable observable) {
observable.remove(this);
}
}
好了,抽象观察者和具体观察者就写好了,我们来写抽象主题和具体的主题,代码如下:
//抽象主题实现增加,删除和更新功能
public interface Observable {
void addObserver(Observer observer);
void remove(Observer observer);
void update(String text);
}
//具体的主题
public class ObservableManager implements Observable {
private static final List observers;
private static ObservableManager sInstance;
private ObservableManager() {
}
public static ObservableManager getInstance() {
if (sInstance ==null) {
synchronized (ObservableManager.class) {
if (sInstance ==null) {
sInstance = new ObservableManager();
}
}
}
return sInstance;
}
static {
observers = new ArrayList<>();
observers.clear();
}
@Override
public void addObserver(Observer observer) {
observers.add(observer);
}
@Override
public void remove(Observer observer) {
if (observers.indexOf(observer) > 0) {
observers.remove(observer);
}
}
@Override
public void update(String text) {
if (observers.size() <= 0) {
Log.d("[app]", “没有具体的观察者”);
return;
}
for (Observer observer : observers) {
observer.updateText(text);
}
}
public void removeAll(List mList) {
if (mList ==null || mList.size() ==0) {
return;
}
Log.d("[app]", “移除的集合大小为:” + mList.size());
for (Observer observer : mList) {
remove(observer);
}
Log.d("[app]", “剩下的集合大小为:” + observers.size());
}
}
好了,抽象主题和具体的实现也写好了,我们再来分析一下,要把那些TextView添加到具体的主题里面,那么应该怎么添加,在 view这棵树已经添加到window上之后进行添加,在添加时判断是否是AutoUpdatetextView,如果是的话,就添加,如果不是,就不要添加了,为此,我们需要写一个BaseActivity,在这里,布局就不贴了,代码如下:
public abstract class BaseActivity extends AppCompatActivity {
protected List mList = new ArrayList<>();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mList.clear();
setContentView(getResID());
ViewGroup viewGroup = (ViewGroup) getWindow().getDecorView();
addUpdateTextView(viewGroup);
}
protected abstract int getResID();
//把Observer的子类添加进去
private void addUpdateTextView(ViewGroup viewGroup) {
for (int i = 0; i < viewGroup.getChildCount(); i++) {
View childView = viewGroup.getChildAt(i);
if (childView instanceof ViewGroup) {
ViewGroup childGroup = (ViewGroup) childView;
addUpdateTextView(childGroup);
} else {
if (childView instanceof Observer) {
//添加到观察者集合里面进去
Observer observer = (Observer) childView; ObservableManager.getInstance().addObserver(observer);
mList.add(observer);
}
}
}
}
//在onDestroy之后需要把这棵树上的AutoUpdatetextView移除,避免持有Activity导致内存泄露
@Override
protected void onDestroy() { ObservableManager.getInstance().removeAll(mList);
super.onDestroy();
}
}
下面我们看一下TestActivity1的代码:
public class TestActivity1 extends BaseActivity {
private TextView text;
@Override
protected int getResID() {
return R.layout.activity_test1;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
text = (TextView) findViewById(R.id.text);
text.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(TestActivity1.this, TestActivity2.class);
startActivity(intent);
}
});
}
}
就跳转到TestActivity2而已。
TestActivity2的代码:
public class TestActivity2 extends BaseActivity {
private TextView update;
String[] texts = {“开开心心”, “呵呵,新的文本”, “你好啊”, “Android开发呢”, “ios开发呢”, “WP开发”, “PHP开发”, “Python开发”};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
update = (TextView) findViewById(R.id.update);
update.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int len = texts.length;
int result = new Random().nextInt(len);
Log.d("[app]", “result=” + texts[result]);
//这里是重点,通知其他注册了的地方改变文本,这里是文本,实际上可以做其他事情 ObservableManager.getInstance().update(texts[result]);
}
});
}
@Override
protected int getResID() {
return R.layout.activity_test2;
}
}
我们可以看到:
ObservableManager.getInstance().update(texts[result]);
这句话是重点,这里更新了文本,那么意味着之前注册的全部文本控件都将收到消息,从而走AutoUpdatetextView的更新文本的接口方法:
//在更新文本的接口方法里面实现了自动更新文本
@Override
public void updateText(String text) {
if (!TextUtils.isEmpty(text)) {
setText(text);
}
}
最后
以上就是标致信封为你收集整理的Android设计模式—观察者模式的全部内容,希望文章能够帮你解决Android设计模式—观察者模式所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复