如何使用Kotlin协程执行Firebase Transaction操作?
解决方案
第一步:自定义runTransaction协程扩展
kotlinx-coroutines-play-services库仅为Firebase返回Task的API提供了await()扩展,而runTransaction走回调接口逻辑,没有返回Task,需要自行封装协程适配:
import com.google.firebase.database.DataSnapshot import com.google.firebase.database.DatabaseError import com.google.firebase.database.DatabaseReference import com.google.firebase.database.MutableData import com.google.firebase.database.Transaction import kotlinx.coroutines.suspendCancellableCoroutine import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException suspend fun DatabaseReference.awaitRunTransaction( fireLocalEvents: Boolean = false, doTransaction: (MutableData) -> Transaction.Result ): DataSnapshot? = suspendCancellableCoroutine { cont -> runTransaction(object : Transaction.Handler { override fun doTransaction(mutableData: MutableData): Transaction.Result { return doTransaction(mutableData) } override fun onComplete(error: DatabaseError?, committed: Boolean, snapshot: DataSnapshot?) { when { error != null -> cont.resumeWithException(error.toException()) else -> cont.resume(snapshot) } } }, fireLocalEvents) // 协程取消时终止事务的可选处理逻辑 cont.invokeOnCancellation { this@awaitRunTransaction.onDisconnect() } }
第二步:改造业务代码
原有代码在suspend函数内手动创建CoroutineScope启动协程,会脱离当前协程作用域,导致取消、异常无法向上传递,直接删除多余的launch调用即可,改造后完整代码如下:
override suspend fun modifyProductStock( product: ProductModel, valueToModify: Long, replace: Boolean ) { val restaurantId = authRepository.restaurantId.value ?: throw Exception("No restaurant!") val productId = product.id ?: throw Exception("No Product ID!") val reference = FirebaseDatabase.getInstance().getReference("database/$restaurantId").child("products") val stockRef = reference.child(productId).child("stock") if (replace) { stockRef.setValue(valueToModify).await() } else { stockRef.awaitRunTransaction { mutableData -> // 此处写入你的事务操作逻辑,示例为叠加库存: val currentStock = mutableData.getValue(Long::class.java) ?: 0L mutableData.value = currentStock + valueToModify Transaction.success(mutableData) } } }
说明
- 扩展函数的
fireLocalEvents参数对应原生runTransaction的同名参数,默认设为false可避免本地临时变更触发不必要的监听回调,可根据业务需求调整 - 事务执行过程中如果出现冲突、网络错误等异常,会直接抛出到协程中,你可以在调用
modifyProductStock的位置用try-catch捕获处理异常
内容的提问来源于stack exchange,提问作者George O
相关产品推荐
相关产品推荐

