前言

StateFlow会对新的值和旧的值进行equality判断,如果返回true,就不会用这个new value去更新old value,就不会发送这个值。

这个equality判断会发生在 发送端 和 接收端。

发送端

一般有两种发送方式,如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
fun main() = runBlocking {
val mutableStateFlow = MutableStateFlow<Person>(Person())

launch {
delay(1000)
println("用update的方式更改值(equal返回true)")
mutableStateFlow.update {// 1 CAS校验 + 相等性判断,推荐使用
Person("", 0)
}
delay(1000)
println("用emit的方式更改值(equal返回true)")
mutableStateFlow.emit(Person("", 0))// 2 只有相等性判断
}

launch {
mutableStateFlow.collect{

println("collect: $it")
}

}
delay(99999)
}

update 系列有

  1. update
  2. getAndUpdate
  3. updateAndGet

三种,区别只是 返回值 不同。

emit 系列有

  1. emit
  2. tryEmit

都是继承自ShareFlow下来的。

两个系列都是线程安全的,但线程安全不意味着不需要进行一些同步措施,这也是为什么推荐update系列。当要更新的值,依赖于旧值的参数的时候,比如新值的某一个字段是旧值的两倍,用emit系列就会产生同步问题。

比如说当emit在两个coroutine去跑,其中一个coroutine1拿到旧值之后,开始基于这个值去产生新值,但同时另外那个coroutine3也在基于这个旧值去产生新值。这样coroutine1发送的那个新值和coroutine2发送的新值就一样了。这不是想要的效果,所以 update 系列额外的CAS判断很重要。

源码的话大致如下

1
2
3
4
5
6
7
8
private fun updateState(expectedState: Any?, newState: Any): Boolean {

synchronized(this) {
val oldState = _state.value
if (expectedState != null && oldState != expectedState) return false // 1 CAS support
if (oldState == newState) return true // 2 Don't do anything if value is not changing, but CAS -> true
_state.value = newState
}

update 系列会跑注释1和2,但emit 系列在运行到这个方法的时候 expectedState 总是为null,所以只会跑注释2

接收端

collect点进去也会有 equality 判断,不赘述。

不要这个特性

方法也很简单,使用 ShareFlow

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
fun main() = runBlocking {
val mutableStateFlow = MutableSharedFlow<Person>(
replay = 1,
extraBufferCapacity = 0,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)// buffer size = replay + extraBufferCapacity

launch {
delay(1000)
println("用emit的方式更改值(equal返回true)")
mutableStateFlow.tryEmit(Person("", 0))// 不应该用emit
delay(1000)
println("用emit的方式更改值(equal返回true)")
mutableStateFlow.tryEmit(Person("", 0))// 不应该用emit
}

launch {
mutableStateFlow.collect{

println("collect: $it")
}

}
delay(99999)
}

上面 ShareFlow 这样配置,效果 基本StateFlow 一样,要完全一样,还得

1
2
3
4
5
6
7
8
9
// MutableStateFlow(initialValue) is a shared flow with the following parameters:
val shared = MutableSharedFlow(
replay = 1,
extraBufferCapacity = 0,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
shared.tryEmit(initialValue) // emit the initial value
val state = shared.distinctUntilChanged() // get StateFlow-like behavior, 这一步就相当于加上了 equality判断功能

based on document