一、粗略版本的网络监听
这是一个粗略版本的网络监听,能满足实时性不是特别高的需求,如果要做到实时性特别高例如连上WIFI热点后一直判断热点是否有网,那么难免需要定时去ping或做一些其他动作来支持。代码主要思路是继承LiveData实现的,代码如下:
import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import androidx.lifecycle.LiveData
/**
* 网络变化的监听
* 连上网络后会ping一次公共CDN确认有无网络
* 连上网络就真假WIFI变化,这个类没有实现,因为如果要做的话需要连上网络一直ping,代价有点大
*/
class NetworkConnection(context: Context) : LiveData<Boolean>() {
private val connectivityManager: ConnectivityManager = context.getSystemService(
Context.CONNECTIVITY_SERVICE
) as ConnectivityManager
private val networkConnectionCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
checkInternetConnection()
}
override fun onLost(network: Network) {
postValue(false)
}
override fun onCapabilitiesChanged(
network: Network,
capabilities: NetworkCapabilities
) {
updateNetworkConnection(capabilities)
}
}
override fun onActive() {
super.onActive()
updateNetworkConnection()
try {
connectivityManager.registerDefaultNetworkCallback(networkConnectionCallback)
} catch (e: Exception) {
Log.e(e,"NetworkConnection register callback failed")
}
}
override fun onInactive() {
super.onInactive()
try {
connectivityManager.unregisterNetworkCallback(networkConnectionCallback)
} catch (e: Exception) {
// ignore
}
}
private fun updateNetworkConnection(capabilities: NetworkCapabilities? = null) {
val realCapabilities = capabilities ?: connectivityManager.getNetworkCapabilities(
connectivityManager.activeNetwork
)
val isConnected = realCapabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) == true
postValue(isConnected)
}
private fun checkInternetConnection() {
//ping Google CDN(国内要注意不能用)
val command = arrayOf("/system/bin/ping", "-c", "1", "8.8.8.8")
val isConnected = try {
val process = Runtime.getRuntime().exec(command)
val result = process.waitFor() == 0
process.inputStream.close()
process.errorStream.close()
true
} catch (e: Exception) {
false
}
postValue(isConnected)
}
}
项目中使用很简单,如下:
NetworkConnection(requireContext()).observe(viewLifecycleOwner) {
//UI层面的处理
binding.llNetworkError.isGone = it
//如果之前的网络状态是断开,那么网络恢复后是否需要刷新一次数据
if (!mLastNetWorkState && it) {
binding.swipeRefresh.isRefreshing = true
//请求一次新数据
requestData()
}
mLastNetWorkState = it
}
基于LiveData绑定页面周期的特性自动注册或反注册网络的监听,并且回到前台会重新判断一次网络,日常用基本能满足需求。
二、实时检测的网络状态监听
有些时候需要实时的网络检测,因为可能涉及MQTT、Sockect等的重连,下面是一个示例:
class NetworkConnection(context: Context) {
private val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
/** 定义事件类型 */
private sealed class NetworkEvent {
data object MaybeChanged : NetworkEvent() // 网络可能变化,需要 ping
data object Lost : NetworkEvent() // 网络明确断开,不用 ping
}
// Flow 用于触发网络状态变化
private val _networkEvents = MutableSharedFlow<NetworkEvent>(extraBufferCapacity = 1)
/**
* 对外暴露 LiveData
*/
@OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class)
val connectedLiveData: LiveData<Boolean> = _networkEvents
.debounce { event -> if (event is NetworkEvent.MaybeChanged) 300 else 0 } // 防抖,只对可能变化的事件
.mapLatest { event ->
when (event) {
is NetworkEvent.MaybeChanged -> checkInternetConnection()
is NetworkEvent.Lost -> false
}
}
//.distinctUntilChanged() //根据实际情况是否去重
.asLiveData()
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
triggerNetworkEvent(NetworkEvent.MaybeChanged)
}
override fun onLost(network: Network) {
triggerNetworkEvent(NetworkEvent.Lost)
}
override fun onCapabilitiesChanged(network: Network, capabilities: NetworkCapabilities) {
triggerNetworkEvent(NetworkEvent.MaybeChanged)
}
override fun onLinkPropertiesChanged(network: Network, lp: LinkProperties) {
triggerNetworkEvent(NetworkEvent.MaybeChanged)
}
}
init {
try {
connectivityManager.registerDefaultNetworkCallback(networkCallback)
} catch (_: Exception) {
//ignore
}
// 初始检测
triggerNetworkEvent(NetworkEvent.MaybeChanged)
}
private fun triggerNetworkEvent(event: NetworkEvent) {
scope.launch { _networkEvents.emit(event) }
}
/**
* 真实网络检测函数,通过PING
*/
private suspend fun checkInternetConnection(): Boolean = withContext(Dispatchers.IO) {
try {
NetworkUtil.pingRobust()
} catch (_: IOException) {
false
}
}
/**
* 周期性检测网络(兜底),默认间隔5秒
*/
fun startPeriodicCheck(intervalMs: Long = 5000L) {
scope.launch {
while (isActive) {
_networkEvents.emit(NetworkEvent.MaybeChanged)
delay(intervalMs)
}
}
}
fun stop() {
try {
connectivityManager.unregisterNetworkCallback(networkCallback)
} catch (_: Exception) {
}
scope.cancel()
}
}
ping的代码:
/**
* ping google CDN check network.最多ping 3次以提高ping的健壮性
* 放在子线程
*/
suspend fun pingRobust(retries: Int = 3, timeoutMs: Long = 1000): Boolean {
repeat(retries) {
if (pingOnce(timeoutMs)) return true
}
return false
}
private suspend fun pingOnce(timeoutMs: Long): Boolean = withContext(Dispatchers.IO) {
try {
val process = Runtime.getRuntime().exec(arrayOf("/system/bin/ping", "-c", "1", "-W", "${timeoutMs / 1000}", "8.8.8.8"))
val result = process.waitFor() == 0
process.inputStream.close()
process.errorStream.close()
result
} catch (_: Exception) {
false
}
}
实际使用:
//订阅监听网络的变化,根据需求选择observer或observeForever
networkConnection?.connectedLiveData?.observeForever { isNetworkAvailable ->
//do something
}
//开始周期性检测网络的变化
networkConnection?.startPeriodicCheck()