You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

设备旋转时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事件 + 重复初始化逻辑

  1. LiveData的粘性特性触发第一次打印
    NetworkUtils是单例,其内部的networkLiveData会保留最后一次的网络状态值(已连接true)。设备旋转后Activity重建,新的观察者注册时,LiveData会自动把保存的最新值推送给观察者,这是第一次打印"Connected"。

  2. 重复调用初始化逻辑触发第二次打印
    每次调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 05:15:35