Fork me on GitHub

初探Android观察者模式

在开始学习Android的过程中,我一直都很好奇,像QQ和微博那样的即时讯息交流应用,或者是新闻应用,是怎么做到无论何种情况都能做到及时通知用户有新消息的。后来在跟一个做服务器的同学交流的时候,他觉得,需要在客户端与服务器端建立一个心跳连接,这样,客户端就能与服务器定时获取最新信息了。接下来,只要做到将消息通知到应用就可以了。

以我自己能想到的全局通知的方法,有两种:

  • 1、将获取到的信息放入全局application

  • 2、在线程中发送广播(这种没尝试过,不知是否可行)

依个人来看,第一种实现很简单,但是,在不清楚获取数据量的情况下,盲目将数据放进全局application,会加重APP的运行内存负担,并不值得推荐。

这种情景下,观察者模式就比较适用了。它最常用的地方包括GUI(图形用户界面)系统、订阅-发布系统等。这个模式的特点是,耦合性低,保存数据与读取数据在很多情况下是互相独立的。它主要定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象状态发生变化时,就会通知所有观察者对象,使它们能够及时得到更新。

为了更好的理解概念,我决定讲一步概念并跟进一步Demo代码,以促进理解和记忆。

模式讲解

先把官方的讲解图放上来。

Subject 抽象主题

抽象主题把所有观察者对象的引用保存在一个聚集里,每个主题可以有任何数量的观察者。抽象主题提供接口,可以获取、增加或删除观察者对象。示例如下:

public class HandleName {

    /**
     *
     */
    private static final NameObserver nameObserver=new NameObserver();
    private static final NameObservable nameObservable=new NameObservable();

    /**
     * 
     * @return
     */
    public synchronized static NameObservable getNameObservable() {
        return nameObservable;
    }

    /**
     * 
     * @return
     */
    public synchronized static NameObserver getNameObserver() {
        nameObservable.addObserver(nameObserver);
        return nameObserver;
    }
}

ConcreteSubject 具体主题

具体主题将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。示例如下:

public class NameObservable extends Observable {

    private String name;         

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;

        setChanged();
        notifyObservers();
    }
}

Observer 抽象观察者

抽象观察者为所有的具体观察者定义一个接口,在得到主题通知时更新自己。

public interface ObserverListener {

    /**
     * 变化之后的观察者
     * @param observable
     */
    void changeObserverListener(Observable observable);

}

ConcreteObserver 具体观察者

实现抽象观察者所需要的更新接口,以便使本身的状态与主题状态协调。

public class NameObserver implements Observer {

    private ObserverListener mObserverListener;

    @Override
    public void update(Observable observable, Object o) {
        if(mObserverListener == null || observable == null){
            return;
        }
        mObserverListener.changeObserverListener((NameObservable) observable);
    }

    /**
     * 设置监听
     * @param observerListener
     */
    public void onChangeListener(ObserverListener observerListener){
        this.mObserverListener=observerListener;
    }

}

依照官方找到的每个实现类的官方名称(我不大确定有没有对应上每个主题),与开始的时候大佬给我讲解时的称呼有些出入。因为NameObservable包含具体内容信息,故称为被观察者;NameObserver作为监管NameObservable内容变化,被称为观察者。listener类和另一个类作为监管和通知内容变化的监听类。

代码引用

观察者模式的代码引用很简单,在需要存数据的地方调用被观察者存入最新数据;在另外需要显示数据的地方调用观察者显示数据。用法及用处任意,可以不在同一个页面中引用,也可以引用不止一处地方,真正可以做到“一处通知,多处更新”。

private NameObservable mNameObservable;
private NameObserver mNameObserver;
private List mStringList = null;

protected void onCreate(Bundle savedInstanceState) {
           ...
        mNameObservable= HandleName.getNameObservable();
        mNameObserver= HandleName.getNameObserver();
        mNameObserver.onChangeListener(new ObserverListener() {
            @Override
            public void changeObserverListener(Observable observable) {
                String name=mNameObservable.getName();

                /*
                 *显示数据段代码
                 */
            }
        });

        initList();        

        new Thread(new Runnable() {
            int number=0;
            @Override
            public void run() {
                //每隔一秒执行一次此线程
                mHandler.postDelayed(this,1000);
                number++;
                if(number<10) {
                    name = mStringList.get(number % 4);
                    mNameObservable.setName(name);
                }
            }
        }).start();

}

private void initList(){
        mStringList=new ArrayList<>();
        mStringList.add("Qi");
        mStringList.add("Mi");
        mStringList.add("Lin");
        mStringList.add("Yun");
    }

这段代码我没有贴全。大概流程是,开启一个线程更新数据,监听者在收到更新通知后,在页面更新显示最新的数据。其中我用一个ArrayList来指定更新变化的数据。

-------------本文结束感谢您的阅读-------------