在调查 Android 中的 Kotlin Flow 期间,我试图理解并(也许)将 LiveData 替换为 Flow。但我不明白如何在 StateFlow 和 SharedFlow 中收集和发送数据。下面是一个简单的例子和我的意思:
示例数据类
data class SimpleDto(val value: Int)
示例视图模型
class SimpleViewModel : ViewModel() {
private val _uiDtoState = MutableStateFlow(SimpleDto(1))
val uiDtoState: StateFlow<SimpleDto> = _uiDtoState.asStateFlow()
fun testCollectData() {
viewModelScope.launch {
val tempList = listOf(
SimpleDto(1),
SimpleDto(2),
SimpleDto(3),
SimpleDto(4),
SimpleDto(5)
)
for (dto in tempList) {
_uiDtoState.value = dto
}
}
}
}
并在Activity中收集数据
class MainActivity : AppCompatActivity() {
private val viewModel: SimpleViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//...
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
viewModel.uiDtoState.drop(1).collect { result ->
Log.d("MyTag", "uiDtoState collected = ${result?.value}")
}
}
}
}
}
所以,当我调用 viewModel 时。 testCollectData() 它只收集最后一个对象 SimpleDto(5)。
问题1:我假设StateFlow只存储一个数据(状态)。最好在需要时使用,就像标志数据(例如布尔值)一样。
如果使用SharedFlow可以解决这个问题,但是哪个配置呢?例如:
val uiDtoState: MutableSharedFlow<SimpleDto> = MutableSharedFlow(
replay = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST,
)
它也只收集最后一个对象。很清楚,因为replay=1。
问题2:如果我不知道如何计算存储的数据,我们应该使用SharedFlow的什么配置?
是的,StateFlow 和 LiveData 之间的一个区别是 StateFlow 不保证收集所有中间值。 “状态”的假设是您只关心当前值,而不关心任何历史记录。在这种情况下,由于您不会在为 StateFlow 设置新值的调用之间放弃主线程,因此主调度程序上的收集器甚至永远没有机会看到中间值。
另一方面,当您在主线程上更新其值时,LiveData 会立即同步触发其观察者。
如果您不知道 SharedFlow 需要多少历史记录,则应将
replay
设置为 Int.MAX_VALUE
。但缺点是它会让历史永远回溯。新的收集器最终将收集整个值的历史记录,即使有些值已过时,因此这与 LiveData 的行为不同,LiveData 不会为新观察者重播值。
允许主调度程序上 StateFlow 的当前收集器获取所有值的一种可能的方法是在设置值的循环内调用
yield()
。但我不确定这是否是一个可靠的解决方案。它依赖于这样的假设:主调度程序将协程片段提交给主 Looper 来执行,因此调用 yield()
总是会在收集器都有机会收集先前的值之后进行下一次循环迭代。它还假设您的收集函数不进行任何挂起函数调用。