android 网络监控下的观察者模式运用

观察者模式,由观察者和被观察对象组成,Java已经提供了相关类供我们开发者调用!
Observable – 被观察者
Observer- 观察者
在app开发中,很多情况下都会监听网络变化,如Socket长链接,视频在线播放,唤醒某些服务,友好的用户体验考虑…

那么在这种场景下,Observable就是网络状态,Observer就是Activity

Observable里面维护着一个观察者集合
List observers = new ArrayList();
引用官方解释
/* Observable is used to notify a group of Observer objects when a change * occurs. On creation, the set of observers is empty. After a change occurred, * the application can call the {@link #notifyObservers()} method. This will * cause the invocation of the {@code update()} method of all registered * Observers. The order of invocation is not specified. This implementation will * call the Observers in the order they registered. Subclasses are completely * free in what order they call the update methods. * * @see Observer */

当数据变化时,Observable会通知集合里的所有观察者对象!具体在数据变化后,app调用Observable的notifyObservers方法,那么 集合里的所有Observer的update()会被执行!

具体写法:
第一步:Observable<被观察者对象>

 public class NetObservable extends Observable {
    private Context context;

    public NetObservable(Context context) {
        super();
        this.context = context;
    }

    @Override
    public void addObserver(Observer observer) {
        try {
            super.addObserver(observer);
            NetworkInfo networkInfo = Network.getCurrentActiveNetwork(this.context);//获取当前连接可用的网络
            if (networkInfo != null) {
                if (!networkInfo.isAvailable()) {//网络不可用
                    observer.update(this, new NetObserver.NetAction(false,
                            false, Network.getSubType(context)));
                    return;
                }

                if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {//WIFI
                    observer.update(this, new NetObserver.NetAction(true,
                            true, Network.getSubType(context)));
                    return;
                }
                //非WIFI
                observer.update(this, new NetObserver.NetAction(true,
                        false, Network.getSubType(context)));
                return;
            }
            //无网络
            observer.update(this, new NetObserver.NetAction(false,
                    false, Network.getSubType(context)));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void notifyObservers(Object data) {
        try {
            this.setChanged();//用来设置一个内部标志注明数据发生了变化
            super.notifyObservers(data);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

分析:
1:被观察者提供了这样一个方法addObserver(Observer observer),用来添加观察者,可以绑定多个观察者,父类做了啥呢?

    public void addObserver(Observer observer) {
        if (observer == null) {
            throw new NullPointerException("observer == null");
        }
        synchronized (this) {
            if (!observers.contains(observer))
                observers.add(observer);
        }
    }

很简单,父方法,就是无重复加入到内部集合里面!
上面NetObservable重写了addObserver方法,仅仅是为了第一添加的时候,去立即反馈观察者当前网络状态!
还重写了notifyObservers方法,做法是先调用this.setChanged();
然后调用父通知方法super.notifyObservers(data);
这里setChanged()是关键,她将内部标志置为真changed = true,下面是通知父方法

 public void notifyObservers(Object data) {
        int size = 0;
        Observer[] arrays = null;
        synchronized (this) {
            if (hasChanged()) {//如果changed==true,就填充arrays数组
                clearChanged();
                size = observers.size();
                arrays = new Observer[size];
                observers.toArray(arrays);
            }
        }
        if (arrays != null) {//如果被填充过,就通知数组类所有对象
            for (Observer observer : arrays) {
                observer.update(this, data);
            }
        }
    }

第二步:Observer<观察者对象>
写法:

public abstract class NetObserver implements Observer {
    public static class NetAction {
        private boolean isAvailable;
        private boolean isWifi;
        private Network.Type type;

        public NetAction(boolean isAvailable, boolean isWifi, Network.Type type) {
            super();
            this.isAvailable = isAvailable;
            this.isWifi = isWifi;
            this.type = type;
        }

        public boolean isAvailable() {
            return this.isAvailable;
        }

        public Network.Type getType() {
            return type;
        }

        public void setType(Network.Type type) {
            this.type = type;
        }

        public boolean isWifi() {
            return this.isWifi;
        }
    }

    public abstract void notify(NetAction action);

    @Override
    public void update(Observable observable, Object data) {
        this.notify(((NetAction) data));
    }
}

分析:
这里衍生了一个抽象方法notify(NetAction action),这里观察者只关心NetAction(具体消息)这类结果,而不关心Observable情况,所以子类重写该抽象方法就够了,如果你的情况是需要 得到 被观察者对象Observable,那么还需重写update方法!

第3步:什么时候通知观察者
当然是收到网络状态变化的广播时候啦,看看接收器

 public class NetMonitor extends BroadcastReceiver {
    private static final String TAG = "NetMonitor";
    private static NetMonitor instance;
    private NetObservable observable;


    public void addObserver(NetObserver observer) {//添加观察者
        this.observable.addObserver(observer);
    }

    public void delObserver(NetObserver observer) {//删除某个观察者
        this.observable.deleteObserver(observer);
    }

    public void destory() {//销毁方法,删除所有观察者
        this.observable.deleteObservers();
        this.observable = null;
    }

    public static NetMonitor getInstance() {//单例模式
        if (instance == null) {
            synchronized (NetMonitor.class) {
                if (instance == null) {
                    instance = new NetMonitor();
                }
            }
        }
        return instance;
    }

    public void init(Context context) {//初始化,最好在Application,仅仅一次
        this.observable = new NetObservable(context);
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
        context.registerReceiver(this, intentFilter);//Context注册广播
    }

    //数据源有变化,告诉被观察者NetObservable(唯一的)
    private void notifyNetState(Context context) {
        try {
            NetworkInfo networkInfo = Network.getCurrentActiveNetwork(context);
            if (networkInfo != null) {
                if (!networkInfo.isAvailable()) {
                    this.observable.notifyObservers(new NetObserver.NetAction(false, false, Network.getSubType(context)));
                    return;
                }
                if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
                    this.observable.notifyObservers(new NetObserver.NetAction(true, true, Network.getSubType(context)));
                    return;
                }

                this.observable.notifyObservers(new NetObserver.NetAction(true, false, Network.getSubType(context)));
                return;
            }

            this.observable.notifyObservers(new NetObserver.NetAction(false, false, Network.getSubType(context)));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        this.notifyNetState(context);
    }
}

分析:
在应用启动的时候,先拿单例初始化一下,哈哈,套路!
没错,源头都在这NetMonitor,想想主动MVC模式,这里NetMonitor就扮演着Controller控制层,NetObservable相当Model,而NetObserver就是View了,中间传递的事件是NetAction!
NetMonitor告诉模型层NetObservable你的NetAction事件来了, 既然来了,那就通知出去吧,于是立即通知View层,完成事件传递!

最后看看上层:

  private NetObserver mNetObserver = new NetObserver() {//观察者
        @Override
        public void notify(NetAction action) {
            if (action.isAvailable()) {
                Log.e(MainActivity.class.getSimpleName(), "网络可用 > " + "网络类型:" + action.getType().toString());
            } else {
                Log.e(MainActivity.class.getSimpleName(), "网络不可用 > " + "网络类型:" + action.getType().toString());
            }
        }
    };


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        NetMonitor.getInstance().addObserver(this.mNetObserver);//注册
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        NetMonitor.getInstance().delObserver(this.mNetObserver);//取消注册
    }

最后还要提一下NetAction中有一个枚举类成员Network.Type,详细描述了是哪一种网络:

  public enum Type {
        UNKNOWN, WIFI, MOBILE, MOBILE2G, MOBILE3G, MOBILE4G
  }

PS:在做App的时候,为了给用户省流量,为了不激起用户的愤怒,为了更好的用户体验,是需要根据用户当前网络情况来做一些调整的,也可以在 App 的设置模块里,让用户自己选择,在 2G / 3G / 4G 网络条件下,是否允许请求一些流量比较大的数据。

那么怎么区分呢?这里主要根据TelephonyManager里的常量值 (NETWORK_TYPE_GPRS\NETWORK_TYPE_LTE….)来分类,具体常量值有19中(可能版本不同,值会有变化),其实如何判断用户2G/3G/4G移动数据网络,源码TelephonyManager里有一个方法getNetworkClass可以满足,但悲剧的是被系统隐藏了,打上了@hide标签,不过可以考虑反射调用哦! API 23下代码如下:

   /** * Return general class of network type, such as "3G" or "4G". In cases * where classification is contentious, this method is conservative. * * @hide */
    public static int getNetworkClass(int networkType) {
        switch (networkType) {
            case NETWORK_TYPE_GPRS:
            case NETWORK_TYPE_GSM:
            case NETWORK_TYPE_EDGE:
            case NETWORK_TYPE_CDMA:
            case NETWORK_TYPE_1xRTT:
            case NETWORK_TYPE_IDEN:
                return NETWORK_CLASS_2_G;
            case NETWORK_TYPE_UMTS:
            case NETWORK_TYPE_EVDO_0:
            case NETWORK_TYPE_EVDO_A:
            case NETWORK_TYPE_HSDPA:
            case NETWORK_TYPE_HSUPA:
            case NETWORK_TYPE_HSPA:
            case NETWORK_TYPE_EVDO_B:
            case NETWORK_TYPE_EHRPD:
            case NETWORK_TYPE_HSPAP:
            case NETWORK_TYPE_TD_SCDMA:
                return NETWORK_CLASS_3_G;
            case NETWORK_TYPE_LTE:
            case NETWORK_TYPE_IWLAN:
                return NETWORK_CLASS_4_G;
            default:
                return NETWORK_CLASS_UNKNOWN;
        }
    }

到此,整个注册,通知流程就分析完了,最好自己亲自打印一下网络状态变化,尤其是由WIFI切换到移动网络,或者由移动网络切换到WIFI的情况,看看广播发出情况,谢谢小伙伴的浏览~~~

项目传送:
github.com/shonegg/Net…

    原文作者:算法小白
    原文地址: https://juejin.im/entry/5895437bb123db16a394afe2
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞