【问题标题】:Firebase Realtime Database and Kotlin CoroutinesFirebase 实时数据库和 Kotlin 协程
【发布时间】:2020-12-29 14:57:04
【问题描述】:

我是一名初学者,目前正在使用 Kotlin 开发一个从 TMDb 获取数据的 Android 应用程序 API 并使用 Firebase 实时数据库来存储和检索数据。

我使用 Kotlin 协程从 TMDb API 获取数据,但我不确定是否应该使用协程从 Firebase 实时数据库存储或检索数据。

我希望 Firebase 自动完成 Kotlin 协程完成的工作。

这是我想要执行的 Firebase 操作之一:(从数据库中检索对象)

firebaseDatabase = Firebase.database
        dbReference = firebaseDatabase.getReference("users/$uid")

val dbListener = object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {
                // Get Post object and use the values to update the UI
                val fobject = dataSnapshot.getValue<TvFirebase>()
                Log.v("utk", "tv show name is " + fobject!!.name)
                // ...
            }

            override fun onCancelled(databaseError: DatabaseError) {
                // Getting Post failed, log a message
                Log.w("utk", "onCancelled", databaseError.toException())
                // ...
            }
        }

val tvReference = firebaseDatabase.getReference("users/$uid/tv/236")
            tvReference.addValueEventListener(dbListener)

那么我需要在 Firebase 实时数据库中使用 Kotlin 协程吗?

【问题讨论】:

  • 请贴出有问题的代码
  • 我已经添加了代码。但我只想知道我是否需要使用协程来让 firebase 并发或异步工作。谢谢

标签: android kotlin firebase-realtime-database kotlin-coroutines


【解决方案1】:

不需要将协程与任何 Firebase API 一起使用,但如果您正确执行,这肯定可以让您的开发更轻松。但是,协程与报告随时间变化的值的 Firebase 侦听器不兼容。对于这些,您可能想要使用 LiveData 或 Flow。

【讨论】:

    【解决方案2】:

    只需对您的代码添加一个小改动即可。

    从此...

    firebaseDatabase = Firebase.database
            dbReference = firebaseDatabase.getReference("users/$uid")
    
    val dbListener = object : ValueEventListener {
                override fun onDataChange(dataSnapshot: DataSnapshot) {
                    // Get Post object and use the values to update the UI
                    val fobject = dataSnapshot.getValue<TvFirebase>()
                    Log.v("utk", "tv show name is " + fobject!!.name)
                    // ...
                }
    
                override fun onCancelled(databaseError: DatabaseError) {
                    // Getting Post failed, log a message
                    Log.w("utk", "onCancelled", databaseError.toException())
                    // ...
                }
            }
    
    val tvReference = firebaseDatabase.getReference("users/$uid/tv/236")
                tvReference.addValueEventListener(dbListener)
    

    到这里……

        val firebaseDatabase = FirebaseDatabase.getInstance().getReference("users/$uid")
        
        firebaseDatabse.addChildEventListener(object: ChildEventListener {
        
        override fun onChildAdded(snapshot: DataSnapshot, previousChildName: String?) {
        Log.d("MainFragment", "Children $snapshot")
        }
        override fun onChildChanged(snapshot: DataSnapshot, previousChildName: String?) {}
        override fun onChildRemoved(snapshot: DataSnapshot) {}
        override fun onChildMoved(snapshot: DataSnapshot, previousChildName: String?) {}
        override fun onCancelled(error: DatabaseError) {}
        })
    

    不要忘记检查它是否正确;)

    【讨论】:

      【解决方案3】:

      您可以使用callBackFlow API 删除所有 firebase 回调。 简单的例子:

      override suspend fun getSomething(): Flow<Long> = callbackFlow {
              val valueListener = object: ValueEventListener{
      
                 override fun onCancelled(error: DatabaseError) {
                      close()
                 }
      
                 override fun onDataChange(snapshot: DataSnapshot) {
                    offer(snapshot.childrenCount)
                  }
              }
              currentUserId?.let { userId ->
                  firebaseRef?.child(userId)?.addValueEventListener(valueListener)
                          ?: offer(0)        }
      
              awaitClose {
                  currentUserId?.let { userId ->
                      firebaseRef?.child(userId)?.removeEventListener(valueListener)
                  }
              }
          }
      

      【讨论】:

        【解决方案4】:

        我正在使用“实时数据库”和“Firestore”在我的旧项目中添加一些新功能。

        由于 firestore 在 Coroutines 上支持“等待”挂起功能,因此我在 Realtime 数据库中搜索了相同的功能。如果你不混合挂起和回调函数,协程会很棒..

        最终自己做。 我还没有尝试下面的代码,但它应该可以工作

        注意:自 1.2.0 以来,某些功能仍被标记为 ExperimentalCoroutinesApi,因此如果在未来版本中发生更改,请小心。

            sealed class RealtimeDatabaseValueResult {
                class Success(val dataSnapshot: DataSnapshot): RealtimeDatabaseValueResult()
                class Error(val error: DatabaseError): RealtimeDatabaseValueResult()
            }
        
            /**
             * Perform a addListenerForSingleValueEvent call on a databaseReference in a suspend function way
             * @param onCancellation action to perform if there is a cancellation
             */
            @ExperimentalCoroutinesApi
            suspend fun DatabaseReference.awaitSingleValue(onCancellation: ((cause: Throwable) -> Unit)? = null) = suspendCancellableCoroutine<RealtimeDatabaseValueResult> { continuation ->
        
                val valueEventListener = object: ValueEventListener{
                    override fun onCancelled(error: DatabaseError) {
                        continuation.resume(RealtimeDatabaseValueResult.Error(error = error), onCancellation)
                    }
        
                    override fun onDataChange(snapshot: DataSnapshot) {
                        continuation.resume(RealtimeDatabaseValueResult.Success(snapshot), onCancellation)
                    }
                }
        
                // add listener like you normally do
                addListenerForSingleValueEvent(valueEventListener)
        
                // in case the job, coroutine, etc. is cancelled, we remove the current event listener
                continuation.invokeOnCancellation { removeEventListener(valueEventListener) }
            }
        

        用法:

         fun fetchUser(firebaseDatabase: FirebaseDatabase, userId: String){
                CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate).launch{
                    when(val result = firebaseDatabase.getReference("users").child(userId).awaitSingleValue()){
                        is RealtimeDatabaseValueResult.Success -> {
                            val dataSnapshot: DataSnapshot = result.dataSnapshot
                            // proceed action with dataSnapshot
        
                        }
                        is RealtimeDatabaseValueResult.Error -> {
                            val error: DatabaseError = result.error
                            // proceed action with error
                        }
                    }
                }
            }
        

        【讨论】:

        • 当您知道您正在访问的确切节点(如 users/someUserId)时,这是可以的。但是如果我想获取路径用户中的所有用户怎么办?例如:使用带有 orderByChild 和 limitToFirst(20) 的查询。在这种情况下,它会返回 {key = users, value = null}
        猜你喜欢
        • 2019-12-25
        • 1970-01-01
        • 2019-08-07
        • 2020-09-19
        • 2019-04-29
        • 2021-12-04
        • 1970-01-01
        • 2021-12-30
        • 2020-09-18
        相关资源
        最近更新 更多