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()就是注册的过程,代码就不贴了,感兴趣的小伙伴可以自行查看源码。
!!还是小菜鸟,如若有问题欢迎指出[微笑脸]
喵印~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,100评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,308评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,718评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,275评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,376评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,454评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,464评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,248评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,686评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,974评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,150评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,817评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,484评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,140评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,374评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,012评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,041评论 2 351

推荐阅读更多精彩内容

  • 观察者模式理解 观察者模式 Observer 观察者模式定义了一个一对多的依赖关系,让多个观察者对象同时监听一个主...
    Nickyzhang阅读 5,470评论 5 14
  • 源码地址 定义 定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动...
    yangMr阅读 353评论 0 1
  • 关于观察者模式的一些小的Demo 以前总是用,都是从晚上进行copy。今天自己写了一个。分别定义两个接口。一个是观...
    基本密码宋阅读 243评论 0 0
  • 观察者模式的定义 观察者定义了一个一对多的依赖关系,让一个或者多个观察者监听一个主题(被观察者)的变化。因此,当主...
    MaZH阅读 495评论 0 1
  • 前言 对于设计模式的学习,作者一开始是拒绝的。因为作者的Java基础和Android基础很烂。想着补一下Java坑...
    空城新月阅读 718评论 3 6