通过网络请求回填时从 Room 发出协程流
Emit coroutine Flow from Room while backfilling via network request
我的架构是这样的:
Dao 方法返回 Flow<T>
:
@Query("SELECT * FROM table WHERE id = :id")
fun itemById(id: Int): Flow<Item>
存储库层从数据库返回项目,但也从网络回填:
(* 此处需要帮助 -- 这未按预期工作 **)
fun items(): Flow<Item> = flow {
// Immediately emit values from DB
emitAll(itemDao.itemById(1))
// Backfill DB via network request without blocking coroutine
itemApi.makeRequest()
.also { insert(it) }
}
ViewModel 层处理流程,应用任何转换,并使用 .asLiveData() 将其转换为 LiveData:
fun observeItem(): LiveData<Item> = itemRepository.getItemFlow()
.map { // apply transformation to view model }
.asLiveData()
片段观察 LiveData 排放和更新 UI:
viewModel.item().observeNotNull(viewLifecycleOwner) {
renderUI(it)
}
我遇到的问题是在第 2 步。我似乎无法找到一种构建逻辑的方法,以便我可以立即从 Flow 发出项目,而且无需等待即可执行网络获取。
由于从网络逻辑中获取数据在同一个挂起函数中,它将等待网络请求完成,然后再向下游发出结果。但我只想独立触发该请求,因为我对等待结果不感兴趣(当它返回时,它会更新 Room,我会自然地得到结果)。
有什么想法吗?
编辑
Marko 的解决方案对我来说效果很好,但我确实尝试了类似的方法:
suspend fun items(): Flow<List<Cryptocurrency>> = coroutineScope {
launch {
itemApi.makeRequest().also { insert(it) }
}
itemDao.itemById(1)
}
听起来您在描述要启动的后台任务。为此,您需要访问协程范围,因此 items()
应该是 CoroutineScope
:
上的扩展函数
fun CoroutineScope.items(): Flow<Item> {
launch {
itemApi.makeRequest().also { insert(it) }
}
return flow {
emitAll(itemDao.itemById(1))
}
}
另一方面,如果您想启动一个远程获取,其结果也将成为响应的一部分,您可以按如下方式进行:
fun items(): Flow<Item> = flow {
coroutineScope {
val lateItem = async { itemApi.makeRequest().also { insert(it) } }
emitAll(itemDao.itemById(1))
emit(lateItem.await())
}
}
我的架构是这样的:
Dao 方法返回
Flow<T>
:@Query("SELECT * FROM table WHERE id = :id") fun itemById(id: Int): Flow<Item>
存储库层从数据库返回项目,但也从网络回填:
(* 此处需要帮助 -- 这未按预期工作 **)
fun items(): Flow<Item> = flow { // Immediately emit values from DB emitAll(itemDao.itemById(1)) // Backfill DB via network request without blocking coroutine itemApi.makeRequest() .also { insert(it) } }
ViewModel 层处理流程,应用任何转换,并使用 .asLiveData() 将其转换为 LiveData:
fun observeItem(): LiveData<Item> = itemRepository.getItemFlow() .map { // apply transformation to view model } .asLiveData()
片段观察 LiveData 排放和更新 UI:
viewModel.item().observeNotNull(viewLifecycleOwner) { renderUI(it) }
我遇到的问题是在第 2 步。我似乎无法找到一种构建逻辑的方法,以便我可以立即从 Flow 发出项目,而且无需等待即可执行网络获取。
由于从网络逻辑中获取数据在同一个挂起函数中,它将等待网络请求完成,然后再向下游发出结果。但我只想独立触发该请求,因为我对等待结果不感兴趣(当它返回时,它会更新 Room,我会自然地得到结果)。
有什么想法吗?
编辑
Marko 的解决方案对我来说效果很好,但我确实尝试了类似的方法:
suspend fun items(): Flow<List<Cryptocurrency>> = coroutineScope {
launch {
itemApi.makeRequest().also { insert(it) }
}
itemDao.itemById(1)
}
听起来您在描述要启动的后台任务。为此,您需要访问协程范围,因此 items()
应该是 CoroutineScope
:
fun CoroutineScope.items(): Flow<Item> {
launch {
itemApi.makeRequest().also { insert(it) }
}
return flow {
emitAll(itemDao.itemById(1))
}
}
另一方面,如果您想启动一个远程获取,其结果也将成为响应的一部分,您可以按如下方式进行:
fun items(): Flow<Item> = flow {
coroutineScope {
val lateItem = async { itemApi.makeRequest().also { insert(it) } }
emitAll(itemDao.itemById(1))
emit(lateItem.await())
}
}