Android常见设计模式——观察者模式 (Observer Pattern)

1. 前言

观察者模式是在代码框架中使用比较高的一个设计模式,常常又叫做订阅/发布模式。而通过这个设计模式通常我们就可以做到代码的解耦

在现实生活中,比如当我们订阅了Android官方资讯后,用户就可以收到来自这些网站的推荐消息。在这个场景中就是发布/订阅模式。而这种观察行为通常是一个被观察者,多个观察者。通过观察者模式可以实现一种一对多的关系,使得当被观察者的状态发生改变的时候,所有的观察者都可以得到通知,并作出相应的更新操作。

2. 观察者模式

JavaObserverObservableJDK内置实现的,当我们需要用到观察者模式的时候就可以考虑实现相关的方法。比如下面的案例。

首先定义一观察者,需要实现Observer接口:

// 观察者
static class Person implements Observer{
    
    

    private String name;
    public Person(String n){
    
    
        this.name = n;
    }

    @Override
    public void update(Observable o, Object arg) {
    
    
        System.out.println(this.name+": ==> " + arg);
    }
}

以及对应的被观察者对象,继承自Observable类:

// 被观察者
static class Message extends Observable{
    
    

    public void messageHasChanged(String content){
    
    
        setChanged();
        notifyObservers(content);
    }
}

最后我们就可以简单的测试了:

public static void main(String[] args) {
    
    
    Message message = new Message(); // 被观察者
    Person person1 = new Person("张三"); // 观察者
    Person person2 = new Person("李四");
    Person person3 = new Person("王五");

    // 将二者关联
    message.addObserver(person1);
    message.addObserver(person2);
    message.addObserver(person3);

    // 发布消息
    message.messageHasChanged("这是一个测试消息!");
}

测试结果:
在这里插入图片描述
可以看到是按照注册的倒序来更新消息。当然至于为什么会是这样,其实在源码中是将原本的观察者对象拷贝到一个数组中,然后数组从最后一个位置向前遍历得来的。接下来不妨看看Observer接口和Observable类的源代码。

2.1 源码

Observer接口非常简单,就一个未实现的方法update

public interface Observer {
    
    
    void update(Observable o, Object arg);
}

Observable类中定义了一个容器数组Vector来存储观察者对象Observer,在添加观察者对象的时候会对观察者做一个非空的判断。且定义了一个布尔类型的私有变量changed来标识是否发生改变。整个代码逻辑比较简单,可以发现主要的逻辑在notifyObservers方法中。在进行遍历通知观察者的时候,在该方法中首先对当前对象加了一个synchronized锁,主要是为了判断当前被观察者对象是否改变,如果没有改变就返回。否则就将所有注册的观察者装载到一个Object数组中(装载后将changed变量重置为false),然后逆序调用每个Observer对象的update方法。

public class Observable {
    
    
    private boolean changed = false;
    private Vector<Observer> obs;

    public Observable() {
    
    
        obs = new Vector<>();
    }

    public synchronized void addObserver(Observer o) {
    
    
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
    
    
            obs.addElement(o);
        }
    }

    public synchronized void deleteObserver(Observer o) {
    
    
        obs.removeElement(o);
    }

    public void notifyObservers() {
    
    
        notifyObservers(null);
    }

    public void notifyObservers(Object arg) {
    
    
        Object[] arrLocal;

        synchronized (this) {
    
    
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }

	...
}

2.2 结构

为了直观的Idea中查看到类图,不妨在一个包下面仿造上述两个类,然后再定义测试方法。最后使用Idea的插件Sketch lt!来进行plantuml生成,最终看到结构。

定义为:

package com.weizu;

public interface CustomObserver {
    
    
    void update(CustomObservable o, Object msg);
}

public class CustomObservable {
    
    

    private List<CustomObserver> mObservers;

    public CustomObservable(){
    
    
        mObservers = new ArrayList<CustomObserver>();
    }

    public synchronized void addObserver(CustomObserver o) {
    
    
        if (o == null)
            throw new NullPointerException();
        if (!mObservers.contains(o)) {
    
    
            mObservers.add(o);
        }
    }

    public void notifyObservers(Object msg) {
    
    
        for (CustomObserver mObserver : mObservers) {
    
    
            mObserver.update(this, msg);
        }
    }
}

测试用例:

public class Demo {
    
    

    // 观察者
    static class Person implements CustomObserver{
    
    

        private String name;
        public Person(String n){
    
    
            this.name = n;
        }

        public void update(CustomObservable o, Object arg) {
    
    
            System.out.println(this.name+": ==> " + arg);
        }
    }

    // 被观察者
    static class Message extends CustomObservable{
    
    

        public void messageHasChanged(String content){
    
    
            notifyObservers(content);
        }
    }

    public static void main(String[] args) {
    
    
        Message message = new Message(); // 被观察者
        Person person1 = new Person("张三"); // 观察者
        Person person2 = new Person("李四");
        Person person3 = new Person("王五");

        // 将二者关联
        message.addObserver(person1);
        message.addObserver(person2);
        message.addObserver(person3);

        // 发布消息
        message.messageHasChanged("这是一个测试消息!");
    }
}

然后使用下面的工具来生成weizu.plantuml文件:
在这里插入图片描述

最后,拷贝weizu.plantuml文件内容到http://www.plantuml.com/,即可看见下图:
在这里插入图片描述

不难发现其实上面的类图中涉及到了四种不同类型的角色,分别是被观察者、观察者、具体被观察者以及具体观察者对象。

在上面的代码中不难发现一件事情,那就是使用观察者模式确实可以降低代码的耦合关系。其主要思想不难看出主要是在被观察者中遍历观察者,后调用抽象方法update。也就是说当观察者对象很多的时候,通知的发布就是一件非常耗时的事情了,可能回影响到程序的效率。

且在观察者和被观察者之间的关系也仅是抽象的调用关系,所以在这里依赖于抽象而没有依赖于具体的实现,符合依赖倒置原则。

3. Android中的观察者模式

在使用ListView添加数据的时候,会调用AdapternotifyDataSetChanged()方法。其实这里也是观察者模式的运用,不妨来简单看下源码。比如此时自定义的Adapter继承自ArrayAdapter,然后从这个自定义类的notifyDataSetChanged方法开始追踪:
在这里插入图片描述
从上面的流程中可以清晰的看到,其实最终的观察者模式,且被观察者对象是Adapter中的mDataSetObservable对象,该对象继承自Observable对象。从Observable定义的mObservers为泛型类型可以看出观察者的对象这里为了通用性,直接申明了泛型。

那么,被观察者是在什么地方进行观察者的注册的呢?在Adapter中的观察者又是什么对象?带着这两个问题,这里继续看源码。

因为前面提到了,被观察这对象是Adapter中的mDataSetObservable对象,为了找到在什么地方注册的观察者,所以我们需要找到被观察者的注册方法,也就是:

public abstract class Observable<T> {
    
    
    protected final ArrayList<T> mObservers = new ArrayList<T>();

    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);
        }
    }
    ...
}

那么,不妨按住Ctrl+鼠标左键进行追踪:
在这里插入图片描述

可以看到前面我们看过的类BaseAdapter中调用了这个方法,所以这里切换到这个方法中:

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
    
    
	public void registerDataSetObserver(DataSetObserver observer) {
    
    
        mDataSetObservable.registerObserver(observer);
    }
    ...
}

使用类似的方法对类BaseAdapter中的registerDataSetObserver方法进行追踪,但其实没有结果了。所以这里尝试换个对象,因为适配器更新的对象为ListView的每个Item,按道理讲观察者对象应该就是这些Item。所以这里找到ListViewsetAdapter方法,这里因为我本地并没有下载相关的源码配置,所以这里androidxref来查看源码。比如ListView

// ListView.java
public void setAdapter(ListAdapter adapter) {
    
    
    // 如果有Adapter,且观察者存在就注销观察者 
    if (mAdapter != null && mDataSetObserver != null) {
    
    
        mAdapter.unregisterDataSetObserver(mDataSetObserver);
    }

    ...
    super.setAdapter(adapter);

    if (mAdapter != null) {
    
    
        ...

        // 创建观察者,并注册到被观察者对象
        mDataSetObserver = new AdapterDataSetObserver();
        mAdapter.registerDataSetObserver(mDataSetObserver);

        mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());

        ...
    }
    ...
}

观察上面代码,我们知道其实观察者在这里也就是AdapterDataSetObserver这个类的实例。不妨再次在网站上查找,这个类定义在AbsListView.java中,为一个内部类:

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();
        }
    }
}

可以看到出现了前面在DataSetObservable被观察者类中的遍历调用onChanged方法,继续追踪很有意思,mFastScroll.onSectionsChanged()方法如下:

// FastScroller.java
private Adapter mListAdapter;
public void onSectionsChanged() {
    
    
	mListAdapter = null;
}

所以说其实主要的处理方法其实在其父类中,这里继续追踪父类AdapterView<ListAdapter>.AdapterDataSetObserver中的onChanged()

在这里插入图片描述
也就是观察者调用的onChanged方法即为其触发方法。在这个方法中,会请求重新绘制ListView界面。

到这里是否就完了?明天继续!


References

  • 《Android源码设计模式》

Guess you like

Origin blog.csdn.net/qq_26460841/article/details/121335675