欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

观察者模式及在Android源码中的应用

程序员文章站 2023-01-29 21:19:34
观察者模式 观察者模式是一种行为类模式,它定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。 观察者模式是一个使用率非常高...

观察者模式

观察者模式是一种行为类模式,它定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。

观察者模式是一个使用率非常高的模式,它最常用在gui、订阅–发布系统。因为这个模式的一个重要作用就是解耦,将被观察者和观察者解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。比如安卓的开源项目eventbus、otto、androideventbus等事件总线类的和rxjava响应式其核心都是使用观察者模式。

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

栗子
这里举一个追剧的例子,平常为了不错过最新的电视剧我们会订阅或关注这个电视剧,当电视剧更新后会第一时间推送给我们,下来就简单实现一下。

抽象观察者类

/**
 *  抽象观察者类,为所有具体观察者定义一个接口,在得到通知时更新自己
 */
public interface observer {
    /**
     *  有更新
     *  
     *  @param message 消息
     */
    public void update(string message);

}

抽象被观察者类

/**
 * 抽象被观察者类
 */
public interface observable {

    /**
     * 推送消息
     * 
     * @param message 内容
     */
    void push(string message);

    /**
     * 订阅
     * 
     * @param observer 订阅者
     */
    void register(observer observer);
}

具体的观察者类

/**
 * 具体的观察者类,也就是订阅者
 */
public class user implements observer {
    // 订阅者的名字
    private string name;

    public user(string name) {
        this.name = name;
    }

    @override
    public void update(string message) {
        system.out.println(name + "," + message + "更新了!");

    }
}

具体的被观察者类

/**
 *  具体的被观察者类,也就是订阅的节目
 */
public class teleplay implements observable{

    private list list = new arraylist();//储存订阅者

    @override
    public void push(string message) {
        for(observer observer:list){
            observer.update(message);
        }
    }

    @override
    public void register(observer observer) {
        list.add(observer);
    }

}

实现

public class client {
    public static void main(string[] args) {
        //被观察者,这里就是用户订阅的电视剧
        teleplay teleplay = new teleplay();
        //观察者,这里就是订阅用户
        user user1 = new user("小明");
        user user2 = new user("小光");
        user user3 = new user("小兰");
        //订阅
        teleplay.register(user1);
        teleplay.register(user2);
        teleplay.register(user3);
        //推送新消息
        teleplay.push("xxx电视剧");
    }
}

结果

小明,xxx电视剧更新了!
小光,xxx电视剧更新了!
小兰,xxx电视剧更新了!

由上面的代码可以看出实现了一对多的消息推送,推送消息都是依赖observer和observable这些抽象类,而user和teleplay完全没有耦合,保证了订阅系统的灵活性和可扩展性。

在以前,我们最常用到的控件就是listview了,而listview最重要的一个点就是adapter,在我们往listview添加数据后,我们都会调用一个方法: notifydatasetchanged(), 这个方法就是用到了我们所说的观察者模式。

跟进这个方法notifydatasetchanged方法,这个方法定义在baseadapter中,代码如下:

public abstract class baseadapter implements listadapter, spinneradapter {
    // 数据集观察者
    private final datasetobservable mdatasetobservable = new datasetobservable();

    // 代码省略

    public void registerdatasetobserver(datasetobserver observer) {
        mdatasetobservable.registerobserver(observer);
    }

    public void unregisterdatasetobserver(datasetobserver observer) {
        mdatasetobservable.unregisterobserver(observer);
    }

    /**
     * notifies the attached observers that the underlying data has been changed
     * and any view reflecting the data set should refresh itself.
     * 当数据集用变化时通知所有观察者
     */
    public void notifydatasetchanged() {
        mdatasetobservable.notifychanged();
    }
}

可以发现,当数据发生变化时候,notifydatasetchanged中会调用mdatasetobservable.notifychanged()方法

public class datasetobservable extends observable {
    /**
     * invokes onchanged on each observer. called when the data set being observed has
     * changed, and which when read contains the new state of the data.
     */
    public void notifychanged() {
        synchronized(mobservers) {
            // 调用所有观察者的onchanged方式
            for (int i = mobservers.size() - 1; i >= 0; i--) {
                mobservers.get(i).onchanged();
            }
        }
    }

}

mdatasetobservable.notifychanged()中遍历所有观察者,并且调用它们的onchanged方法。

那么这些观察者是从哪里来的呢?首先listview通过setadapter方法来设置adapter

    @override
    public void setadapter(listadapter adapter) {
        // 如果已经有了一个adapter,那么先注销该adapter对应的观察者
        if (madapter != null && mdatasetobserver != null) {
            madapter.unregisterdatasetobserver(mdatasetobserver);
        }

        // 代码省略

        if (mheaderviewinfos.size() > 0|| mfooterviewinfos.size() > 0) {
            madapter = wrapheaderlistadapterinternal(mheaderviewinfos, mfooterviewinfos, adapter);
        } else {
            madapter = adapter;
        }

        super.setadapter(adapter);

        if (madapter != null) {
            mareallitemsselectable = madapter.areallitemsenabled();
            molditemcount = mitemcount;
            // 获取数据的数量
            mitemcount = madapter.getcount();
            checkfocus();
            // 注意这里 : 创建一个数据集观察者
            mdatasetobserver = new adapterdatasetobserver();
            // 将这个观察者注册到adapter中,实际上是注册到datasetobservable中
            madapter.registerdatasetobserver(mdatasetobserver);

            // 代码省略
        } else {
            // 代码省略
        }

        requestlayout();
    }

在设置adapter时会构建一个adapterdatasetobserver,最后将这个观察者注册到adapter中,这样我们的被观察者、观察者都有了。

adapterdatasetobserver定义在listview的父类abslistview中,代码如下 :

 class adapterdatasetobserver extends adapterview.adapterdatasetobserver {
        @override
        public void onchanged() {
            super.onchanged();
            if (mfastscroll != null) {
                mfastscroll.onsectionschanged();
            }
        }

        @override
        public void oninvalidated() {
            super.oninvalidated();
            if (mfastscroll != null) {
                mfastscroll.onsectionschanged();
            }
        }
    }

从代码中可看出,它继承于adapterview的内部类adapterdatasetobserver,代码如下:

class adapterdatasetobserver extends datasetobserver {

        private parcelable minstancestate = null;
        // 调用adapter的notifydatasetchanged的时候会调用所有观察者的onchanged方法,核心实现就在这里
        @override
        public void onchanged() {
            mdatachanged = true;
            molditemcount = mitemcount;
            // 获取adapter中数据的数量
            mitemcount = getadapter().getcount();

            // detect the case where a cursor that was previously invalidated has
            // been repopulated with new data.
            if (adapterview.this.getadapter().hasstableids() && minstancestate != null
                    && molditemcount == 0 && mitemcount > 0) {
                adapterview.this.onrestoreinstancestate(minstancestate);
                minstancestate = null;
            } else {
                remembersyncstate();
            }
            checkfocus();
            // 重新布局listview、gridview等adapterview组件
            requestlayout();
        }

        // 代码省略

        public void clearsavedstate() {
            minstancestate = null;
        }
    }

可见该类确实继承于观察者抽象类datasetobserver。
当listview的数据发生变化时,调用adapter的notifydatasetchanged函数,这个函数又会调用datasetobservable的notifychanged函数,这个函数会调用所有观察者 (adapterdatasetobserver) 的onchanged方法。这就是一个观察者模式!

**总结:**adapterview中有一个内部类adapterdatasetobserver,在listview设置adapter时会构建一个adapterdatasetobserver,并且注册到adapter中,这个就是一个观察者。而adapter中包含一个数据集可观察者datasetobservable,在数据数量发生变更时开发者手动调用adapter.notifydatasetchanged,而notifydatasetchanged实际上会调用datasetobservable的notifychanged函数,该函数会遍历所有观察者的onchanged函数。在adapterdatasetobserver的onchanged函数中会获取adapter中数据集的新数量,然后调用listview的requestlayout()方法重新进行布局,更新用户界面。