为什么我不能像 rxJava.Single.create 这样在 Kotlin Flow 中使用 emit 函数?

Why I can't use an emit function in Kotlin Flow like rxJava.Single.create?

我正在尝试将带有 rxjava 链的交互器重写为 kotlin 流。在 LocationHandlerImpl 中,我使用 LocationService 来获取我的当前位置。在 addOnSuccessListener 和 addOnFailureListener 中,我正在发出我的模型但有错误:

“挂起函数只能在协程体内调用”。我做错了吗?但是我可以在听众之外调用 emit(看下面的流生成器​​)

如 Prokash (here) 所述,流设计为自包含的。您的位置服务侦听器不在 Flow 的范围内。

不过,您可以查看 callbackFlow,它为您提供了使用基于回调 API 构建流程所需的机制。

Callback Flow Documentation,请注意回调流程仍处于实验阶段。

您似乎正试图从 Android 定位服务获取最后一个位置。这是 Google Play 服务中许多 Task 返回调用之一。 Kotlin 已经有一个模块,kotlinx-coroutines-play-services,贡献了一个函数

suspend fun <T> Task<T>.await(): T?

在你的项目中,你可以简单地这样写:

suspend fun getMyLocation(): Location? =
        LocationServices.getFusedLocationProvider(context)
                .lastLocation
                .await()

如果您想将其与其他基于 Flow 的代码集成,请添加此包装函数:

fun <T> Task<T>.asFlow() = flow { emit(await()) }

现在你可以写了

fun getLocationAsFlow(): Flow<Location?> =
        LocationServices.getFusedLocationProvider(context)
                .lastLocation
                .asFlow()

如果出于教育目的,您希望了解如何在没有附加模块的情况下直接实现它,那么最直接的方法如下:

fun getLocationAsFlow() = flow {
    val location = suspendCancellableCoroutine<Location?> { cont ->
        LocationServices.getFusedLocationProvider(context)
                .lastLocation
                .addOnCompleteListener {
                    val e = exception
                    when {
                        e != null -> cont.resumeWithException(e)
                        isCanceled -> cont.cancel()
                        else -> cont.resume(result)
                    }
                }
    }
    emit(location)
}

这是将 Task.await() 的简化实现内联到其使用站点的结果。