6

我正在尝试使用 Kotlin Flows 和 Firebase 为我的视图提供实时更新。

这就是我从以下位置收集实时数据的方式ViewModel

class MainViewModel(repo: IRepo): ViewModel() {

    val fetchVersionCode = liveData(Dispatchers.IO) {
        emit(Resource.Loading())

        try {
            repo.getVersionCode().collect {
                emit(it)
            }

        } catch (e: Exception){
            emit(Resource.Failure(e))
            Log.e("ERROR:", e.message)
        }
    }
}

这就是每当 Firebase 中的值发生变化时,我如何从我的存储库中发出每个数据流:

class RepoImpl: IRepo {

    override suspend fun getVersionCodeRepo(): Flow<Resource<Int>> = flow {

        FirebaseFirestore.getInstance()
            .collection("params").document("app").addSnapshotListener { documentSnapshot, firebaseFirestoreException ->
                val versionCode = documentSnapshot!!.getLong("version")
                emit(Resource.Success(versionCode!!.toInt()))
            }
    }

问题是当我使用时:

 emit(Resource.Success(versionCode!!.toInt()))

Android Studio 使用以下命令突出显示发出调用:

挂起函数 'emit' 只能从协程或另一个挂起函数中调用

但我从CoroutineScope我的ViewModel.

这里有什么问题?

谢谢

4

2 回答 2

9

Firestore 快照侦听器实际上是一个异步回调,它在另一个线程上运行,与 Kotlin 管理的协程线程无关。这就是为什么你不能emit()在异步回调中调用 - 回调根本不在协程上下文中,所以它不能像协程一样挂起。

您正在尝试做的事情要求您使用任何您认为合适的方法(例如)将您的调用发出回协程上下文launch,或者可能启动一个回调流,让您提供来自其他线程的对象。

于 2020-02-24T20:38:38.407 回答
6

suspend关键字 ongetVersionCodeRepo()不适用, emit(Resource.Success(versionCode!!.toInt()))因为它是从 lambda 中调用的。由于您无法更改,因此addSnapshotListener您需要使用协程构建器launch来调用suspend函数。

当将 lambda 传递给函数时,其相应函数参数的声明决定了它是否可以在没有协程构建器的情况下调用挂起函数。例如,这是一个采用无参数函数参数的函数:

fun f(g: () -> Unit)

如果这个函数是这样调用的:

f {
    // do something
}

花括号内的所有内容都被执行,就好像它在一个声明为的函数中一样:

fun g() {
    // do something
}

由于g没有使用suspend关键字声明,因此它不能在suspend不使用协程构建器的情况下调用函数。

但是,如果f()这样声明:

fun f(g: suspend () -> Unit)

并被称为:

f {
    // do something
}

花括号内的所有内容都被执行,就好像它在一个声明为的函数中一样:

suspend fun g() {
    // do something
}

由于g suspend用关键字声明的,它可以suspend在不使用协程构建器的情况下调用函数。

在调用 lambda 的情况下,addEventListener就像在声明为的函数中调用它一样:

public abstract void onEvent (T value, FirebaseFirestoreException error)

由于此函数声明没有suspend关键字(它不能,它是用 Java 编写的),因此传递给它的任何 lambda 都必须使用协程构建器来调用使用suspend关键字声明的函数。

于 2020-02-24T21:02:25.577 回答