準(zhǔn)確說是相同的兩個值牵囤,只會響應(yīng)一次爸黄。
最近在練習(xí)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()
}
}
}
}
出現(xiàn)的問題是:當(dāng)我登錄失敗,第一次會有提示揭鳞,第二次失敗就沒有提示了炕贵。
追蹤看下源碼(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行顯示當(dāng)新老數(shù)據(jù)一致的時候就不做任何事兒。
那說到這里汹桦,要想每次都有回調(diào)鲁驶,那么就讓每次數(shù)據(jù)不一樣就行了鉴裹。
重寫回調(diào)對象的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()
}
}
這樣就可以了。每次都會有回調(diào)
====當(dāng)然還有中辦法是通過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()
}
}
}
}
}
坑還多径荔,慢慢填督禽。。总处。