准确说是相同的两个值,只会响应一次。
最近在练习flow
遇到了这个问题 如下代码
viewmodel中的部分代码
val loginResult = MutableStateFlow(LoginResult())
val loginR: StateFlow<LoginResult> = 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<StateFlowSlot?>? = 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<LoginResult>(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()
}
}
}
}
}
坑还多,慢慢填。。。
网友评论