在公司的一台Android球机设备上,制造商修改了系统,在连接wifi后,如果还插有sim卡,则网络是走sim卡的流量,不走wifi,所以此时我们ping wifi局域网的网关或局域网中别的ip是ping不通的,因为网络走的sim卡不走wifi。在这种情况下,一些判断wifi是否已连接的代码将失效(为什么此时我要判断wifi是否已连接?因为我要判断wifi是连接的话我就关闭sim卡数据流量,以让流量走wifi,如果wifi没连接我就开启sim卡数据流量,以让网络走sim卡。恶心的厂家乱改!)。
示例代码所需权限:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
fun isWifiConnected(): Boolean {
val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val networkInfo = cm.activeNetworkInfo
println("isConnected = ${networkInfo?.isConnected}")
println("isMobile = ${networkInfo?.type == ConnectivityManager.TYPE_MOBILE}")
return networkInfo != null && networkInfo.type == ConnectivityManager.TYPE_WIFI
}
用这个函数判断wifi是否连接是不准的,运行结果如下:
isConnected = true
isMobile = true
当前,设备是连接了wifi了的,但是由于系统被改为wifi和sim卡同时存在时走sim卡,所以这里获取的activeNetworkInfo
是 sim卡的,并不是 wifi的。
同理,用下面的函数判断wifi是否已连接也是不准确的,如下:
private fun isWifiConnected(): Boolean {
val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val activeNetwork = connectivityManager.activeNetwork
val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork)
return networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ?: false
}
下面的方式也不准:
fun isWifiConnected(): Boolean {
val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val networkInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI)
println("isConnected = ${networkInfo?.isConnected}")
return networkInfo?.isConnected ?: false
}
运行结果如下:
isConnected = false
另一种方式来自ChatGPT:
fun isWifiConnected(): Boolean {
val wifiManager = applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
println("networkId = ${wifiManager.connectionInfo.networkId}")
return wifiManager.connectionInfo.networkId != -1
}
运行结果如下:
networkId = -1
明明连接了wifi了,但是它竟然返回-1,获取ssid也获取不到,后来查看connectionInfo
的文档声明,发现它需要定位权限,于是把权限加上:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
加上权限再次运行就OK了,可以成功判断wifi是否连接。
另外我自己想了一个方式,因为wifi连接之后会分配到ip,所以我通过判断wifi网络接口是否有ip,有的话就是wifi已经连接上了,该方式需要网络权限,如下:
<uses-permission android:name="android.permission.INTERNET"/>
fun isWifiConnected(): Boolean {
val networkInterface = NetworkInterface.getByName("wlan0")
val inet4Address = networkInterface?.inetAddresses?.asSequence()?.find { it is Inet4Address }
println("ip = ${inet4Address?.hostAddress}")
return inet4Address != null
}
运行结果如下:
192.168.1.173
断开wifi连接再运行,结果如下:
ip = null
这里我们取了ipv4的ip地址,简单一点应该是只要有ip就行,不管你是ipv4还是ipv6,如下:
fun isWifiConnected(): Boolean {
val networkInterface = NetworkInterface.getByName("wlan0")
return networkInterface?.inetAddresses?.hasMoreElements() ?: false
}
这种方式虽然实现了我们需要的功能,但总感觉不是很正规的样子,Android官方有提供正规的方式,如 下:
fun isWifiConnected(): Boolean {
val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val allNetworks = connectivityManager.allNetworks
for (network in allNetworks) {
val networkCapabilities = connectivityManager.getNetworkCapabilities(network)
if (networkCapabilities != null && networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
return true
}
}
return false
}
在IDE中显示connectivityManager.allNetworks
是过时的,然后推荐了另一种方式,如下:
fun isWifiConnected() {
val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val request = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.build()
val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: android.net.Network) {
println("wifi已连接")
}
override fun onLost(network: android.net.Network) {
println("wifi未连接")
}
}
connectivityManager.registerNetworkCallback(request, networkCallback)
}
如上方法,当wifi是连接的情况下,它就像是个粘性广播,一调用register函数就会立马收到回调,但是如果wifi是未连接的情况下,调用register函数时不会收到回调,所以这点不是很好,感觉像是Android官方Bug,你推荐我们用这种方式,但是当wifi未连接时,调用register函数确实收不到回调通知。当我们已经调用注册了,此时才把wifi断开,这是可以收到回调的。所以,如果要监听wifi连接、断开的事件,可以使用这个注册回调的方式,如果要主动获取,则还是使用前一种方式,或者使用判断是否有ip的方式。
总结:可以准确获取wifi连接状态的方式有4种,如下:
fun isWifiConnected(): Boolean {
val networkInterface = NetworkInterface.getByName("wlan0")
return networkInterface?.inetAddresses?.hasMoreElements() ?: false
}
fun isWifiConnected(): Boolean {
val wifiManager = applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
return wifiManager.connectionInfo.networkId != -1
}
fun isWifiConnected(): Boolean {
val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val allNetworks = connectivityManager.allNetworks
for (network in allNetworks) {
val networkCapabilities = connectivityManager.getNetworkCapabilities(network)
if (networkCapabilities != null && networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
return true
}
}
return false
}
fun isWifiConnected() {
val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val request = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.build()
val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: android.net.Network) {
println("wifi已连接")
}
override fun onLost(network: android.net.Network) {
println("wifi未连接")
}
}
connectivityManager.registerNetworkCallback(request, networkCallback)
}