Android随笔3:如何判断网络断开

判断手机没网了,手机又有网了,看起来应该是个很基础的功能,但是我总感觉实现起来有点别扭。

以前判断网络是否连接的时候是ping一下服务器,这个方法目前看还是万无一失,ping不通App就没有数据,除非服务器挂了。

try {
      //-c 3  ping3次,-w 60  超时时间为60秒
      Process p = Runtime.getRuntime().exec("ping -c 3 -w 60 " + "服务器ip地址");
      return p.waitFor() == 0;
  } catch (Exception e) {
      e(e.toString(), TAG);
      return false;
  }

同时还使用了ConnectivityManager#getActiveNetworkInfo方法获取联网状态,比如用NetworkInfo#isConnected()判断网络是否连接,用NetworkInfo#getType()判断网络连接的类型。但是版本29之后NetworkInfo这个类被标记过期了。

NetworkInfo deprecated

推荐使用ConnectivityManager中的NetworkCallback获取网络连接的相关信息。或者通过getNetworkCapabilityes或者getNetworkLinkProperties同步获取。

之前获取网络变化状态的方法是通过注册广播CONNECTIVITY_ACTION,同样被标记过期


广播 deprecated

同时推荐了另外三个方法,这个三个方法就不是通过广播获取网络状态了,而是通过接口回调的方式异步返回信息。

研究一下新方法应该怎么用。还是在ConnectivityManager 这个类中。


ConnectivityManager

ConnectivityManager就是用来告诉我们网络的连接状态,以及通知网络连接发生了哪些变化。那么现在应该如何判断当前的网络状态?ConnectivityManager#getType的注释中给了一些提示。

/**
     * Reports the type of network to which the
     * info in this {@code NetworkInfo} pertains.
     * @return one of {@link ConnectivityManager#TYPE_MOBILE}, {@link
     * ConnectivityManager#TYPE_WIFI}, {@link ConnectivityManager#TYPE_WIMAX}, {@link
     * ConnectivityManager#TYPE_ETHERNET},  {@link ConnectivityManager#TYPE_BLUETOOTH}, or other
     * types defined by {@link ConnectivityManager}.
     * @deprecated Callers should switch to checking {@link NetworkCapabilities#hasTransport}
     *             instead with one of the NetworkCapabilities#TRANSPORT_* constants :
     *             {@link #getType} and {@link #getTypeName} cannot account for networks using
     *             multiple transports. Note that generally apps should not care about transport;
     *             {@link NetworkCapabilities#NET_CAPABILITY_NOT_METERED} and
     *             {@link NetworkCapabilities#getLinkDownstreamBandwidthKbps} are calls that
     *             apps concerned with meteredness or bandwidth should be looking at, as they
     *             offer this information with much better accuracy.
     */
    @Deprecated
    public int getType() {
        synchronized (this) {
            return mNetworkType;
        }
    }

这个提到了如果想判断当前网络连接的类型,可以用NetworkCapabilities#hasTransport以及NetworkCapabilities中的常量进行判断,随后又说app中可以不关心传输方式(transport)是怎样的,通过NetworkCapabilities#NET_CAPABILITY_NOT_METERED和NetworkCapabilities#getLinkDownstreamBandwidthKbps可以更准确的获取到我们想要的信息。

其中getLinkDownstreamBandwidthKbps就是字面意思,下载的带宽是多少,个人认为这也是判断网络是否连接的标准,有数据回来了网络就连接好了。有时连接到了路由器,但是网络欠费了,状态是连接成功,实际上确没网。

另外提到的NET_CAPABILITY_NOT_METERED,这个常量代表了是否是付费网络,比如手机连接的是wifi,那么就是not metered,未计量、不收费的,如果连接的是运营商的移动网络,那么就是metered,计量的、收费的。

最终判断网络是否连接的方式是,是否有数据传输回来:

NetworkCapabilities networkCapabilities = connectivityManager.getNetworkCapabilities(connectivityManager.getActiveNetwork());
int downstreamBandwidthKbps = networkCapabilities.getLinkDownstreamBandwidthKbps();
return downstreamBandwidthKbps > 0;

如果此时网络不是连通状态,那么获取到的networkCapabilities直接就是null。

接下来看看如何监听网络状态变化,文档里推荐了三个方法,其中requestNetwork方法需要Manifest.permission.CHANGE_NETWORK_STATE权限,或者是修改系统设置(android.provider.Settings.System)。修改系统设置很麻烦,debug模式无法设置,需要打包并给apk系统签名,然后跳转到一个修改系统页面手动允许。

我用手里的华为手机测试时,设置了CHANGE_NETWORK_STATE权限,就不需要修改系统设置了,官方文档对于这两种操作的说明也是用“or”进行连接的,所以暂时就认为两种操作取其一就可以了,如果其他手机既需要权限又需要修改系统设置,可能也可能。

registerNetworkCallback(NetworkRequest request, NetworkCallback networkCallback)方法就稍微省事一些,不涉及修改系统设置,只需要考虑如何设置NetworkRequest就行了,比如要监听移动网络和wifi。

NetworkRequest.Builder builder = new NetworkRequest.Builder();
builder.addTransportType(TRANSPORT_WIFI);
builder.addTransportType(TRANSPORT_CELLULAR);
NetworkRequest request = builder.build();

如果还是觉得麻烦,request也不想设置,因为还得弄明白request的参数都代表什么,可以直接用registerDefaultNetworkCallback(NetworkCallback networkCallback),监听系统默认的network,参数只有callback回调,不需要对请求进行设置,也可以监听移动网络和wifi的变化。

网络连接有变化时会在NetworkCallback回调中告知我们,NetworkCallback中的方法有:

mConnectivityManager.registerDefaultNetworkCallback(new ConnectivityManager.NetworkCallback(){
                                    
        @Override
        public void onAvailable(Network network) {
              super.onAvailable(network);
              //连接准备就绪时调用
        }

        @Override
        public void onLosing(Network network, int maxMsToLive) {
                super.onLosing(network, maxMsToLive);
                //断开连接时调用。随后会出现新的连接,紧接着触发onAvailable
        }

        @Override
        public void onLost(Network network) {
                super.onLost(network);
                //断开连接时调用,彻底断开,比如手动关闭手机网络。
        }

        @Override
        public void onUnavailable() {
              super.onUnavailable();
              //未发现适用的网络
        }

        @Override
        public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
              super.onCapabilitiesChanged(network, networkCapabilities);
              //Capabilities属性改变时调用
        }

        @Override
        public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {
              super.onLinkPropertiesChanged(network, linkProperties);
              //LinkProperties属性改变时调用
        }

});

当连接到新的网络时,onAvailable,onCapabilitiesChanged,onLinkPropertiesChanged,三个方法会依次出现。

在测试时发现,如果手动断开网络,仅仅会调用onLoss方法,再此手动打开手机网络,才会触发onAvailable。同时,在源码中发现了如下用法。


callback

在onAvailable中记录下当前连接的NetWork,然后在onLost中判断断开的是否为当前的连接的NetWork,如果是,则认为网络断开。

这里还有个细节需要弄清楚,手机是否一次只能连接一个Network,如果是,那么onAvailable和onLost组合才能正确判断网络是否连接。我在测试时,如果手机先连接了移动网络,在切换到Wi-Fi网络,会调用onAvailable方法,此时如果关闭Wi-Fi,会调用onLost方法,但此时手机为无网络状态(ping不通),并不会马上切换回移动网络,需要一小段时间才能切换回移动网络时,然后重新触发onAvailable方法。

所以判断网络断开的方式就可以是在onAvailable中确认网络连接,在onLost中确认网络断开:

public void onAvailable(Network network) {
        super.onAvailable(network);

        mCurrentNetwork = network;
        //网络连接 do sth
    }

    @Override
    public void onLost(Network network) {
        super.onLost(network);

        ConnectivityManager conn = (ConnectivityManager) mContext.getSystemService(Activity.CONNECTIVITY_SERVICE);
        if (conn == null) {
            return;
        }

        if (network.equals(mCurrentNetwork)) {
            mCurrentNetwork = null;

            //延迟3s执行,如果mCurrentNetwork仍为null,那么可能没有新的Network连接,则认为网络断开
            Handler handler = new Handler();
            handler.postDelayed(() -> {
                if (mCurrentNetwork == null ) {
                    //网络断开 do sth
                }
            }, 3000);
        }

    }

Google之所依废弃以前的方法,就是为了提供给我们更多,更准确网络信息。比如在onCapabilitiesChanged和onLinkPropertiesChanged两个回调方法中,把参数打印一下。
networkCapabilities.toString():

[ Transports: CELLULAR Capabilities: SUPL&INTERNET&NOT_RESTRICTED&TRUSTED&NOT_VPN&VALIDATED&FOREGROUND LinkUpBandwidth>=51200Kbps LinkDnBandwidth>=102400Kbps Specifier: <2>]

linkProperties.toString():


linkProperties.toString()

NetworkCapabilities,描述了网络的种类和带宽,原来描述手机网络只需要一个常量TYPE_MOBILE,但是随着手机网络的速度越来快,4G、5G的发展,简单的分类不足以描述越来越多的手机网络,于是Google采用了直接用带宽描述网络连接的方式,所以不直接给一个返回布尔值的方法判断网络连接状态也没啥好别扭的了。

LinkProperties 则包含地址、网关、dns、代理等信息。

如果想知道具体的网络种类,可以通过NetworkCapabilities#hasTransport方法,通过不同的参数,可以判断当前的连接的网络是什么,参数有7种。

TRANSPORT_CELLULAR:蜂窝网络
TRANSPORT_WIFI:wifi
TRANSPORT_BLUETOOTH:蓝牙拨号,手机通过蓝牙连接pc上的网络,网上有教程
TRANSPORT_ETHERNET:因特网,手机直接连接网线,网上有这样的转接头
TRANSPORT_VPN:vpn
TRANSPORT_WIFI_AWARE:这个没见过,据说场景大概是,比如你和一家商店通过这种协议进行了网络连接,下次你路过这家商店的时候,手机会直接收到这家店的商品信息
TRANSPORT_LOWPAN:ipv6网络

Google也提示我们不需要关心这些,关注是否计费和网络带宽就行。

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

推荐阅读更多精彩内容