美文网首页
MutableStateFlow第一次回调成功后,后面就不会再响

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

作者: 清汤白面 | 来源:发表于2021-09-02 16:57 被阅读0次

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

最近在练习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()
                        }
                    }
                }

            }
        }

坑还多,慢慢填。。。

相关文章

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

    准确说是相同的两个值,只会响应一次。 最近在练习flow 遇到了这个问题 如下代码 viewmodel中的部分代码...

  • Android AutoDispose网络请求生命周期管理踩坑

    需求:微信支付成功后回调刷新界面步骤:微信成功回调后,通过接口回调,调用刷新界面的网络请求再finish掉微信回调...

  • 7月7日盘前提示:重个股轻指数,指数第一次回调是买入点。

    大盘:消息面,《新闻联播》再提A股,会加速场外资金流入。走势看,昨天继续放量逼空长阳,第一次回调临近,不过第一次回...

  • 汇聚,杉德,微信原生支付,支付宝成功/失败回调记录

    目录 1、微信第一次成功,后面都是回调失败 2、微信原生支付回调 3、汇聚,杉德小程序支付 4、支付宝支付 微信第...

  • 碎碎念

    今天又忙着做戚风蛋糕了,这次还比较成功,就是表面有点开裂,不平整,很正常。如果能再耐心点,中途调一下温度,就不会。...

  • 斜坡示范

    下图为一分钟周期。三轨向下斜坡下行,在第一次回轨后做多被止损,第二次回轨仍被止损,后面绝不能再开多仓。第三次开多仓...

  • 苹果第三方登录问题

    1. 苹果第一次登录会弹出姓名密码,后面就不会再弹出了,解决办法 在设置->Apple id(姓名) -> 密码与...

  • Linux 命令及技巧

    Linux 命令拼接 顺序执行 前面执行成功后,后面不执行 前面执行成功后,后面才执行 Linux find 命令...

  • 惭愧

    早上醒来,就不想上班。 把闹钟向后调了20分钟,接着睡。 闹铃响,烦!向后调5分钟,再睡! 再响,向后调3分钟,闭...

  • callback 的作用

    1.callback 的作用: 当段落隐藏后再弹出框,只有前面成功了才会调用回调函数image.png 先弹出框,...

网友评论

      本文标题:MutableStateFlow第一次回调成功后,后面就不会再响

      本文链接:https://www.haomeiwen.com/subject/jvlcwltx.html