Android ---【Kotlin篇】Kotlin 协程中 StateFlow 与 SharedFlow 的网络状态对比与应用

在 Kotlin 协程中,StateFlow 和 SharedFlow 确实是最常用的两种可共享的热流(Hot Flow),它们都是 SharedFlow 的子类,你截图里的新旧代码对比正好就是它们的典型用法。下面为你系统梳理二者的区别、适用场景及核心注意事项,结尾附上可直接复用的网络状态监听工具类示例。


1. 基础概念:冷流 vs 热流

  • 冷流(Cold Flow):比如最基础的 flow { ... },它是"懒加载"的,只有当有 Collector(收集者)订阅时才会开始发射数据,每个 Collector 都会触发一次独立的数据生产,订阅者之间互不影响。

  • 热流(Hot Flow):SharedFlow 和 StateFlow 都属于热流,其数据生产与订阅行为解耦,不管有没有 Collector 订阅,都会持续生产数据(或维护状态),所有订阅者共享同一套数据,确保数据一致性。


2. SharedFlow:通用的多播热流

  • 核心特点:

    • 可配置缓存策略:通过 replay 参数设置缓存历史数据条数,新订阅者会优先接收缓存数据;extraBufferCapacity 用于设置额外缓冲区大小,应对突发发射场景。

    • 无初始值约束:仅在调用 emit() 或 tryEmit() 时才会发射数据,未发射数据前无默认值可用。

    • 多播特性:支持多个订阅者同时接收数据,数据发射一次可被所有订阅者捕获。

  • 适用场景:

    • 事件通知类场景:如按钮点击事件、网络请求结果回调、消息推送、日志上报等。

    • 无需维护当前状态:仅需广播一次性事件,无需订阅者获取"最新快照"的场景。

代码示例:

kotlin 复制代码
private val _networkState = MutableSharedFlow<Boolean>(extraBufferCapacity = 1)
val networkChangedFlow = _networkState.asSharedFlow()      

通过 extraBufferCapacity = 1 配置额外缓冲区大小为1,可避免发射数据时因无订阅者导致阻塞,同时让新订阅者能接收最近1条缓存数据,确保状态不丢失。


3. StateFlow:持有单一状态的 SharedFlow

  • 核心特点:

    • 强状态绑定:是 SharedFlow 的特殊实现,专门用于持有和维护单一"当前状态值",本质等价于 SharedFlow(replay = 1, extraBufferCapacity = 0) 并自动过滤重复值。

    • 必须指定初始值:创建 MutableStateFlow 时需传入初始值,确保任何时候订阅者都能获取到状态(即使未更新过)。

    • 自动去重与即时同步:更新状态时,仅当新值与旧值不相等才会发射数据,避免无效刷新;新订阅者订阅后会立即收到当前最新状态值。

  • 适用场景:

    • 状态管理类场景:如网络连接状态、用户登录状态、UI 控件状态(开关、进度)、页面数据缓存等。

    • 需实时获取最新状态:订阅者不仅要监听状态变化,还需随时获取当前快照的场景。

  • 代码示例:

kotlin 复制代码
private val _networkState = MutableStateFlow<Boolean?>(null)
val networkChangedFlow = _networkState.asStateFlow()

这里用 null 作为初始值,代表网络状态初始未知;后续通过 _networkState.value = true/false 更新状态(连接/断开),订阅者会实时收到变化通知并获取最新状态。


4. 核心对比与使用注意事项

4.1 核心维度对比

4.2 关键使用注意事项

  • 线程安全问题:MutableStateFlow 和 MutableSharedFlow 的 emit() 方法需在协程中调用,tryEmit() 可在非协程环境使用(返回布尔值表示发射结果),二者均支持多线程并发更新,无需额外加锁。

  • 订阅生命周期管理:在 Android 等平台使用时,需绑定页面/组件生命周期(如 lifecycleScope.launchWhenStarted),避免内存泄漏;订阅后若无需监听,可调用 Job.cancel() 手动取消。

  • 状态暴露规范:对外暴露不可变流(asStateFlow()/asSharedFlow()),仅在内部保留可变流(MutableXXXFlow)的修改权限,确保状态可控,避免外部随意篡改。

  • SharedFlow 缓存配置:避免过度设置 replay 大小,否则会占用额外内存;若仅需新订阅者接收最新一条数据,设置 replay = 1 即可,无需额外缓冲区。


5. 实战示例:StateFlow 实现网络状态监听工具类

结合上文内容,以下是基于 StateFlow 实现的网络状态监听工具类,适配 Android 平台,支持监听网络连接/断开状态,可直接集成到项目中使用:

kotlin 复制代码
import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.os.Build
import androidx.lifecycle.LiveData
import androidx.lifecycle.asLiveData
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch

/**
 * 网络状态监听工具类(基于 StateFlow 实现)
 * 支持监听网络连接状态,对外暴露不可变流,确保状态安全
 */
class NetworkMonitor private constructor(private val context: Context) {
    // 可变状态流(内部修改),初始值为 null(未知状态)
    private val _networkState = MutableStateFlow<Boolean?>(null)
    // 不可变状态流(对外暴露),供外部订阅
    val networkState: StateFlow<Boolean?> = _networkState.asStateFlow()
    // 可选:转为 LiveData 适配传统生命周期组件
    val networkLiveData: LiveData<Boolean?> = networkState.asLiveData()

    private val connectivityManager by lazy {
        context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    }

    // 网络回调,监听网络状态变化
    private val networkCallback = object : ConnectivityManager.NetworkCallback() {
        override fun onAvailable(network: Network) {
            super.onAvailable(network)
            // 网络可用,更新状态为 true
            _networkState.value = true
        }

        override fun onLost(network: Network) {
            super.onLost(network)
            // 网络断开,检查是否还有其他可用网络
            _networkState.value = isNetworkAvailable()
        }

        override fun onUnavailable() {
            super.onUnavailable()
            // 网络不可用,更新状态为 false
            _networkState.value = false
        }
    }

    /**
     * 初始化监听
     */
    fun startMonitoring() {
        // 初始获取一次网络状态
        _networkState.value = isNetworkAvailable()
        // 注册网络监听回调
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            connectivityManager.registerDefaultNetworkCallback(networkCallback)
        } else {
            // 适配低版本系统
            val request = android.net.NetworkRequest.Builder()
                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                .build()
            connectivityManager.registerNetworkCallback(request, networkCallback)
        }
    }

    /**
     * 停止监听,避免内存泄漏
     */
    fun stopMonitoring() {
        connectivityManager.unregisterNetworkCallback(networkCallback)
    }

    /**
     * 主动检查当前网络是否可用
     */
    private fun isNetworkAvailable(): Boolean {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            val network = connectivityManager.activeNetwork ?: return false
            val capabilities = connectivityManager.getNetworkCapabilities(network) ?: return false
            return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                    && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
        } else {
            // 低版本兼容
            val networkInfo = connectivityManager.activeNetworkInfo ?: return false
            return networkInfo.isConnected && networkInfo.isAvailable
        }
    }

    // 单例模式,确保全局唯一实例
    companion object {
        @Volatile
        private var instance: NetworkMonitor? = null

        fun getInstance(context: Context): NetworkMonitor {
            return instance ?: synchronized(this) {
                instance ?: NetworkMonitor(context.applicationContext).also { instance = it }
            }
        }
    }
}
    

示例使用方式

在 Android Activity/Fragment 中订阅网络状态,绑定生命周期避免内存泄漏:

kotlin 复制代码
// 在 Activity 中初始化
class MainActivity : AppCompatActivity() {
    private lateinit var networkMonitor: NetworkMonitor

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 初始化网络监听工具
        networkMonitor = NetworkMonitor.getInstance(applicationContext)
        // 启动监听
        networkMonitor.startMonitoring()

        // 订阅网络状态(使用 lifecycleScope 绑定生命周期)
        lifecycleScope.launchWhenStarted {
            networkMonitor.networkState.collect { isConnected ->
                when (isConnected) {
                    true -> {
                        // 网络已连接,更新 UI 或执行相关逻辑
                        tvNetworkStatus.text = "网络已连接"
                    }
                    false -> {
                        // 网络已断开,提示用户或执行离线逻辑
                        tvNetworkStatus.text = "网络已断开"
                    }
                    null -> {
                        // 初始未知状态,可显示加载中
                        tvNetworkStatus.text = "正在检查网络状态..."
                    }
                }
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        // 停止监听,释放资源
        networkMonitor.stopMonitoring()
    }
}
    

该示例优势:基于 StateFlow 确保状态实时同步和自动去重,避免 UI 无效刷新;适配高低版本 Android 系统,提供单例模式和生命周期管理,兼顾安全性与实用性。

相关推荐
资料库012 小时前
Bond的模式都有哪些?
服务器·网络
Fate_I_C2 小时前
Android Jetpack实战
android·android jetpack
以太浮标2 小时前
华为eNSP模拟器综合实验之- ARP代理
网络·智能路由器
等风来不如迎风去2 小时前
【android】oppo手机拷贝视频文件
android·windows·智能手机
LuminescenceJ2 小时前
RPC通信中的Context上下文如何跨进程传递消息,gRPC为例分析
开发语言·网络·后端·网络协议·rpc·golang
噔噔君2 小时前
ip link show输出详解
网络·网络协议·tcp/ip
坐怀不乱杯魂2 小时前
Linux网络 - Socket编程(IPv4&IPv6)
linux·服务器·网络·c++·udp·tcp
悠哉清闲2 小时前
android studio中怎么引用SVG
android·android studio
网硕互联的小客服2 小时前
站群服务器里的8C/4C/2C/1C有什么区别?选择哪个比较好?
运维·服务器·网络