如何从任何地方(存储库等)获取 Flow 重要吗?

Does it matter how to get Flow from anywhere(repository, etc.)?

我的流量列表是这样的:

val list = repository.someFlowList()

有时我会这样做:

fun list() = repository.someFlowList()

在 Google Codelab 中,它是这样使用的:

val list: Flow<List<Something>>
    get() = repository.someFlowList()

我知道什么是属性、getter、setter 和函数。但我只想知道一件事:在效率、性能等方面有什么区别吗?如果重要的话,我将该流用作 activity.

中的实时数据(仅使用 asLiveData() 方法)

在 Kotlin 中有一个 Backing Fields 的概念,这些字段仅在需要时用作 属性 的一部分以在内存中保存其值。

使用 getter 函数 get() = repository.someFlowList() 每次访问 属性 时都会评估主体,因为没有为其分配支持字段。而在 val list = repository.someFlowList() 的情况下,值在初始化期间进行评估并保存在支持字段中。 Getters & Setters 上的 Kotlin 文档也对此进行了解释。

TL;DR:您应该更喜欢现场支持的 属性 而不是计算的 属性,但结果是一样的。如果是 热流 ,在这种情况下,您应该使用字段支持 属性 以避免意外行为。


Flow API 有点声明性,这意味着当您创建 Flow 时,您只是在定义它的作用,这就是为什么它被称为 cold flow.收集流量时,您定义的计算仅 运行s。这意味着您可以根据需要在同一个实例上多次调用 collect,它定义的计算每次都会从头开始 运行。每次作为计算 属性 或函数结果创建新 Flow 实例的唯一潜在影响是,您分配了相同 Flow 定义的更多实例。

冷流

检查这个简单的例子:

val flow = flow {
    emit(0)
    emit(1)
}
runBlocking {
    val f1 = flow
    f1.collect {
        println("Flow ID: ${f1.hashCode()} - emits $it")
    }
    val f2 = flow
    f2.collect {
        println("Flow ID: ${f2.hashCode()} - emits $it")
    }
}

这将打印:

Flow ID: 608188624 - emits 0
Flow ID: 608188624 - emits 1
Flow ID: 608188624 - emits 0
Flow ID: 608188624 - emits 1

你看到同一个 Flow 实例在收集时将 运行 Flow 发射,每次收集它。

如果您使用 getter (val flow get() = flow {...}) 更改分配,则输出为:

Flow ID: 608188624 - emits 0
Flow ID: 608188624 - emits 1
Flow ID: 511833308 - emits 0
Flow ID: 511833308 - emits 1

看到结果是一样的,不同的是现在你有2个Flow实例。

热流

当 Flow 是 hot 时,也就是说,它甚至在收集器开始收集之前就有值,那么情况就不同了。一个StateFlow就是一个典型的hot Flow。看看这个:

val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())

val flow = MutableStateFlow(0)

val job1 = scope.launch {
    val f = flow
    f.value = 1 // <-- note this
    f.collect {
        println("A: $it")
    }
}
val job2 = scope.launch {
    val f = flow
    f.collect {
        println("B: $it")
    }
}
runBlocking {
    job1.cancelAndJoin()
    job2.cancelAndJoin()
}

输出为:

A: 1
B: 1

两个 Flow 集合都收到单个热 Flow 实例的最新值。

如果您更改为 val flow get() = MutableStateFlow(0),您将获得:

A: 1
B: 0

这次我们创建了一个不同的 StateFlow 实例,因此第二个收集器错过了我们之前在第一个 Flow 实例上所做的值更改。如果 属性 公开为 public,这是一个问题,因为 属性 的实现,无论是字段支持的还是计算的,都不应该与调用者相关。最终这可能会导致意外行为 - 错误。