Android网络状态获取及NetworkCallback简述

前言

在APP的开发中,获取到网络的链接状态是一个经常使用到的方法。除了可以使用ping指令来判断当前的网络状况之外,还可以直接通过ConnectivityManager来对网络状态进行判断。

一、网络判断旧方法(deprecated)

权限申请

如果要获取网络信息,首先是需要申请网络权限:

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
判断网络

使用ConnectivityManager可以十分方便的直接判断网络,调用方法如下:

    //判断网络是否连接
    fun isNetworkConnected(context: Context?): Boolean {
        if (context != null) {
            val mConnectivityManager = context
                .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            val mNetworkInfo = mConnectivityManager.activeNetworkInfo
            if (mNetworkInfo != null) {
                return mNetworkInfo.isAvailable
            }
        }
        return false
    }
        //判断WiFi是否连接
    fun isWifiConnected(context: Context?): Boolean {
        if (context != null) {
            val mConnectivityManager = context
                .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            val mWiFiNetworkInfo = mConnectivityManager
                .getNetworkInfo(ConnectivityManager.TYPE_WIFI)
            if (mWiFiNetworkInfo != null) {
                return mWiFiNetworkInfo.isAvailable
            }
        }
        return false
    }

    //判断移动网络是否连接
    fun isMobileConnected(context: Context?): Boolean {
        if (context != null) {
            val mConnectivityManager = context
                .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            val mMobileNetworkInfo = mConnectivityManager
                .getNetworkInfo(ConnectivityManager.TYPE_MOBILE)
            if (mMobileNetworkInfo != null) {
                return mMobileNetworkInfo.isAvailable
            }
        }
        return false
    }

    //获取连接网络的网络信息
    fun getConnectedType(context: Context?): Int {
        if (context != null) {
            val mConnectivityManager = context
                .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            val mNetworkInfo = mConnectivityManager.activeNetworkInfo
            if (mNetworkInfo != null && mNetworkInfo.isAvailable) {
                return mNetworkInfo.type
            }
        }
        return -1
    }

但是在完成代码之后却发现有些方法已经被标记为deprecated了,如isAvailable()getActiveNetworkInfo()方法。查看代码注释,其中提示我们应该使用ConnectivityManager.NetworkCallback去监听网络连接变化,并使用registerNetworkCallback去注册监听,内容如下:

     * @deprecated Apps should instead use the
     *             {@link android.net.ConnectivityManager.NetworkCallback} API to
     *             learn about connectivity changes.
     *             {@link ConnectivityManager#registerDefaultNetworkCallback} and
     *             {@link ConnectivityManager#registerNetworkCallback}. These will
     *             give a more accurate picture of the connectivity state of
     *             the device and let apps react more easily and quickly to changes.

接下来,我们对此网络监听api进行简单说明。

二、网络监听

ConnectivityManager.NetworkCallback是一个抽象类,具有如下几个方法:

方法 描述
onAvailable(network: Network) 网络连接成功回调
onUnavailable() 网络连接超时或网络不可达
onLost(Network network) 网络已断开连接
onLosing(Network network, int maxMsToLive) 网络正在丢失连接
onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) 网络状态变化
onLinkPropertiesChanged(Network network, LinkProperties linkProperties) 网络连接属性变化
onBlockedStatusChanged(Network network, boolean blocked) 访问的网络阻塞状态发生变化

其中最为常用的方法为onAvailable,onLost,onCapabilitiesChanged方法。

我们定义一个继承自ConnectivityManager.NetworkCallback的子类:NetUtil,示例代码如下:

package com.example.demowork1.util

import android.content.Context
import android.net.ConnectivityManager
import android.net.LinkProperties
import android.net.Network
import android.net.NetworkCapabilities


class NetUtil: ConnectivityManager.NetworkCallback() {

    //网络连接成功
    override fun onAvailable(network: Network) {
        LogUtil.instance.d("网络连接成功")
        super.onAvailable(network)
    }

    //网络已断开连接
    override fun onLost(network: Network) {
        LogUtil.instance.d("网络已断开连接")
        super.onLost(network)
    }

    override fun onLosing(network: Network, maxMsToLive: Int) {
        LogUtil.instance.d("网络正在断开连接")
        super.onLosing(network, maxMsToLive)
    }

    //无网络
    override fun onUnavailable() {
        LogUtil.instance.d("网络连接超时或者网络连接不可达")
        super.onUnavailable()
    }

    //当网络状态修改(网络依然可用)时调用
    override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
        super.onCapabilitiesChanged(network, networkCapabilities)
        LogUtil.instance.d( "net status change! 网络连接改变")
    }

    //当访问的网络被阻塞或者解除阻塞时调用
    override fun onBlockedStatusChanged(network: Network, blocked: Boolean) {
        super.onBlockedStatusChanged(network, blocked)
    }

    //当网络连接属性发生变化时调用
    override fun onLinkPropertiesChanged(network: Network, linkProperties: LinkProperties) {
        super.onLinkPropertiesChanged(network, linkProperties)
    }
    ...
}

onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities)是我们较为常用的网络状态发生变化时的监听,其中的参数networkCapabilities包含有一些我们常用的api ,其中保罗有两个重要方法,如下:

  1. hasCapability
  2. hasTransport

hasCapability可以判断网络是否连接,有多个参数(参数可以查看接口NetCapability),其中常用参数如下:

参数 说明
NetworkCapabilities.NET_CAPABILITY_INTERNET 表示是否连接上了互联网(不关心是否可以上网)
NetworkCapabilities.NET_CAPABILITY_VALIDATED 表示能够和互联网通信(这个为true表示能够上网)

hasTransport可以判断网络的类型,同样有多个参数(参数可以查看接口Transport),其中常用的参数如下:

参数 说明
TRANSPORT_WIFI 表示接入的是WIFI网络
TRANSPORT_CELLULAR 表示接入的是数据网络
TRANSPORT_BLUETOOTH 表示接入的是蓝牙

具体的调用示例如下:

    //当网络状态修改(网络依然可用)时调用
    override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
        super.onCapabilitiesChanged(network, networkCapabilities)
        LogUtil.instance.d( "net status change! 网络连接改变")
        // 表明此网络连接成功验证
        if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
            if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
                // 使用WI-FI
                LogUtil.instance.d("当前在使用WiFi上网")
            } else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
                // 使用数据网络
                LogUtil.instance.d("当前在使用数据网络上网")
            } else{
                LogUtil.instance.d("当前在使用其他网络")
                // 未知网络,包括蓝牙、VPN等
            }
        }
    }
监听调用

具体的调用实现如下:

    private fun setNetListener() {
        var request = NetworkRequest.Builder().build()
        var connMgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        connMgr.registerNetworkCallback(request, NetUtil.instance)
    }

三、 同步网络状态获取

需要注意的是,虽然Google提供了此Callback方法来获取网络状态的,但是此回调是异步的,如果需要同步获取网络状态,需要调用如下方法getNetworkCapabilities(Network network)方法来获取当前NetWork的连接状态,示例代码如下:

                val network = mConnectivityManager.activeNetwork ?: return false
                val status = mConnectivityManager.getNetworkCapabilities(network)
                        ?: return false
                if (status.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
                    return true
                }

但是此方法是出现在Android M之后的SDK中,所以同步获取网络连接状态的代码还需要对Android的版本进行判断,最终代码如下:

    /**
     * 判断网络是否连接
     */
    fun isNetworkConnected(context: Context?): Boolean {
        if (context != null) {
            val mConnectivityManager = context
                    .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                val mNetworkInfo = mConnectivityManager.activeNetworkInfo
                if (mNetworkInfo != null) {
                    return mNetworkInfo.isAvailable
                }
            } else {
                val network = mConnectivityManager.activeNetwork ?: return false
                val status = mConnectivityManager.getNetworkCapabilities(network)
                        ?: return false
                if (status.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
                    return true
                }
            }
        }
        return false
    }

    /**
     * 判断是否是WiFi连接
     */
    fun isWifiConnected(context: Context?): Boolean {
        if (context != null) {
            val mConnectivityManager = context
                    .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                val mWiFiNetworkInfo = mConnectivityManager
                        .getNetworkInfo(ConnectivityManager.TYPE_WIFI)
                if (mWiFiNetworkInfo != null) {
                    return mWiFiNetworkInfo.isAvailable
                }
            } else {
                val network = mConnectivityManager.activeNetwork ?: return false
                val status = mConnectivityManager.getNetworkCapabilities(network)
                        ?: return false
                if (status.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
                    return true
                }
            }
        }
        return false
    }

    /**
     * 判断是否是数据网络连接
     */
    fun isMobileConnected(context: Context?): Boolean {
        if (context != null) {
            val mConnectivityManager = context
                    .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                val mMobileNetworkInfo = mConnectivityManager
                        .getNetworkInfo(ConnectivityManager.TYPE_MOBILE)
                if (mMobileNetworkInfo != null) {
                    return mMobileNetworkInfo.isAvailable
                }
            } else {
                val network = mConnectivityManager.activeNetwork ?: return false
                val status = mConnectivityManager.getNetworkCapabilities(network)
                        ?: return false
                status.transportInfo
                if (status.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
                    return true
                }
            }
        }
        return false
    }

总结

本文主要是对于获取网络状态以及网络监听进行说明,需要注意的是对于同步和异步的区分。

参考文章

google官方文档

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

推荐阅读更多精彩内容