Android随笔4:观察者模式

1. 概念

1.1 什么是观察者模式

定义一个被观察者和一组观察者,观察者观察被观察者(订阅或者说是监听关系)。每当被观察者发生改变,会通知所有观察者,观察者收到通知可以做出对应的处理。

1.2 现实中的例子

其实现实生活当中有很多这样的例子,比如说直播订阅。看过斗鱼、虎牙直播的朋友肯定知道,很多主播在直播的时候都会吆喝这么一句话:“没有订阅的朋友们可以走一波订阅~”。
在这里,主播就相当于一个被观察者,直播间的观众就是一个个观察者,而订阅这个行为就是观察。我们订阅主播之后,每当主播上线,都会发出一组通知,通知所有的被观察者,即所有订阅的观众------“我上线啦~快来我的直播间吧”。而观察者(观众)在接受到通知之后,可以做出对应的处理:打开直播间观看直播,或者无视。淘宝的抢购预定、12306的候补购票也是同样的道理。所以,观察者模式也常被称为订阅者模式。

1.3 UML类图

观察者模式类图

  • 抽象被观察者(Subject):它把所有对观察者对象的引用保存在一个集合中(比如ArrayList),每个被观察者都可以有任何数量的观察者。抽象被观察者提供接口:增加观察者、移除观察者、通知观察者状态发生改变。
  • 具体观察者(ConcreteSubject):实现抽象被观察者接口,将有关state存入具体观察者对象;在具体观察者的内部state改变时,给所有注册过的观察者发出通知。
  • 抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到被观察者的通知时更新自己。
  • 具体观察者(ConcreteObserver):实现抽象观察者接口,在update接口中具体实现自己的更新逻辑。

2. 项目中的应用:网络切换

我本身是做网络一块的开发,之前在项目中有接到一个需求:在切换网络时,重新请求接口获取数据。下面,我会不使用观察者模式使用观察者模式分别实现这个需求,然后来分析使用观察者模式的好处。

2.1 不使用观察者模式

public class NetMonitor {
    private NetBroadcastReceiver netBroadcastReceiver;
    private Context context;

    public NetMonitor() {
    }

    /**
     * 开启网络监听
     */
    public void start(Context context) {
        Context applicationContext = context.getApplicationContext();
        this.context = applicationContext;
        
        netBroadcastReceiver = new NetBroadcastReceiver();
        IntentFilter filter = new IntentFilter();
        
        //设置action为CONNECTIVITY_ACTION
        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
        //动态注册广播
        applicationContext.registerReceiver(netBroadcastReceiver, filter);
    }

    /**
     * 停止网络监听
     */
    public void stop() {
        if (context == null) {
            return;
        }

        if (netBroadcastReceiver != null) {
            context.unregisterReceiver(netBroadcastReceiver);
        }

        context = null;
    }

    
    private static class NetBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            //如果action是网络变化,则执行以下处理逻辑
            if (action != null && action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
                //执行一系列更新:
                //更新网络状态
                //发起网络探测
                //请求接口,重新获取数据
                //......
            }
        }
    }
}

先来看一下NetBroadcastReceiver 这个内部静态类,这个继承了BroadcastReceiver并实现了onReceive方法。当有广播通知时,就会回调onReceive方法。方法中判断了,如果actionCONNECTIVITY_ACTION,即网络状态发生了变化,才会继续执行后续的更新逻辑。

然后回头看一下start方法,我们传入了context上下文用来registerReceiver,动态注册一个广播。重点关注一下filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION),我们知道IntentFilter的作用是过滤Intent,这行代码的意思就是我们的广播只接受CONNECTIVITY_ACTION类型的action,避免接受不必要的广播通知。

stop方法比较简单,就是在不需要监听网络变化的时候反注册。

讲完了实现,接下来具体看一下,接收到网络切换之后的处理逻辑。也就是onReceive方法里面做的事情,我列举了很多可能需要在网络切换后做的逻辑:更新网络状态、发起网络探测、请求接口等…咋一看其实还好,就是一些方法的调用,代码也不复杂。但如果以后还有新的处理逻辑要加进来呢?那是不是又要在onReceive方法中增加新的代码了?是的 ,这就是问题的所在,NetMonitor这个类和具体的业务处理逻辑强耦合了,每当业务逻辑改变时,都要更新NetMonitor的实现。而使用观察者模式,则能很好地将NetMonitor类的职责和业务逻辑处理解耦。下面让我们来看一下具体的实现~

BTW,2.1说是不使用观察者模式,但是不知不觉当中还是实现了观察者模式,你发现了吗?
没错,就是BroadcastReceiver!我们向系统注册广播的过程其实就相当于订阅,当网络状态发生改变时,我们得到通知,进行更新处理。

2.2 使用观察者模式

首先,我们需要定义一个网络状态变化的接口:

public interface OnNetworkChangeListener {
    void onNetworkChange();
}

接口很简单,只有一个onNetworkChange方法。

下面看一下NetMonitor的实现:

public class NetMonitor {
    private static List<OnNetworkChangeListener> onNetworkChangeListeners;
    private NetBroadcastReceiver netBroadcastReceiver;
    private Context context;

    public NetMonitor() {
        onNetworkChangeListeners = new ArrayList<>();
    }

    /**
     * 开启网络监听
     */
    //...

    /**
     * 停止网络监听
     */
    //...

    /**
     * 注册监听
     */
    public void addListener(OnNetworkChangeListener listener) {
        if (listener != null) {
            onNetworkChangeListeners.add(listener);
        }
    }

    /**
     * 注销监听
     */
    public void removeListener(OnNetworkChangeListener listener) {
        if (listener != null) {
            onNetworkChangeListeners.remove(listener);
        }
    }

    /**
     * 通知网络状态更新
     */
    private static void notifyNetworkChange() {
        for (OnNetworkChangeListener listener : onNetworkChangeListeners) {
            if (listener != null) {
                listener.onNetworkChange();
            }
        }
    }

    private static class NetBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            //如果action是网络变化,则执行以下处理逻辑
            if (action != null && action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        notifyNetworkChange();
                    }
                }).start();
            }
        }
    }
}

startstop方法一样和2.1一样,就不多加赘述了。观察上面这段代码,我们发现,NetMonitor中增加了一个onNetworkChangeListeners的数组,这个数组就是用来保存管理观察者注册的监听。观察者可以通过addListener方法向NetMonitor注册网络监听,也可以通过removeListener来注销监听。而所有观察者注册的listener就保存在onNetworkChangeListeners的数组中。
再看一下onReceive中的处理逻辑,很简约有没有~只是新开了一个线程,然后调用了notifyNetworkChange()方法。那么notifyNetworkChange()方法里面做了什么呢?没错,遍历onNetworkChangeListeners数组,然后调用listener.onNetworkChange(),通知所有观察者进行更新。而观察者只需要实现OnNetworkChangeListener接口,在notifyNetworkChange回调中实现各自的更新逻辑即可。比如:

public class MainActivity extends AppCompatActivity implements OnNetworkChangeListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        NetMonitor netMonitor = new NetMonitor();
        netMonitor.start(this.getApplicationContext());
        netMonitor.addListener(this);
    }

    @Override
    public void onNetworkChange() {
        //更新操作
    }
}

NetMonitor并不关心网络切换之后具体要做什么样的处理,它只关心网络切换了,然后把这个信息通知给所有注册过监听的观察者,而具体的处理是观察者应该关心的事。这样就达成了NetMonitor的解耦,它只负责网络状态变化的通知,具体的处理交给许许多多的观察者实现。

3. 总结

如果项目中存在一对多的依赖关系时,就可以考虑使用观察者模式。其实Button中的点击事件也使用到了观察者模式,Button在这里其实就是被观察者,然后我们通过setOnClickListenerButton注册监听,实现了onClick方法,去实现点击之后的具体处理逻辑。还有很多很多的例子,像大名鼎鼎的RxJava框架,其核心也是观察者模式。

4. demo代码

demo地址: git跳转.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值