Android源码之观察者模式的应用
白羽 2018-06-05 来源 :网络 阅读 514 评论 0

摘要:观察者模式是一种行为类模式,它定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。 观察者模式是一个使用率非常高的模式,它最常用在GUI系统、订阅–发布系统。因为这个模式的一个重要作用就是解耦,将被观察者和观察者解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。比如安卓的开源项目EventBus、Otto、AndroidEventBus等事件总线类的和RxJava响应式编程其核心都是使用观察者模式。 希望本文对大家学Android有所帮助。





观察者模式



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

观察者模式是一个使用率非常高的模式,它最常用在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);
    }
 
}</observer></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();
            }
        }
    }
 
}</datasetobserver>
   
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();
           }
       }
   }</listadapter>
   
从代码中可看出,它继承于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()方法重新进行布局,更新用户界面。

 


本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之Android频道!


本文由 @白羽 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程