【问题标题】:Passing MutableLiveData between functions在函数之间传递 MutableLiveData
【发布时间】:2020-09-26 23:21:41
【问题描述】:

我在 ViewModel 中有一个函数“A”,它从 firebase 检索数据,并将值分配给 MutableLiveData<Int>(所有这些都包含在 onSuccessListener 中)并返回它。此函数是从同一 ViewModel 中的另一个函数“B”调用的。但是当我尝试从'A'返回MutableLiveData<Int>时,它返回为0(默认值)。但是,如果我在 onSuccessListener 之外为 MutableLiveData<Int> 赋值,则返回该值。

代码:

val num = MutableLiveData<Int>().default(0)

private fun A():Int {
   FirebaseOperation
     .addOnSuccessListener{  //it:DocumentSnapshot!
         num.value = it.num
     }
   return num.value.toInt() // outside onsuccesslistener, default value 0 is being returned
}

private fun B() {
   val num2 = A()
}

更新: 在了解了firebase回调地狱之后,我已经切换到了用于firebase的kotlin协程(实现org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.1.1作为依赖) 更新代码:

private suspend fun A():DocumentSnapshot? {
     return Firebase.firestore.collection("collection").documet("document").get().await()
    }

private suspend fun B(): Int{
    val data = A()
    val user = data.toObject<User>()
    val num = user.num
    return num
}

但主线程冻结,应用程序因Reason: Input dispatching timed out (Waiting to send key event because the focused window has not finished processing all of the input events that were previously delivered to it. Outbound queue length: 0. Wait queue length: 9.) 而崩溃

【问题讨论】:

    标签: android firebase kotlin google-cloud-firestore kotlin-coroutines


    【解决方案1】:

    从 firebase 检索数据是异步进行的,换句话说,addOnSuccessListener 回调仅在从 firebase 检索数据时被调用。因此,只有在调用回调时才会更新 livedata。 当您从B() 调用A() 时,它会返回0,因为它不等待更新livedata(调用回调),而是返回默认值。

    当您简单地更新 addOnSuccessListener 之外的 livedata 值时,它会同步更新,因此您会得到更新后的结果

    【讨论】:

    • 那么有没有办法等到数据被获取?如果我删除 onsuccesslistener 那么它会崩溃说任务尚未完成。
    • @Anirudh Ganesh,你使用协程吗?
    • @AnirudhGanesh 而不是返回 Int,您应该返回 livedata 本身并通过调用 observe() 来观察它,并且每当 livedata 的值将被更新时,您都会在 Observer 上获得回调。
    【解决方案2】:

    LiveData的主要用途是异步工作后更新和观察。在函数A() 中我看到你只添加了SuccessListener 但没有执行任何操作,所以SuccessListener 没有被调用。在函数B() 中,您只需检索对MutableLiveData 的引用,但不要观察它,因此它始终包含默认值。通常LiveData对象在Activity或Fragment中观察到:

    num.observe(lifecycleOwner, androidx.lifecycle.Observer {
        // here you retrieve changes of your "num" variable as parameter "it"
    })
    

    其中lifecycleOwner - 通常是活动或片段。

    【讨论】:

    • 我确实执行了firebase操作并返回了一个数据快照,它被分配给了num
    【解决方案3】:

    如果你使用kotlinx.coroutines,你可以使用suspendCoroutine。

    private suspend fun A(): Int = suspendCoroutine { cont ->
        FirebaseOperation.addOnSuccessListener{  // it: DocumentSnapshot!
            cont.resume(it.num)
        }
    }
    
    private suspend fun B() {
       val num2 = A()
    }
    

    【讨论】:

    • 所以我切换到协程,因为我使用 Firestore,所以我使用 org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.1.1 依赖项,我使用 Firebase.firestore.collection("collection").documet("document").get().await() 但主要线程只是冻结和崩溃
    • @AnirudhGanesh logcat 或设备上是否报告了任何崩溃日志?
    • @AnirudhGanesh 如果 UI 线程 (Dispatchers.Main) 用于长时间运行的任务,可能会发生,请尝试调用 B() 的协程将上下文的调度程序覆盖为 Default 或 IO,例如:scope.launch(Dispatchers.IO) { /* B() */ }
    【解决方案4】:

    一种在不使用协程且不通过生命周期观察 livedata 的情况下获取函数 B 中的值的方法(顺便说一句,您不能这样做,因为您在 viewmodel 中没有生命周期所有者,您应该避免将任何内容传递给它),是使用MediatorLiveData。

    中介实时数据的主要目的是它的价值依赖于其他实时数据,它可以在没有生命周期的情况下观察其他实时数据。

    所以你可以在函数 B 中创建一个MediatorLiveData,然后从函数 A 返回 livedata,然后在函数 B 中使用 MediatorLiveData 观察它。这样,当 A 中的值更新时,你将在 B 中得到通知。

    您可以在此处阅读有关 MediatorLiveData 的更多信息:https://developer.android.com/reference/android/arch/lifecycle/MediatorLiveData

    【讨论】:

      【解决方案5】:

      你可以有流量,也可以直接喂给你的虚拟机。

      class YourViewModel:ViewModel(){
      
      val num = MutableLiveData<Int>().default(0)
      
         private suspend fun A():DocumentSnapshot? {
           return Firebase.firestore.collection("collection").documet("document").get().await()
         }
      
         fun onSomeButtonClick(){
             viewModelScope.launch(Main){
                val num =  A()?.toObject<User>().num ?: return
                num.value = num
             }
         } 
      
      }
      

      或flow 带有asLiveData 扩展名。

      val num:LiveData<Int> = flow{
         val num =  A()?.toObject<User>()?.num ?: 0
         emit(num)
      }.asLiveData()
      

      在你的UI,你可以观察得到id。

      【讨论】:

        【解决方案6】:

        我以某种方式设法合并了这两个函数,并且我现在已经嵌套了所有数据库回调。我不认为这对代码可读性有好处。与此同时,我将尝试其他替代方案。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2018-04-27
          • 1970-01-01
          • 2020-07-02
          • 1970-01-01
          • 2014-06-23
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多