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

来源:互联网 发布:昆士兰it 编辑:程序博客网 时间:2024/06/05 18:43

观察者模式

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

观察者模式是一个使用率非常高的模式,它最常用在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<Observer> list = new ArrayList<Observer>();//储存订阅者    @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完全没有耦合,保证了订阅系统的灵活性和可扩展性。

Android源码中的应用

在以前,我们最常用到的控件就是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<DataSetObserver> {    /**     * 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<ListAdapter>.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()方法重新进行布局,更新用户界面。

原创粉丝点击