android - Kotlin Flow:带有回调对象延迟初始化器的 callbackFlow
问题描述
Kotlin Flow
我想在我的 Android 项目中使用反应范式。我有一个基于外部回调的 API,所以我的选择是callbackFlow
在我的Repository
课堂上使用。
我已经在没有帮助的情况下深入地阅读了一些适当的文档:
- 回调流文档
- Roman Elizarov 的回调和 Kotlin 流程
我想要达到的目标:
目前我的Repository
班级看起来像这样(简化代码):
lateinit var callback: ApiCallback
fun someFlow() = callbackFlow<SomeModel> {
callback = object : ApiCallback {
override fun someApiMethod() {
offer(SomeModel())
}
}
awaitClose { Log.d("Suspending flow until methods aren't invoked") }
}
suspend fun someUnfortunateCallbackDependentCall() {
externalApiClient.externalMethod(callback)
}
当someUnfortunateCallbackDependentCall
被调用比收集更快时会出现问题someFlow()
。现在为了避免UninitializedPropertyAccessException
我在调用之前在协程中添加了一些延迟,someUnfortunateCallbackDependentCall
但这对我来说是一种 hack/code 的味道。
我的第一个想法是使用by lazy
而不是lateinit var
因为这是我想要的 - 回调对象的延迟初始化。但是,我无法完全编写代码。我想从中发出/提供/发送一些数据someApiMethod
以形成数据流,但超出范围则callbackFlow
需要ProducerScope
在其中。另一方面,someUnfortunateCallbackDependentCall
它根本不是基于 Kotlin Flow 的(最多可以使用Coroutines
API 暂停)。
有可能吗?也许使用其他一些 Kotlin 代表?任何帮助,将不胜感激。
解决方案
要从技术上回答您的问题,您当然可以懒惰地或使用 lateinit 初始化回调,但您不能这样做并同时共享协程范围(一个用于 Flow,一个用于挂起函数) - 您需要自己建立某种同步。
下面我对你想要达到的目标做了一些假设,也许它们对你来说并不完美,但希望能给你一些启发如何改进。
由于它是您正在创建的存储库,我将首先假设您正在寻找存储SomeModel
并允许应用程序的其余部分观察对其的更改。如果是这样,最简单的方法是使用MutableStateFlow
属性而不是callbackFlow
:
interface Repository {
val state: Flow<SomeModel>
suspend fun reload()
}
class RepositoryImpl(private val service: ApiService) : Repository {
override val state = MutableStateFlow(SomeModel())
override suspend fun reload() {
return suspendCoroutine { continuation ->
service.callBackend(object : ApiCallback {
override fun someApiMethod(data: SomeModel) {
state.value = data
if (continuation.context.isActive)
continuation.resume(Unit)
}
})
}
}
}
interface ApiCallback {
fun someApiMethod(data: SomeModel)
}
data class SomeModel(val data: String = "")
interface ApiService {
fun callBackend(callback: ApiCallback)
}
此解决方案的缺点是您必须调用reload()
才能真正调用后端,仅收集 Flow 是不够的。
myrepository.state.collect {}
myrepository.reload()
另一个解决方案,同样取决于您想要实现的目标,是提供两种调用后端的方法:
interface Repository {
fun someFlow(): Flow<SomeModel>
suspend fun reload(): SomeModel
}
class RepositoryImpl(private val service: ApiService) : Repository {
override fun someFlow() = callbackFlow<SomeModel> {
service.callBackend(object : ApiCallback {
override fun someApiMethod(data: SomeModel) {
offer(data)
}
})
awaitClose {
Log.d("TAG", "Callback Flow is closed")
}
}
override suspend fun reload(): SomeModel {
return suspendCoroutine<SomeModel> { continuation ->
service.callBackend(object : ApiCallback {
override fun someApiMethod(data: SomeModel) {
if (continuation.context.isActive)
continuation.resume(data)
}
})
}
}
}
interface ApiCallback {
fun someApiMethod(data: SomeModel)
}
data class SomeModel(val data: String = "")
interface ApiService {
fun callBackend(callback: ApiCallback)
}
现在您可以调用reload()
或 someFlow()
检索SomeModel()
并且Repository
保持没有“状态”。
请注意,该reload()
函数只是该callbackFlow
想法的“协程”版本。
推荐阅读
- javascript - JavaScript:未捕获的类型错误:无法在 prac.js:13 处读取未定义的属性“长度”
- html - 切换时引导导航栏切换器不显示链接?
- python - 使用带有连接词的电子邮件时恢复密码和登录
- amazon-web-services - 无服务器中的 Cloudformation 内在函数
- php - 如何从 DOMDocument 或 file_get_content 中获取自定义 html 标签的价值
- wpf - 您如何将数据绑定到 Smith HTML 编辑器
- python-3.x - 没有聚合的熊猫数据透视表
- javascript - DRY - 如何制作 2 个将不同表带入 1 个 GET 函数的 GET 函数
- ssrs-2012 - 具有行和列组问题的 SSRS 范围
- r - 计算预期收益 - 方差图