Room在后台线程上自动执行返回LiveData
的查询。但我想返回一个未封装到LiveData
中的值(因为我不想要实时更新)。如何使用协程实现这一点?
如何从该函数返回Task
对象?
fun getTask(id: Int): Task {
viewModelScope.launch {
repository.getTask(id)
}
}
此函数位于ViewModel中。它将调用转发到DAO:
@Query("SELECT * FROM task_table WHERE id = :id")
fun getTask(id: Int): Task
如果不从Room返回LiveData,则不会从DB获得更新。但是,您可以从viewModel返回LiveData。
val data = liveData {
emit(repository.getTask(id))
}
liveData
扩展函数在协同程序中运行,然后您可以使用DAO的挂起版本来正确处理后台。
@Query("SELECT * FROM task_table WHERE id = :id")
suspend fun getTask(id: Int): Task?
如果您在查询中没有使用聚合函数,那么您需要做的一件大事就是确保它可以为null。
如果你真的想调用viewModel中的方法来返回任务,你应该从你的活动/片段(不推荐)运行启动
ViewModel
suspend fun getTask(id: Int): Task {
repository.getTask(id)
}
Activity/Fragment
lifecycleScope.launch {
val task = viewModel.getTask(id)
// Do What you want with the task
}
Flow是新的LiveData
我之前在两个项目中遇到过类似的问题,每个项目的解决方法都不同。但最近我学会了使用Flow
,这似乎是迄今为止最干净的方法。
LiveData的替代方案
如果你不需要使用LiveData,你有两个选择:
- 通过@query检索
Cursor
,适用于重构旧项目 - 使用
Flow
,适用于新项目
仅检索一个对象/值
- LiveDate:您可以取消订阅LiveData,在第一次获取后删除观察者。在我看来不干净
- 流:如果需要,可以只检索单个对象/值,然后停止流收集
刀:
getTask():此方法返回一个Flow<Task>
:
@Query("SELECT * FROM task_table WHERE id = :id")
fun getTask(id: Int): Flow<Task>
视图模型:
getTask():返回一个Task
对象(不是Flow<Task>
),也是suspend
函数。
first()返回由流,然后取消流的收集。抛出NoSuchElementException如果流是空的。
suspend fun getTask(id: Int): Task {
return dao.getTask(id).first()
}
片段/活动:
属性:
private var viewModelJob = Job()
private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
当不需要片段/活动时,不要忘记取消viewModelJob,也就是onClear/onDestory/。。。所以所有与此密切相关的协同活动都被取消了
用法
现在,每当我们想从那个挂起的函数中检索对象Task
时,我们都需要在一个挂起或协程中。因此,使用launch
构建器来创建协程在这里是合适的(因为我们不希望从该构建器中返回任何返回对象,所以我们只想运行一个挂起函数,否则async将返回一个延迟函数)。
onCreate() / onCreateView()
.
..
...
uiScope.launch() {
// Here are the Task & Main-UI
val task = viewModel.getTask(1)
binding.taskTitleTextView.text = task.title
}
如果我们不使用first()
,那么我们需要收集流viewModel.getTasks().collect{ it }
在kotlin.coroutines.flow中有很多有用的函数。Flow is
是Coroutine包中发生的最好的事情,哦,很抱歉我通过了存储库层,在大多数情况下,它只是viewModel的重复。
Room中的Suspend函数是主要的安全函数,在自定义的调度员。与您在问题中提到的LiveData相同。以下是实现相同的示例
viewmModel类中的某些函数内部
viewModelScope.launch {
// suspend and resume make this database request main-safe
// so our ViewModel doesn't need to worry about threading
someLiveData.value =
repository.getSomething()
}
存储库类
suspend fun getSomething(): List<Something> {
return dao.getSomething()
}
In-Dao类
@Query("select * from tableName")
suspend fun getSomething(): List<Something>
解决方法之一是立即返回Deferred对象,然后在返回Deferred 时调用.await()
fun getTaskAsync(id: Int): Deferred<Task> = viewModelScope.async {
repository.getTask(id)
}
//call-site
getTaskAsync(id).await() // <- this is suspension point