Android设备WiFi已开启但无网络连接的检测方案需求
检测WiFi已开启但无互联网连接的解决方案
原代码仅监听网络的物理连接状态(是否有WiFi/蜂窝网络连接),但无法区分网络已连接但无互联网访问的场景(比如WiFi连接到无网的路由器)。以下是两种可行的解决方案:
方案一:利用Android系统NetworkCapabilities验证
Android API 23及以上提供NetworkCapabilities接口,可直接判断网络是否具备真实的互联网访问能力。核心是检查NET_CAPABILITY_VALIDATED(系统验证该网络可访问互联网)和NET_CAPABILITY_INTERNET(网络具备互联网访问的基础能力)。
完整修改代码
首先更新ConnectivityObserver的状态枚举,新增对应状态:
interface ConnectivityObserver { enum class Status { Available, // 有网络且可访问互联网 ConnectedWithoutInternet, // 网络已连接但无互联网访问 Losing, Lost, Unavailable } fun observe(): Flow<Status> }
修改后的NetworkConnectivityObserver实现:
class NetworkConnectivityObserver( private val context: Context ) : ConnectivityObserver { private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager private val networkRequest = NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) .build() override fun observe(): Flow<ConnectivityObserver.Status> { return callbackFlow { val callback = object : ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network) { super.onAvailable(network) launch { send(getNetworkStatus(network)) } } override fun onCapabilitiesChanged( network: Network, networkCapabilities: NetworkCapabilities ) { super.onCapabilitiesChanged(network, networkCapabilities) launch { send(getNetworkStatus(network)) } } override fun onLosing(network: Network, maxMsToLive: Int) { super.onLosing(network, maxMsToLive) launch { send(ConnectivityObserver.Status.Losing) } } override fun onLost(network: Network) { super.onLost(network) launch { send(ConnectivityObserver.Status.Lost) } } override fun onUnavailable() { super.onUnavailable() launch { send(ConnectivityObserver.Status.Unavailable) } } } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { connectivityManager.registerDefaultNetworkCallback(callback) } else { connectivityManager.registerNetworkCallback(networkRequest, callback) } awaitClose { connectivityManager.unregisterNetworkCallback(callback) } }.distinctUntilChanged() } private fun getNetworkStatus(network: Network): ConnectivityObserver.Status { val capabilities = connectivityManager.getNetworkCapabilities(network) val hasInternet = capabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) == true val isValidated = capabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) == true return when { hasInternet && isValidated -> ConnectivityObserver.Status.Available hasInternet -> ConnectivityObserver.Status.ConnectedWithoutInternet else -> ConnectivityObserver.Status.Unavailable } } }
关键说明
NET_CAPABILITY_VALIDATED:系统通过内部验证确认该网络可访问互联网- 重写
onCapabilitiesChanged方法,确保网络能力变化时及时更新状态 - 需在Manifest中添加权限:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
方案二:主动网络探测(补充方案)
系统验证在部分场景下可能不准确(比如内网环境需访问特定公网资源),此时可通过主动发起轻量网络请求验证连通性。
实现逻辑
- 网络连接状态变化时,定期发起HEAD请求到可靠的轻量资源(如Google的204无内容接口,流量极小)
- 根据请求结果判断是否有互联网访问
- 结合系统监听避免无效请求
示例代码
在原Flow中添加主动探测逻辑:
override fun observe(): Flow<ConnectivityObserver.Status> { return callbackFlow { // 原有callback注册逻辑保持不变 }.distinctUntilChanged() .flatMapLatest { status -> if (status == ConnectivityObserver.Status.Available || status == ConnectivityObserver.Status.ConnectedWithoutInternet) { flow { while (true) { val hasInternet = isInternetAvailable() emit(if (hasInternet) ConnectivityObserver.Status.Available else ConnectivityObserver.Status.ConnectedWithoutInternet) delay(5000) // 每5秒检测一次,可按需调整 } } } else { flow { emit(status) } } }.distinctUntilChanged() } private fun isInternetAvailable(): Boolean { return try { // 可根据地区替换为国内可靠接口,如https://connect.rom.miui.com/generate_204 val url = URL("https://www.google.com/generate_204") val connection = url.openConnection() as HttpURLConnection connection.connectTimeout = 3000 connection.readTimeout = 3000 connection.requestMethod = "HEAD" connection.responseCode in 200..399 } catch (e: Exception) { false } }
注意事项
- 需添加权限:
<uses-permission android:name="android.permission.INTERNET" /> - 避免过于频繁的请求,防止消耗过多流量
- 可根据用户所在地区替换探测接口,提升准确性
内容的提问来源于stack exchange,提问作者Krupali Shingala
相关产品推荐
相关产品推荐

