Kotlin Flow进阶:从“冷热”之辨到架构选型

466 阅读4分钟

一句话总结:

冷流是“一次性的数据蓝图”,为每个订阅者独立执行;热流是“共享的数据总线”,在特定生命周期内持续存在。而stateInshareIn操作符,则是将数据蓝图“激活”成共享总线的关键“转换器”。


一、冷流 (Cold Flow) - “一次性”的数据请求蓝图

冷流的本质不是数据源,而是一个生产数据的计划书(Blueprint)

  • 核心特性:只有collect()被调用时,计划书才会被从头到尾完整执行一遍
  • 最佳实践:非常适合定义在**数据层(Repository)**中,用于封装一次性的数据操作,如网络请求或数据库查询。
class NewsRepository(private val api: NewsApi) {
    // 定义一个获取新闻的“计划书”,它本身不执行任何操作
    fun fetchLatestNews(): Flow<List<News>> = flow {
        // 当有订阅者collect时,这行代码才会被执行
        val latestNews = api.getNews()
        emit(latestNews)
    }
}

二、从“冷”到“热”的转变:共享的艺术 (stateIn & shareIn)

在ViewModel中,我们通常不希望每次UI重绘(如屏幕旋转)都重新执行一遍fetchLatestNews()。我们需要将这个“冷”的计划书,转换成一个“热”的、可共享的、能在ViewModel生命周期内持续存在的数据源。

这就是stateInshareIn的用武之地。

class NewsViewModel(private val repository: NewsRepository) : ViewModel() {
    
    // 使用stateIn将冷流转换为一个热的StateFlow
    val uiState: StateFlow<NewsUiState> = repository.fetchLatestNews()
        .map { news -> NewsUiState.Success(news) } // 转换成UI状态
        .catch { e -> emit(NewsUiState.Error(e)) } // 处理异常
        .stateIn(
            scope = viewModelScope, // ① 指定热流的生命周期范围
            started = SharingStarted.WhileSubscribed(5000), // ② 指定共享策略
            initialValue = NewsUiState.Loading // ③ 提供一个初始值
        )
}

关键参数解读

  1. scope: 热流的“生命线”,通常是viewModelScope,确保ViewModel销毁时,上游的数据流也会被取消。
  2. started: 共享的启动和停止策略。SharingStarted.WhileSubscribed(5000)是Google推荐的最佳实践,意味着:当最后一个订阅者消失后,等待5秒,如果还没有新的订阅者,就停止上游流以节省资源。
  3. initialValue / replay: 热流的“记忆”。stateIn需要一个初始值,而shareIn可以配置replay数量,让新订阅者收到最近的N条历史数据。

三、热流家族的“双子星”:StateFlowSharedFlow

通过stateInshareIn转换后,我们就得到了两种定位不同的热流。

StateFlow - 状态的“唯一真实来源”

  • 比喻:会议室里的一块白板。它永远展示着当前的最新内容。

  • 特性

    • 永远有值:创建时必须有初始值。
    • 值是唯一的:不会发射与前一个值相同的数据。
    • 值会被合并(Conflation) :如果生产者更新状态过快,消费者只会收到最新的值。
  • 用途UI状态管理。它是Android平台上LiveData的现代替代品。

SharedFlow - 事件的“广播总线”

  • 比喻:一个扩音喇叭。它喊出的话(事件),只有当时在场的人才能听到。

  • 特性

    • 可以没有初始值
    • 可以配置重播(replay) :让新来的听众也能听到之前的几句话。
    • 可以配置缓冲策略:当喊话太快听众处理不过来时,是丢掉旧的、新的,还是挂起等待。
  • 用途:**一次性事件(Single-shot events)**的分发,如显示一个Toast、导航、弹出一个Dialog。

四、总结与架构选型

特性冷流 (flow)热流 (StateFlow)热流 (SharedFlow)
本质数据生产蓝图共享的状态容器共享的事件总线
生命周期依赖于collect依赖于指定的scope依赖于指定的scope
典型位置Repository (数据层)ViewModel (UI逻辑层)ViewModel (UI逻辑层)
转换方式N/AstateInshareIn
核心场景定义一次性数据操作向UI暴露可观察的状态向UI分发一次性事件

结论:

在现代Android开发中,我们通常遵循一个标准模式:在数据层使用冷流(Flow)作为数据源的“蓝图”,然后在ViewModel中使用stateIn或shareIn,将其转换为与UI生命周期绑定的、可共享的、健壮的热流(StateFlow或SharedFlow),最终由UI层安全地进行订阅和消费。 理解这个从冷到热的转换流程,是掌握Kotlin Flow在架构中应用的关键。