在Android API 28以前,如果需要监听网络状态来做一些操作的话,我用的是广播监听,也就是常见的android.net.conn.CONNECTIVITY_CHANGE
,而现在。。。看着一堆的/** @deprecated */
注释我们就知道,这些方法命不久矣,也好,趁手在学习Kotlin的时候重新写一个,熟悉下语法和快捷键。
那我们用什么来监听网络状态变化呢?
答案是ConnectivityManager.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() | 当网络连接超时或网络请求达不到可用要求时调用 |
显而易见的是,我们一般只需要监听onAvailable
、onLost
、onCapabilitiesChanged
就能达到监听网络状态的效果。
我用了一个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)}")
}
}
-keep class com.dlong.netstatus.** {*;}
-dontwarn com.dlong.netstatus.**
-keepclassmembers class * {
@com.dlong.netstatus.annotation.DLNet <methods>;
@com.dlong.netstatus.annotation.NetType <fields>;
}
https://github.com/D10NGYANG/NetStatusManager