问题描述
我在Android应用中使用以下代码将数据从远程源加载到本地数据库缓存中.
I'm using the following code in my Android app to load data from a remote source into a local database cache.
inline fun <ResultType, RequestType> networkBoundResource(
crossinline query: () -> Flow<ResultType>,
crossinline fetch: suspend () -> RequestType,
crossinline saveFetchResult: suspend (RequestType) -> Unit,
crossinline onFetchSuccess: () -> Unit = { },
crossinline onFetchFailed: (Throwable) -> Unit = { },
crossinline shouldFetch: (ResultType) -> Boolean = { true }
) = flow {
val data = query().first()
val flow = if (shouldFetch(data)) {
emit(Resource.(data))
try {
// this could take a while, I want to keep getting updates meanwhile
saveFetchResult(fetch())
onFetchSuccess()
query().map { Resource.Success(it) }
} catch (t: Throwable) {
onFetchFailed(t)
query().map { Resource.Error(t, it) }
}
} else {
query().map { Resource.Success(it) }
}
emitAll(flow)
}
query
是一个数据库查询,不断通过emitAll
发出数据库更新,直到我们再次调用此方法为止.
The query
is a database query that keeps emitting database updates through emitAll
until we call this method again.
此设置的问题是Resource.Loading
仅包含一个快照".的当前数据(first()
),直到我们到达try/catch
块的末尾并调用emitAll
之前,我们将不会收到任何数据库更新.但是我想在Loading
仍在进行时继续接收数据库更新.但是,我不能只在Resource.Loading
上调用emitAll
,因为这会阻塞整个流程.
The problem with this setup is that Resource.Loading
only contains a "snapshot" of the current data (first()
) and we won't receive any database updates until we get to the end of the try/catch
block and call emitAll
. But I would like to keep receiving database updates while Loading
is still in progress. However, I can't just call emitAll
on Resource.Loading
because it would block the whole Flow.
是否可以在try
块完成后调用Loading
上的emitAll
,然后切换到Success
/Error
?
Is there a way to call emitAll
on Loading
and then switch to Success
/Error
once the try
block has finished?
推荐答案
我仅对此进行了简单的测试以验证它,但是看起来您可以侦听查询并发出它在根据外部Flow的上下文新发布的协程-功能中的其他工作将继续进行,不受阻碍.完成其他工作后,可以取消侦听查询的协程.例如:
I've only done simple testing on this to validate it, but it looks like you can listen to the query and emit any/all data it propagates in a newly launched coroutine based on the outer Flow's context -- the other work in the function will continue, unblocked. Once that other work is done, the coroutine that's listening to the query can be cancelled. For example:
inline fun <ResultType, RequestType> networkBoundResource(
crossinline query: () -> Flow<ResultType>,
crossinline fetch: suspend () -> RequestType,
crossinline saveFetchResult: suspend (RequestType) -> Unit,
crossinline onFetchFailed: (Throwable) -> Unit = { },
crossinline shouldFetch: (ResultType) -> Boolean = { true }
): Flow<Resource<ResultType>> = flow {
emit(Resource.Loading())
val data = query().first()
val flow = if (shouldFetch(data)) {
val flowContext = currentCoroutineContext()
val loading: Job = coroutineScope {
launch(flowContext) {
query().map { Resource.Loading(it) }
.collect { withContext(flowContext) { emit(it) } }
}
}
try {
val request = fetch()
loading.cancel()
saveFetchResult(request)
query().map { Resource.Success(it) }
} catch (throwable: Throwable) {
loading.cancel()
onFetchFailed(throwable)
query().map { Resource.Error(throwable, it) }
}
} else {
query().map { Resource.Success(it) }
}
emitAll(flow)
}
让我知道是否可行!
这篇关于直到完成另一项操作之前,emitAll?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!