MutableStateFlow在第一次发出Kotlin协程后不再发出值

时间:2020-06-11 19:18:28

标签: kotlin kotlin-coroutines kotlin-coroutines-flow kotlin-coroutine-channel

这是我的FirebaseOTPVerificationOperation类,其中定义了MutableStateFlow属性,并更改了值,

    @ExperimentalCoroutinesApi
class FirebaseOTPVerificationOperation @Inject constructor(
    private val activity: Activity,
    val logger: Logger
) {
    private val _phoneAuthComplete = MutableStateFlow<PhoneAuthCredential?>(null)
    val phoneAuthComplete: StateFlow<PhoneAuthCredential?>
        get() = _phoneAuthComplete

    private val _phoneVerificationFailed = MutableStateFlow<String>("")
    val phoneVerificationFailed: StateFlow<String>
        get() = _phoneVerificationFailed

    private val _phoneCodeSent = MutableStateFlow<Boolean?>(null)
    val phoneCodeSent: StateFlow<Boolean?>
        get() = _phoneCodeSent

    private val _phoneVerificationSuccess = MutableStateFlow<Boolean?>(null)
    val phoneVerificationSuccess: StateFlow<Boolean?>
        get() = _phoneVerificationSuccess

    fun resendPhoneVerificationCode(phoneNumber: String) {
        _phoneVerificationFailed.value = "ERROR_RESEND"
    }
}

这是我的视图模态,从那里我听状态流属性的变化,如下所示,

class OTPVerificationViewModal @AssistedInject constructor(
    private val coroutinesDispatcherProvider: AppCoroutineDispatchers,
    private val firebasePhoneVerificationListener: FirebaseOTPVerificationOperation,
    @Assisted private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    @AssistedInject.Factory
    interface Factory {
        fun create(savedStateHandle: SavedStateHandle): OTPVerificationViewModal
    }

    val phoneAuthComplete = viewModelScope.launch {
        firebasePhoneVerificationListener.phoneAuthComplete.filter {
            Log.e("1","filter auth $it")
            it.isNotNull()
        }.collect {
            Log.e("2","complete auth $it")
        }
    }

    val phoneVerificationFailed = viewModelScope.launch {
        firebasePhoneVerificationListener.phoneVerificationFailed.filter {
            Log.e("3","filter failed $it")
            it.isNotEmpty()
        }.collect {
            Log.e("4","collect failed $it")
        }
    }

    val phoneCodeSent = viewModelScope.launch {
        firebasePhoneVerificationListener.phoneCodeSent.filter {
            Log.e("5","filter code $it")
            it.isNotNull()
        }.collect {
            Log.e("6","collect code $it")
        }
    }

    val phoneVerificationSuccess = viewModelScope.launch {
        firebasePhoneVerificationListener.phoneVerificationSuccess.filter {
            Log.e("7","filter success $it")
            it.isNotNull()
        }.collect {
            Log.e("8","collect success $it")
        }
    }

    init {
        resendVerificationCode()
        secondCall()
    }

    private fun secondCall() {
        viewModelScope.launch(coroutinesDispatcherProvider.io) {
            delay(10000)
            resendVerificationCode()
        }
    }

    fun resendVerificationCode() {
        viewModelScope.launch(coroutinesDispatcherProvider.io) {
            firebasePhoneVerificationListener.resendPhoneVerificationCode(
                getNumber()
            )
        }
    }

    private fun getNumber() =
            "+9191111116055"
}

问题在于, firebasePhoneVerificationListener.phoneVerificationFailed在视图模式中被首次触发, init { resendVerificationCode() }

但是对于init { secondCall() }的第二次调用, firebasePhoneVerificationListener.phoneVerificationFailed并未在视图模式中触发,我不知道为什么会发生,任何原因或解释都将非常有用。

电流输出: filter auth null filter failed filter code null filter success null filter failed ERROR_RESEND collect failed ERROR_RESEND

预期输出: filter auth null filter failed filter code null filter success null filter failed ERROR_RESEND collect failed ERROR_RESEND filter failed ERROR_RESEND collect failed ERROR_RESEND

5 个答案:

答案 0 :(得分:2)

使用Channel来接收不同的值。

1)将此添加到您的ViewModel

 val _intent = Channel<Intent>(Channel.UNLIMITED)

2)使用报价放置价值

 _intent.offer(intentLocal)

3)观察流量

  _intent.consumeAsFlow().collect { //do something }

答案 1 :(得分:1)

Pankaj 的回答是正确的,StateFlow 不会两次发出相同的值。正如 documentation 所暗示的:

<块引用>

状态流中的值使用 Any.equals 比较以类似于 distinctUntilChanged 运算符的方式进行合并。它用于将传入更新与 value 中的 MutableStateFlow 合并,并在新值等于先前发出的值时抑制向收集器发出值。

因此,要解决此问题,您可以创建一个包装类并覆盖 equals(和 hashCode)方法以返回 false,即使这些类实际上是相同的:< /p>

sealed class VerificationError {
    object Resend: VerificationError()

    override fun equals(other: Any?): Boolean {
        return false
    }

    override fun hashCode(): Int {
        return javaClass.hashCode()
    }
}

答案 2 :(得分:0)

状态流发出的值将被合并,并且不会两次发出相同的连续结果,您可以认为条件检查正在验证旧发出的值不等于新发出的值。

电流输出: 过滤器验证null 筛选失败 过滤器代码为null 筛选成功null 筛选失败ERROR_RESEND 收集失败的错误ERROR_RESEND

(过滤器失败ERROR_RESEND 收集失败的错误ERROR_RESEND)。这是与发出的旧值相同的旧值,因此您不会看到它们被发出。

答案 3 :(得分:0)

合并流后我遇到了类似的问题。 如果使用 == 来判断相等性,则不会执行 emit() 函数。

解决方法:可以包裹一层,重写hashCode()和equals()方法。 equals() 方法直接返回 false。 此解决方案适用于我的代码。联合收割机后的流也发生了变化。

Pankaj 的回答是正确的,StateFlow 不会两次发出相同的值。

在换行之前,即使内容不同,==的结果仍然为真。

答案 4 :(得分:0)

我想我对这个问题有了更深入的了解。首先要确定的是,对于StateFlow,不建议使用变量集合类型(如MutableList等)。因为 MutableList 不是线程安全的。如果核心代码中有多个引用,可能会导致程序崩溃。

之前,我使用的方法是包装类并覆盖equals方法。但是,我认为这种解决方案并不是最安全的方法。最安全的方式是深拷贝,Kotlin 提供的 toMutableList() 和 toList() 方法都是深拷贝。发射方法判断是否有变化取决于equals()的结果是否相等。

我有这个问题的原因是使用emit()的数据类型是:SparseArray。 StateFlow 调用 SparseArray 的 equals 方法。当MutableList发生变化时,此时equals的结果不会发生变化(即使MutableList的equals和hashcode方法发生变化)。

最后,我将类型更改为 SparseArray。虽然增加和删除数据带来了性能损失,但这也从根本上解决了问题。