Kotlin Flow 基础概念

发布时间: 2023-06-05 13:30:07 作者: 大象笔记

本以为我的 Android 开发技术已经天下无敌了😅,没想到连 Flow / Channel 我都第一次见。。。不得不说 Android 的新概念层出不穷。

周末看了一堆文档,有了一个大概的了解。我发现微信公众号的文章质量还真是高,不少高手都做了详细的对比说明。 但是最终我一个也没有关注,从这些人的近期推文看都堕落了,不是贩卖焦炉,就是无脑卖其他骗子的课。扯远了,开始正题。

Flow 为何而生

一个挂起函数 (suspending function) 可以异步地返回一个值,但是没法多次返回。于是 Flow 就诞生了。 这也是为什么 Flow 获取 select 结果不需要加上 suspend 关键词,因为本身就不是挂起函数。

Flow 是 Kotlin 语言层的实现,所以解决了 LiveData 无法跨平台的问题。 同时 Flow 也解决了 LiveData 只能在 UI 主线程更新值的缺点。

cold / hot stream

Flow 是基于 Kotlin Coroutines 实现的 cold stream,即冷流。

注意:虽然 flow 是 code stream,但是新引入的 flow 子类, StateFlow 和 SharedFlow 是 hot stream。

flow 的几种构建方式

// 第一种
val namesFlow = flowOf("Jody", "Steve", "Lance", "Joe")

// 第二种
val namesFlow = listOf("Jody", "Steve", "Lance", "Joe").asFlow()

// 第三种
val namesFlow = flow {
  val names = listOf("Jody", "Steve", "Lance", "Joe")
  for (name in names) {
    delay(100)
    emit(name)
  }
}

emit 是向消费端发送数据;flow 后面的大括号代表一个 lambda block。

flow operator

fun main() = runBlocking {
  namesFlow
      .map { name -> name.length }
      .filter { length -> length < 5 }
      .collect { println(it) }

  println()
}

上面代码中,有两种 flow operator:

Room 对 flow 的支持

Room 2.2 版本开始支持 Flow。

即 select 操作可以被监听,当 insert 或者 remove 数据到数据库时,可以被监听到。

例如:

@Query("SELECT * FROM forecasts_table")
fun getForecasts(): Flow<List<DbForecast>>

这里用到了前面提到的一种将 list 转换为 flow 的做法。

Flow 与 LiveData

Android 中有三种常见的 observer pattern:LiveData, Flow, RxJava:

LiveData 相对于 Flow 的优势:内置了生命周期的管理,所以非常适合 View 与 ViewModel 间的通信。

为何如此看重生命周期管理? 假设我们的程序已经不在前台了,flow 流还在持续更新,则UI更新依然在持续进行当中。这是非常危险的事情,因为在非前台的情况下更新UI,某些场景下是会导致程序崩溃的。

但是我感觉 90% 的场景都适用于 LiveData,即基本都是用在界面状态同步上。偶尔才会有类似手机传感器数据这种复杂的处理场景。

Flow 转换为 LiveData

.asLiveData()

例如:

//1
val forecasts: LiveData<List<ForecastViewState>> = weatherRepository
    //2
    .getForecasts()
    //3
    .map {
      homeViewStateMapper.mapForecastsToViewState(it)
    }
    //4
    .asLiveData()

flow 适合做搜索的地方

private val _locations = queryChannel
    //1
    .asFlow()
    //2
    .debounce(SEARCH_DELAY_MILLIS)
    //3
    .mapLatest {
      if (it.length >= MIN_QUERY_LENGTH) {
        getLocations(it)
      } else {
        emptyList()
      }
    }
    //4
    .catch {
      // Log Error
    }

StateFlow 替代 Channel

BroadcastChannel 未来会在 Kotlin 1.6.0 中弃用,在 Kotlin 1.7.0 中删除。它的替代者是 StateFlow 和 SharedFlow。

StateFlow 和 SharedFlow 是 hot stream。

例如,实时搜索词进行查询的场景: View 通过 channel 将查询词传递给 viewmodel 层,viewmodel 中将 channel 转换为 flow, 再 map 进行查询。这里的 channel 就可以用 flow 替代。

参考

我是一名山东烟台的开发者,联系作者