设备旋转时NetworkCallback回调两次,启动时仅一次的原因排查
我实现了一个检测网络连接的工具类NetworkUtils,通过Hilt提供单例实例。应用首次启动时,"Connected"日志仅打印一次,但旋转设备时会打印两次。另一个未使用Navigation架构组件的项目中,旋转设备时日志仅打印一次,这是为什么?
NetworkUtils实现代码
class NetworkUtils(context: Context) : ConnectivityManager.NetworkCallback() { private val networkLiveData: MutableLiveData<Boolean> = MutableLiveData() private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager fun getNetworkLiveData(): LiveData<Boolean> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { connectivityManager.registerDefaultNetworkCallback(this) } else { val builder = NetworkRequest.Builder() connectivityManager.registerNetworkCallback(builder.build(), this) } var isConnected = false connectivityManager.allNetworks.forEach { network -> val networkCapability = connectivityManager.getNetworkCapabilities(network) networkCapability?.let { if (it.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { isConnected = true return@forEach } } } networkLiveData.postValue(isConnected) return networkLiveData } override fun onAvailable(network: Network) { networkLiveData.postValue(true) } override fun onLost(network: Network) { networkLiveData.postValue(false) } fun unRegister() { connectivityManager.unregisterNetworkCallback(this) } }
Activity中的使用代码
override fun onCreate() { super.onCreate(savedInstanceState) handleNetwork() } override fun onDestroy() { super.onDestroy() networkUtils.unRegister() } private fun handleNetwork() { networkUtils.getNetworkLiveData().observe(this) { isConnected: Boolean -> if (!isConnected) { ... } else { Log.d("Test", "Connected") ... } } }
Hilt单例提供代码
@Module @InstallIn(SingletonComponent::class) class AppUtilsModule { @Singleton @Provides fun provideNetworkUtils(context: Context): NetworkUtils { return NetworkUtils(context) } }
问题原因分析
核心问题:重复触发LiveData事件 + 重复初始化逻辑
LiveData的粘性特性触发第一次打印
NetworkUtils是单例,其内部的networkLiveData会保留最后一次的网络状态值(已连接true)。设备旋转后Activity重建,新的观察者注册时,LiveData会自动把保存的最新值推送给观察者,这是第一次打印"Connected"。重复调用初始化逻辑触发第二次打印
每次调用getNetworkLiveData()时,都会重新执行网络状态检测,并调用networkLiveData.postValue(isConnected)。旋转后Activity重建,onCreate()再次调用getNetworkLiveData(),此时检测到网络已连接,又会推送一次true值,导致第二次打印。
对比另一项目的差异
另一个未使用Navigation的项目中,NetworkUtils的getNetworkLiveData()应该做了逻辑优化——仅在首次调用时执行网络检测和postValue,后续调用直接返回已初始化的LiveData,不会重复触发值推送,因此旋转时只会触发LiveData的粘性事件一次,日志仅打印一次。
解决方案
1. 避免重复执行初始化逻辑
修改getNetworkLiveData(),只在首次调用时注册回调和初始化网络状态:
private var isInitialized = false fun getNetworkLiveData(): LiveData<Boolean> { if (!isInitialized) { // 仅首次注册回调 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { connectivityManager.registerDefaultNetworkCallback(this) } else { val builder = NetworkRequest.Builder() connectivityManager.registerNetworkCallback(builder.build(), this) } // 仅首次初始化网络状态 var isConnected = false connectivityManager.allNetworks.forEach { network -> val networkCapability = connectivityManager.getNetworkCapabilities(network) networkCapability?.let { if (it.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { isConnected = true return@forEach } } } networkLiveData.postValue(isConnected) isInitialized = true } return networkLiveData }
2. 过滤重复事件(可选)
如果需要保留当前初始化逻辑,可在观察者中过滤重复的状态值,避免重复打印:
private var lastNetworkState: Boolean? = null private fun handleNetwork() { networkUtils.getNetworkLiveData().observe(this) { isConnected: Boolean -> // 过滤与上次相同的状态 if (isConnected == lastNetworkState) return@observe lastNetworkState = isConnected if (!isConnected) { ... } else { Log.d("Test", "Connected") ... } } }
内容的提问来源于stack exchange,提问作者Ali

