美文网首页Android过时方法处理Android适配经验简化开发
Android网络状态获取及NetworkCallback简述

Android网络状态获取及NetworkCallback简述

作者: 静水红阳 | 来源:发表于2021-03-30 14:22 被阅读0次

    前言

    在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官方文档

    相关文章

      网友评论

        本文标题:Android网络状态获取及NetworkCallback简述

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