Android 基于NetworkCallback的网络状态监听框架

前言

在Android API 28以前,如果需要监听网络状态来做一些操作的话,我用的是广播监听,也就是常见的android.net.conn.CONNECTIVITY_CHANGE,而现在。。。看着一堆的/** @deprecated */注释我们就知道,这些方法命不久矣,也好,趁手在学习Kotlin的时候重新写一个,熟悉下语法和快捷键。
在这里插入图片描述
那我们用什么来监听网络状态变化呢?
答案是ConnectivityManager.NetworkCallback()

解决

最后会给出源码,懒得看思路的可以直接拉到最后。

了解NetworkCallback

这里是官网文档:ConnectivityManager.NetworkCallback
下面是回调方法,通过复写相关方法,达到监听的目的

方法 介绍
onAvailable(Network network) 网络连接成功,通知可以使用的时候调用
onBlockedStatusChanged(Network network, boolean blocked) 当访问指定的网络被阻止或解除阻塞时调用
onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) 当网络状态修改但仍旧是可用状态时调用
onLinkPropertiesChanged(Network network, LinkProperties linkProperties) 当网络连接的属性被修改时调用
onLosing(Network network, int maxMsToLive) 当网络正在断开连接时调用
onLost(Network network) 当网络已断开连接时调用
onUnavailable() 当网络连接超时或网络请求达不到可用要求时调用

显而易见的是,我们一般只需要监听onAvailableonLostonCapabilitiesChanged就能达到监听网络状态的效果。

继承NetworkCallback

我用了一个Map来存储注册监听网络监听的活动和它的监听方法,在网络变化时,通过反射一个个的将状态传送过去。
直接看代码:

/**
 * 网络监听
 *
 * @author D10NG
 * @date on 2019-10-22 11:13
 */
class NetStatusCallBack : ConnectivityManager.NetworkCallback() {

    // 观察者,key=类、value=方法
    private val checkManMap = HashMap<Any, Method>()

    // 网络状态记录
    @Volatile
    private var netType : @NetType String = NetType.NET_UNKNOWN

    override fun onAvailable(network: Network) {
        super.onAvailable(network)
        Log.i(Constants.TAG, "net connect success! 网络已连接")
    }

    override fun onLost(network: Network) {
        super.onLost(network)
        Log.i(Constants.TAG, "net disconnect! 网络已断开连接")
        post(NetType.NONE)
    }

    override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
        super.onCapabilitiesChanged(network, networkCapabilities)
        Log.i(Constants.TAG, "net status change! 网络连接改变")
        // 表明此网络连接成功验证
        if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
            if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
                networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI_AWARE)) {
                // 使用WI-FI
                post(NetType.WIFI)
            } else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||
                networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) ) {
                // 使用蜂窝网络
                post(NetType.NET)
            } else{
                // 未知网络,包括蓝牙、VPN、LoWPAN
                post(NetType.NET_UNKNOWN)
            }
        }
    }

    // 执行
    private fun post(str: @NetType String) {
        netType = str
        val set: Set<Any> = checkManMap.keys
        for (obj in set) {
            val method = checkManMap[obj] ?: continue
            invoke(obj, method, str)
        }
    }

    // 反射执行
    private fun invoke(obj: Any, method: Method, type: @NetType String) {
        try {
            method.invoke(obj, type)
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    // 注册
    fun register(obj: Any) {
        val clz = obj.javaClass
        if (!checkManMap.containsKey(clz)) {
            val method = findAnnotationMethod(clz) ?: return
            checkManMap[obj] = method
        }
    }

    // 取消注册
    fun unRegister(obj: Any) {
        checkManMap.remove(obj)
    }

    // 取消所有注册
    fun unRegisterAll() {
        checkManMap.clear()
    }

    // 获取状态
    fun getNetType() : @NetType String = netType

    // 查找监听的方法
    private fun findAnnotationMethod(clz: Class<*>) : Method? {
        val method = clz.methods
        for (m in method) {
            // 看是否有注解
            m.getAnnotation(DLNet::class.java) ?: continue
            // 判断返回类型
            val genericReturnType = m.genericReturnType.toString()
            if ("void" != genericReturnType) {
                // 方法的返回类型必须为void
                throw RuntimeException("The return type of the method【${m.name}】 must be void!")
            }
            // 检查参数,必须有一个String型的参数
            val parameterTypes = m.genericParameterTypes
            if (parameterTypes.size != 1 || parameterTypes[0].toString() != "class ${String::class.java.name}") {
                throw RuntimeException("The parameter types size of the method【${m.name}】must be one (type name must be java.lang.String)!")
            }
            return m
        }
        return null
    }
}

额外工具

还有两个我常常需要用到的功能:
1、检查当前连接的网络是否为5G WI-FI
2、获取当前连接Wi-Fi名

/**
 * 网络工具
 *
 * @author D10NG
 * @date on 2019-10-22 10:29
 */
object NetUtils {

    /**
     * 检查当前连接的网络是否为5G WI-FI
     */
    fun is5GWifiConnected(context: Context) : Boolean {
        val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
        val wifiInfo = wifiManager.connectionInfo?: return false
        // 频段
        var frequency = 0
        if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.LOLLIPOP) {
            frequency = wifiInfo.frequency
        } else {
            val ssid = wifiInfo.ssid?: return false
            if (ssid.length < 2) return false
            val sid = ssid.substring(1, ssid.length -1)
            for (scan in wifiManager.scanResults) {
                if (scan.SSID == sid) {
                    frequency = scan.frequency
                    break
                }
            }
        }
        return frequency in 4900..5900
    }

    /**
     * 获取当前连接Wi-Fi名
     * # 如果没有定位权限,获取到的名字将为  unknown ssid
     */
    fun getConnectedWifiSSID(context: Context) : String {
        val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
        val wifiInfo = wifiManager.connectionInfo?: return ""
        return wifiInfo.ssid.substring(1, wifiInfo.ssid.length -1)
    }
}

使用

另外,我已经打包成依赖库,可以通过以下方式直接使用

添加依赖

1、在根目录的build.gradle里插入

	allprojects {
		repositories {
			...
			maven { url 'https://jitpack.io' }
		}
	}

2、在app的build.gradle里插入

	dependencies {
	        implementation 'com.github.D10NGYANG:NetStatusManager:1.0'
	}

使用代码

1、注册监听

    override fun onStart() {
        super.onStart()
        DLNetManager.getInstance(this.application).register(this)
    }

2、取消监听

    override fun onStop() {
        super.onStop()
        DLNetManager.getInstance(this.application).unRegister(this)
    }

3、取消监听所有

    override fun onDestroy() {
        DLNetManager.getInstance(this.application).unRegisterAll()
        super.onDestroy()
    }

4、监听状态变化

    @DLNet
    fun onNetStatusChange(str: @NetType String) {
        // 网络状态改变
        Log.e("测试", "Main网络状态改变:${str}")
        //binding.netType = str
        if (str == NetType.WIFI) {
            if (NetUtils.is5GWifiConnected(this)) {
                Log.e("测试", "这是5G WI-FI")
            } else{
                Log.e("测试", "这是2.4G WI-FI")
            }
            Log.e("测试", "WI-FI名:${NetUtils.getConnectedWifiSSID(this)}")
        }
    }

GitHub

https://github.com/D10NGYANG/NetStatusManager

发布了103 篇原创文章 · 获赞 31 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/sinat_38184748/article/details/102684881