网络状态监听
源:Android ConnectivityManager | iOS NWPathMonitor
监听网络连接状态是应用处理离线场景的基础。本文将展示如何跨平台监听网络状态变化。
平台差异对比
| 平台 | 原生 API | 支持能力 | 权限要求 |
|---|---|---|---|
| Android | ConnectivityManager + NetworkCallback | 网络类型、是否计费、实时监听 | ACCESS_NETWORK_STATE |
| iOS | NWPathMonitor | 网络类型、是否昂贵、实时监听 | 无需权限 |
| Desktop | java.net.NetworkInterface | 基础检测 | 无需权限 |
expect/actual 实现方案
标准代码块
kotlin
enum class NetworkType {
WIFI,
CELLULAR,
ETHERNET,
NONE
}
data class NetworkStatus(
val isConnected: Boolean,
val type: NetworkType,
val isMetered: Boolean = false
)
expect class NetworkMonitor {
fun startMonitoring(callback: (NetworkStatus) -> Unit)
fun stopMonitoring()
fun getCurrentStatus(): NetworkStatus
}kotlin
import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
actual class NetworkMonitor(private val context: Context) {
private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE)
as ConnectivityManager
private var networkCallback: ConnectivityManager.NetworkCallback? = null
actual fun startMonitoring(callback: (NetworkStatus) -> Unit) {
val request = NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build()
networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
callback(getCurrentStatus())
}
override fun onLost(network: Network) {
callback(getCurrentStatus())
}
}
connectivityManager.registerNetworkCallback(request, networkCallback!!)
}
actual fun stopMonitoring() {
networkCallback?.let {
connectivityManager.unregisterNetworkCallback(it)
}
networkCallback = null
}
actual fun getCurrentStatus(): NetworkStatus {
val network = connectivityManager.activeNetwork
val capabilities = connectivityManager.getNetworkCapabilities(network)
if (capabilities == null) {
return NetworkStatus(false, NetworkType.NONE)
}
val type = when {
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> NetworkType.WIFI
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> NetworkType.CELLULAR
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> NetworkType.ETHERNET
else -> NetworkType.NONE
}
val isMetered = !capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
return NetworkStatus(
isConnected = capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET),
type = type,
isMetered = isMetered
)
}
}kotlin
import platform.Network.*
import platform.Foundation.*
actual class NetworkMonitor {
private var monitor: NWPathMonitor? = null
private var currentCallback: ((NetworkStatus) -> Unit)? = null
actual fun startMonitoring(callback: (NetworkStatus) -> Unit) {
currentCallback = callback
monitor = NWPathMonitor()
monitor?.pathUpdateHandler = { path ->
val status = pathToStatus(path)
callback(status)
}
val queue = dispatch_get_global_queue(QOS_CLASS_BACKGROUND.toLong(), 0u)
monitor?.start(queue = queue)
}
actual fun stopMonitoring() {
monitor?.cancel()
monitor = null
currentCallback = null
}
actual fun getCurrentStatus(): NetworkStatus {
val path = monitor?.currentPath
return if (path != null) {
pathToStatus(path)
} else {
NetworkStatus(false, NetworkType.NONE)
}
}
private fun pathToStatus(path: NWPath): NetworkStatus {
val isConnected = path.status == NWPathStatusSatisfied
val type = when {
path.usesInterfaceType(NWInterfaceTypeWifi) -> NetworkType.WIFI
path.usesInterfaceType(NWInterfaceTypeCellular) -> NetworkType.CELLULAR
path.usesInterfaceType(NWInterfaceTypeWiredEthernet) -> NetworkType.ETHERNET
else -> NetworkType.NONE
}
return NetworkStatus(
isConnected = isConnected,
type = type,
isMetered = path.isExpensive
)
}
}kotlin
actual class NetworkMonitor {
actual fun startMonitoring(callback: (NetworkStatus) -> Unit) {
// Desktop 简化实现
}
actual fun stopMonitoring() {
}
actual fun getCurrentStatus(): NetworkStatus {
return try {
val hasInternet = java.net.InetAddress.getByName("google.com").isReachable(3000)
NetworkStatus(hasInternet, NetworkType.ETHERNET)
} catch (e: Exception) {
NetworkStatus(false, NetworkType.NONE)
}
}
}依赖补充
AndroidManifest.xml 配置
xml
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />实战坑点
Android 后台更新限制
API 28+ 限制
Android 9+ 后台应用接收网络状态变化的频率受限。