Android 观察者模式

概念:

定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

四个元素(自我定义):

观察者、被观察者、注册时机、方法调用

两种实现方式:

观察者被动接受消息
1.创建被观察者接口

 public interface Subject{
        public void registerObserver(Observer o);
        public void removeObserver(Observer o);
        public void notifyObservers();
    }

被观察者的实现逻辑

     public class WeatherData implements Subject{

        private ArrayList<Observer> observers;
        //温度
        private String temperature;
        //湿度
        private String humidity;
        //气压
        private String pressure;

        public WeatherData() {
            observers=new ArrayList<Observer>();
        }
        /**
         * 订阅
         */
        public void registerObserver(Observer o) {
            observers.add(o);
        }
        /**
         * 取消订阅
         */
        public void removeObserver(Observer o) {
            if(observers.indexOf(o)>=0){
                observers.remove(o);
            }
        }
        /**
         * 通知观察者
         */
        public void notifyObservers() {
            for(Observer o:observers){
                o.update(temperature, humidity, pressure);
            }
        }
        /**
         * 数据改变后,通知观察者
         * @param temperature
         * @param humidity
         * @param pressure
         */
        public void setNewData(String temperature,String humidity,String pressure){
            this.temperature=temperature;
            this.humidity=humidity;
            this.pressure=pressure;
            notifyObservers();
        }
    }

在notifyObservers方法中会遍历观察者集合调用其的update方法,然后传过去变化的值,在观察者中显示。
2.创建观察者接口

 public interface Observer{
        public void update(String temperature,String humidity,String pressure);
    }

3.观察者的实现与注册,每一个观察者的注册时机不同,这里是在实例化观察者的时候注册的

public class CurrentCodition implements Observer {
        private Subject weaterData;

        public CurrentCodition(Subject weaterData) {
            this.weaterData = weaterData;
            weaterData.registerObserver(this);
        }
        @Override
        public void update(String temperature,String humidity,String pressure) {
        //具体实现逻辑
        }
    }

4.调用更新

WeatherData weatherData=new WeatherData();
Observer1 observer1=new Observer1(weatherData);
weatherData.setNewData("10", "20", "30");

观察者主动接受消息
1.被观察者
在上文的基础上添加get方法,为了给观察者提供获得想要数据的方法。

public String getTemperature() {
        return temperature;
    }
    public String getHumidity() {
        return humidity;
    }
    public String getPressure() {
        return pressure;
    }

2.观察者
修改观察者中的update方法,从WeatherData实例中直接获得自己想要的数据,不需要的就不用get了

public void update(Observable o, Object arg) {
        if(o instanceof WeatherData){
            WeatherData data=(WeatherData)o;
            this.humidity=data.getHumidity();
            this.pressure=data.getPressure();
            this.temperature=data.getTemperature();
        }
        System.out.println("数据提取完毕,并已展示");
    }

在Android中的使用

1.setOnClickListener()实现一对一的观察者模式
首先直接看方法里面是怎么一个实现逻辑

 public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }

可以发现setOnClickListener()的过程中直接将OnClickListener赋值给了ListenerInfo中的变量,然后这个变量什么时候调用呢,接着看

public boolean performClick() {
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        return result;
    }

可以看到在performClick()方法中直接调用onClick方法,那又是谁调的performClick()方法呢,通过搜索定位到View类中,可以看到以下代码逻辑

public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
        if (isNestedScrollingEnabled()
                && (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD
                || action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD
                || action == R.id.accessibilityActionScrollUp
                || action == R.id.accessibilityActionScrollLeft
                || action == R.id.accessibilityActionScrollDown
                || action == R.id.accessibilityActionScrollRight)) {
            if (dispatchNestedPrePerformAccessibilityAction(action, arguments)) {
                return true;
            }
        }

        switch (action) {
            case AccessibilityNodeInfo.ACTION_CLICK: {
                if (isClickable()) {
                    performClick();
                    return true;
                }
            } break;
            case AccessibilityNodeInfo.ACTION_LONG_CLICK: {
                if (isLongClickable()) {
                    performLongClick();
                    return true;
                }
            } break;

代码中用了一个switch条件判断,根据不同的操作执行相应的逻辑,看到这里就应该很清楚,View这个被观察者,监听到状态的变化后,就会去调用注册进来的onClickListener中的onClick观察者方法,完成相应的操作,由于是一对一的监测,所以没有集合的遍历操作。
2.listView实现一对多的观察者模式
先看被观察者的实现方式吧

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
    private final DataSetObservable mDataSetObservable = new DataSetObservable();
    public boolean hasStableIds() {
        return false;
    }
    public void registerDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.registerObserver(observer);
    }
    public void unregisterDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.unregisterObserver(observer);
    }
    public void notifyDataSetChanged() {
        mDataSetObservable.notifyChanged();
    }

从代码中可以看到BaseAdapter并没有直接继承DataSetObservable(被观察者)类,而是通过组合的方式,作为变量使用的。
然后再看观察者
在listView的父类AdapterView中找到观察者的实现类

    class AdapterDataSetObserver extends DataSetObserver {

        private Parcelable mInstanceState = null;

        @Override
        public void onChanged() {
            mDataChanged = true;
            mOldItemCount = mItemCount;
            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();
            requestLayout();
        }

        @Override
        public void onInvalidated() {
            mDataChanged = true;

            if (AdapterView.this.getAdapter().hasStableIds()) {
                // Remember the current state for the case where our hosting activity is being
                // stopped and later restarted
                mInstanceState = AdapterView.this.onSaveInstanceState();
            }

            // Data is invalid so we should reset our state
            mOldItemCount = mItemCount;
            mItemCount = 0;
            mSelectedPosition = INVALID_POSITION;
            mSelectedRowId = INVALID_ROW_ID;
            mNextSelectedPosition = INVALID_POSITION;
            mNextSelectedRowId = INVALID_ROW_ID;
            mNeedSync = false;

            checkFocus();
            requestLayout();
        }

        public void clearSavedState() {
            mInstanceState = null;
        }
    }

然后看方法的调用

public void notifyChanged() {
    synchronized(mObservers) {
    for (int i = mObservers.size() - 1; i >= 0; i--) {
            mObservers.get(i).onChanged();
        }
    }
}

每次有数据更新的时候,都会调用这个方法刷新数据,可以看到方法中是遍历一个观察者的集合,然后调用统一的实现方法onChanged()实现数据的刷新。
最后就是注册了,其实每次setAdapter()就是注册的过程,代码就不贴了,感兴趣的小伙伴可以自行查看源码。
!!还是小菜鸟,如若有问题欢迎指出[微笑脸]
喵印~~

推荐阅读更多精彩内容

  • 观察者模式理解 观察者模式 Observer 观察者模式定义了一个一对多的依赖关系,让多个观察者对象同时监听一个主...
    Nickyzhang阅读 5,278评论 5 14
  • 源码地址 定义 定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动...
    yangMr阅读 231评论 0 1
  • 关于观察者模式的一些小的Demo 以前总是用,都是从晚上进行copy。今天自己写了一个。分别定义两个接口。一个是观...
    基本密码宋阅读 114评论 0 0
  • 观察者模式的定义 观察者定义了一个一对多的依赖关系,让一个或者多个观察者监听一个主题(被观察者)的变化。因此,当主...
    MaZH阅读 309评论 0 1
  • 前言 对于设计模式的学习,作者一开始是拒绝的。因为作者的Java基础和Android基础很烂。想着补一下Java坑...
    空城新月阅读 602评论 3 6
  • 清晨,空气有些清冷,车子两旁是呼啸而过的风。全新的一天,新鲜的能闻到它朝气蓬勃的气味,就像公路两旁鲜草嫩叶的味道。...
    七弦桐语阅读 342评论 0 1
  • 嘴嘴儿有市场了!天天出勤,认识一堆哥们儿姐们儿——安安哥哥、子宽哥哥、双胞胎哥哥、欢欢姐姐、婷婷姐姐、芽芽姐姐、晨...
    给你们_乐乐与开心阅读 64评论 0 1
  • 在股票市场,为什么有人利用股票使自己的财富做了跳级,而有些人却持续成为他人的韭菜?同一个片场演绎出了不同的悲喜剧,...
    whoogaoqiao阅读 247评论 0 0
  • 管好自己的嘴,讲话不要图一时痛快、信口开河,“良言一句三冬暖,伤人一语六月寒”,说话要用脑子,敏事慎言,话多无益,...
    殷小超阅读 648评论 0 0
  • 曾几何时,我教的学生都是学校的佼佼者,但是,自从接了二班,就打破我的记录了,每次大考都是中间,平时做题也还...
    谜遇阅读 70评论 0 0