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

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

Observable里面维护着一个观察者集合
List<Observer> observers = new ArrayList<Observer>();
引用官方解释

/** * 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<被观察者对象>

 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);
        }
    }

很简单,父方法,就是无重复加入到内部集合里面!
2:上面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下代码如下:

 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的情况,看看广播发出情况,谢谢小伙伴的浏览~~~
项目传送:
https://github.com/shonegg/NetMonitor

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

推荐阅读更多精彩内容