超简单实现网络状态的监听

作者: DDDong丶 | 来源:发表于2019-06-12 16:02 被阅读21次

    超简单实现网络状态的监听

    前言:

    我们做项目的时候,不可避免的要做网络状态的监听,一般我们都是抽取出一个工具类来实现,比如:

    //判断网络是否可用
    public static boolean isNetworkAvailable() {
       ConnectivityManager connectivityManager = (ConnectivityManager)       
                   getApplication()
                   .getSystemService(Context.CONNECTIVITY_SERVICE);
       if (connectivityManager == null) {
           return false;
       }
       NetworkInfo[] info = connectivityManager.getAllNetworkInfo();
       if (info != null) {
           for (NetworkInfo networkInfo : info) {
                if (networkInfo.getState() == NetworkInfo.State.CONNECTED) {
                    return true;
                }
           }
       }
       return false;
    }
    

    细致的你当然还会再写一个判断网络类型的方法,反手再来一个例:

    //返回当前网络类型
    public static NetType getNetType() {
        ConnectivityManager connectivityManager = (ConnectivityManager) 
               getApplication()
               .getSystemService(Context.CONNECTIVITY_SERVICE);
        if (connectivityManager == null) {
            return NetType.NONE;
        }
        NetworkInfo info = connectivityManager.getActiveNetworkInfo();
        if (info == null) {
            return NetType.NONE;
        }
        int type = info.getType();
    
        if (type == ConnectivityManager.TYPE_MOBILE) {
           return NetType.MOBILE;
        } else if (type == ConnectivityManager.TYPE_WIFI) {
           return NetType.WIFI;
        }
        return NetType.NONE;
    }
    

    然后就可以在相应的操作前进行判断网络的判断:

    if (NetworkUtils.isNetworkAvailable()) {
        doSomething();
    } else {
        doSomething();
    }
    
    if (NetworkUtils.getNetType()== xxx) {
        doSomething();
    } else {
        doSomething();
    }
    

    但是这样做也有些问题:

    1. 首先最直观的,不够优雅,代码判断量太多,如果你的操作是需要频繁的监听网络状态,那么过多的if/ else肯定会让后面维护变的眼花缭乱。
    1. 以上方法只能在网络操作之前判断网络状态,若用户在网络正常情况下发起操作而中间改变网络,比如下载中途突然丢失网络,则此时无法做出相应的控制。
    1. 无法只针对某种网络类型进行监听,比如只想监听用户切换到 WiFi 网络时做出响应。
    1. 程序多处需要进行网络监听处理时,不能同时接收网络变化,必须逐个地方手动处理。

    如何解决?

    接下来就是本文的重点,如何通过 NetStatusBus 这个库来解决以上问题,让你的网络状态监听变得前所未有的简单粗暴。

    通过以下方式来使用 NetStatusBus:

    1.通过 Gradle 添加依赖:

     implementation 'com.sunchen:netstatusbus:0.1.3'
    Application 中初始化 NetStatusBus:
    // 尽可能早的进行这一步操作, 建议在 Application 中完成初始化操作
    NetStatusBus.getInstance().init(this);
    

    2.根据你的生命周期来注册和注销订阅者,例如:

    @Override
    public void onStart() {
     super.onStart();
     NetStatusBus.getInstance().register(this);
    }
    ​
    @Override
    public void onStop() {
     super.onStop();
     NetStatusBus.getInstance().unregister(this);
    }
    

    3.声明你的订阅方法,在该方法中可以监听到网络状态的变更:

    @NetSubscribe(netType = NetType.AUTO)
     public void doSometing(NetType netType) {
     //netType 会返回当前的网络类型为 NetType.WIFI 还是 NetType.MOBIL 或者 NetType.NONE
     Log.d(Constrants.LOG_TAG, netType.name());
     }
    

    到这里就已经结束了,你的所有网络操作已经都可以清晰优雅的在订阅方法中进行处理了,当然你也可以继续往下看。

    效果

    移动网络状态:

    2019-06-12 15:03:29.397 16755-16755/com.jiyun.wanandroid D/MainActivity: getNetStatus: netType=MOBILE
    

    WIFi状态:

    2019-06-12 15:05:33.845 16755-16755/com.jiyun.wanandroid D/MainActivity: getNetStatus: netType=WIFI
    

    无网络:

    2019-06-12 15:05:58.902 16755-16803/com.jiyun.wanandroid D/MainActivity: getNetStatus: netType=NONE
    

    特别说明

    订阅方法必须填写一个NetType参数,可以通过NetType的值来判断当前网络类型。

    @NetSubscribe中 netType为可选值,可以设置订阅的类型,可选值类型如下:

    NetType.AUTO

    这是默认值,任何网络状态发生变化,该类型订阅者都会被回调。同时会传入NetType参数告知你当前的网络类型,示例如下:

    //任何网络状态发生变化,该类型订阅者都会被回调
    @NetSubscribe(netType = NetType.AUTO)
    public void netChange(NetType netType) {
     Log.d(Constrants.LOG_TAG, netType.name());
    }
    NetType.WIFI
    

    只要当前是由 WIFI 改变引发的网络状态变化,该类型订阅者都会被回调。同时会传入NetType参数告知你当前的网络类型,示例如下:

    // 当 wifi 连接时,或者没有网络时会回调此方法
    @NetSubscribe(netType = NetType.WIFI)
    public void netChange(NetType netType) {
     Log.d(Constrants.LOG_TAG, netType.name());
    }
    NetType.MOBILE
    

    只要当前是由移动网络改变引发的网络状态变化,该类型订阅者都会被回调。同时会传入NetType参数告知你当前的网络类型,示例如下:

    // 当正在使用移动网络时,或者没有网络时会回调此方法
    @NetSubscribe(netType = NetType.MOBILE)
    public void netChange(NetType netType) {
     Log.d(Constrants.LOG_TAG, netType.name());
    }
    

    NetType.NONE

    只有当网络丢失时,该类型订阅者才会被回调。

    // 只有当网络丢失时,该类型订阅者才会被回调。
    @NetSubscribe(netType = NetType.NONE)
    public void netChange(NetType netType) {
     Log.d(Constrants.LOG_TAG, netType.name());
    }
    

    它的原理是什么

    有人会觉得,我用个库爽就行了管它的原理干什么?

    你放心,我只是小小的描述一下实现方式。

    原理简单来说,就是在全局初始化的时候就绑定对网络的变化监听。然后将进行注册的父类所有的订阅方法保存至集合中,这里涉及到一些方法的校验,最后在网络状态发生改变时利用 Java 反射机制遍历执行所有订阅方法。熟悉 EventBus 的小伙伴已经看出来了这里借鉴了一小部分 EventBus 的思想。

    回到第一步,那么具体是如何绑定对网络状态变化的监听呢?

    以前我们是通过注册广播来实现绑定网络变更的监听,在Android 7.0 以后,Google 基于性能和安全原因对广播进行了很多限制,比如监听网络变更的广播 android.net.conn.CONNECTIVITY_CHANGE 使用静态注册的方式则无法生效,而动态注册的方式虽然可以生效但毕竟不是最优解。

    同样出于性能和安全,以及拥抱变化的角度,最终我们还是使用官方推荐的方式,利用 ConnectivityManager.NetworkCallback 来进行网络变化的监听,这是在Android 5.0即android api 21推出的API,目前Android 5.0以上的市场占有率在 85%以上,随着国内各大厂商正在积极的推进适配普及Android Q,这个比例还会进一步增大,所以个人人认为无需过于担心低版本适配。

    使用时出现的问题:

    使用的时候出现这个问题是由于少添加了权限。

    Caused by: java.lang.SecurityException: ConnectivityService: Neither user 10513 nor current process has android.permission.ACCESS_NETWORK_STATE.

    java.lang.RuntimeException: Unable to create application com.jiyun.wanandroid.WanApplication: java.lang.SecurityException: ConnectivityService: Neither user 10513 nor current process has android.permission.ACCESS_NETWORK_STATE.

    加上这几个权限就好了:

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    

    附上原文地址:https://www.jianshu.com/p/2fea980b3e56

    相关文章

      网友评论

        本文标题:超简单实现网络状态的监听

        本文链接:https://www.haomeiwen.com/subject/xmmlfctx.html