MutableStateFlow第一次回调成功后,后面就不会再响应了

准确说是相同的两个值,只会响应一次。

最近在练习flow

遇到了这个问题 如下代码

viewmodel中的部分代码
 val loginResult = MutableStateFlow(LoginResult())
  val loginR: StateFlow = loginResult
    
 viewModelScope.launch {
            
   loginRepository.login(username = username.value, password = password.value)
                .collect {
                    if (it is Result.Success) {
                        val loginResult1 =
                            LoginResult(success = LoggedInUserView(displayName = it.data.user_nicename))
                        loginResult.value = loginResult1
                    } else {
                        val error = it as Result.Error
                        loginResult.value = LoginResult(error = message.msg)
                    }
                }
        }
    
activity中的部分代码
    lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                loginViewModel.loginR.collect {
                    if (it.error != null) {
                        Toast.makeText(this@LoginActivity, it.error, Toast.LENGTH_SHORT).show()
                    }
                }
            }
        }

出现的问题是:当我登录失败,第一次会有提示,第二次失败就没有提示了。

追踪看下源码(MutableStateFlow-->StateFlowImpl)

 private fun updateState(expectedState: Any?, newState: Any): Boolean {
        var curSequence = 0
        var curSlots: Array? = this.slots // benign race, we will not use it
        synchronized(this) {
            val oldState = _state.value
            if (expectedState != null && oldState != expectedState) return false // CAS support
            if (oldState == newState) return true // Don't do anything if value is not changing, but CAS -> true
            _state.value = newState
            curSequence = sequence
            if (curSequence and 1 == 0) { // even sequence means quiescent state flow (no ongoing update)
                curSequence++ // make it odd
                sequence = curSequence
            } else {
                // update is already in process, notify it, and return
                sequence = curSequence + 2 // change sequence to notify, keep it odd
                return true // updated
            }
            curSlots = slots // read current reference to collectors under lock
        }
        /*
           Fire value updates outside of the lock to avoid deadlocks with unconfined coroutines.
           Loop until we're done firing all the changes. This is a sort of simple flat combining that
           ensures sequential firing of concurrent updates and avoids the storm of collector resumes
           when updates happen concurrently from many threads.
         */
        while (true) {
            // Benign race on element read from array
            curSlots?.forEach {
                it?.makePending()
            }
            // check if the value was updated again while we were updating the old one
            synchronized(this) {
                if (sequence == curSequence) { // nothing changed, we are done
                    sequence = curSequence + 1 // make sequence even again
                    return true // done, updated
                }
                // reread everything for the next loop under the lock
                curSequence = sequence
                curSlots = slots
            }
        }
    }

第7行显示当新老数据一致的时候就不做任何事儿。

那说到这里,要想每次都有回调,那么就让每次数据不一样就行了。

重写回调对象的requals() 和 hashCode 就ok了。

如下

data class LoginResult(
    val success: LoggedInUserView? = null,
    val error: String? = null,
) {

    override fun equals(other: Any?): Boolean = false

    override fun hashCode(): Int {
        return Random.nextInt()
    }
}

这样就可以了。每次都会有回调

====当然还有中办法是通过Channel替换MutableStateFlow====

viewModel改写如下
 val channel = Channel(Channel.CONFLATED)
   viewModelScope.launch {
          
            loginRepository.login(username = username.value, password = password.value)
                .collect {
                    if (it is Result.Success) {
                        val loginResult1 =
                            LoginResult(success = LoggedInUserView(displayName = it.data.user_nicename))
                        channel.send(loginResult1)
                    } else {
                            channel.send(LoginResult(error = message.msg+tiems))
                    }
                }


        }

activity改下如下
       lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.CREATED) {
                    loginViewModel.channel.consumeAsFlow().collect {
                        if (it.error != null) {
                            Toast.makeText(this@LoginActivity, it.error, Toast.LENGTH_SHORT).show()
                        }
                    }
                }

            }
        }

坑还多,慢慢填。。。

你可能感兴趣的:(MutableStateFlow第一次回调成功后,后面就不会再响应了)