我不知道如何使用Room和MVVM模式执行“简单”操作。 我正在使用Retrofit获取一些数据。 “适当”的响应会触发活动中的观察者,并且使用Room库将响应本身的一小部分插入数据库中,擦除所有先前存储的值并插入新的值。否则,旧值将保留在DB上。 紧接着,我想检查数据库中的一个字段,但是我无法强制此操作等到上一个操作完成。
模型
@Entity(tableName = "licence")
data class Licence(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "licence_id")
var licenceId: Int = 0,
@Ignore
var config: List<LicenceConfig>? = null,
.......
//all the others attributes )
@Entity(foreignKeys = [
ForeignKey(
entity = Licence::class,
parentColumns = ["licence_id"],
childColumns = ["licence_reference"],
onDelete = ForeignKey.CASCADE
)],tableName = "licence_configurations")
data class LicenceConfig(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "licence_config_id")
var licenceConfigId: Int,
@ColumnInfo(name="licence_reference")
var licenceReference: Int,
活动中的观察者
loginViewModel.apiResponse.observe(this, Observer { response ->
response?.let {
loginViewModel.insertLicences(response.licence)
}
//here I need to wait for the insertion to end
loginViewModel.methodToCheckForTheFieldOnDatabase()
})
ViewModel
fun insertLicences(licences: List<Licence>) = viewModelScope.launch {
roomRepository.deleteAllLicences()
licences.forEach { licence ->
roomRepository.insertLicence(licence).also { insertedLicenceId ->
licence.config?.forEach { licenceConfiguration ->
roomRepository.insertLicenceConfiguration(
licenceConfiguration.apply { licenceReference = insertedLicenceId.toInt() }
)
}
}
}
}
房间存储库
class RoomRepository(private val roomDao: RoomDao) {
val allLicences: LiveData<List<Licence>> = roomDao.getAllLicences()
suspend fun insertLicence(licence: Licence): Long {
return roomDao.insertLicence(licence)
}
suspend fun insertLicenceConfiguration(licenceConfiguration: LicenceConfig){
return roomDao.insertLicenceConfiguration(LicenceConfig)
}
}
RoomDao
@Dao
interface RoomDao {
@Query("select * from licence")
fun getAllLicences(): LiveData<List<Licence>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertLicence(licence: Licence): Long
@Insert
suspend fun insertLicenceConfiguration(licence: LicenceConfig)
@Query("DELETE FROM licence")
suspend fun deleteAllLicences()
}
将观察者设置为“ allLicences” LiveData或直接在DB上的该字段上不是一种选择,因为该操作将在活动创建后立即执行,而我必须等到API响应才能执行它们。
在另一个没有Room的项目中,我在使用协程时使用了async {}和.await()来执行顺序操作,但是我不能真正使其在这里工作。当我在插入方法之后暂停调试器时,“ allLicences”的值始终为空,但是在恢复并导出数据库后,数据已正确插入。我还尝试在ViewModel方法之后添加.invokeOnCompletion {},但结果相同。
基本上,我想等待此方法结束以执行其他操作。
有什么建议吗?
编辑
我完全忘了报告模型!每个许可证都有一个配置列表。当我执行许可证插入时,我将使用自动生成的ID,将其应用于licenceConfig,然后为每个licenceConfig对象(ViewModel方法的嵌套forEach循环中的代码)执行插入。问题似乎在于执行此嵌套循环会破坏操作的“同步性”
答案 0 :(得分:1)
要等到插入完成后,您需要将协程创建从insertLicences()
移到观察者,并使insertLicences()
成为暂停函数。
loginViewModel.apiResponse.observe(this, Observer { response ->
lifecycleScope.launch {
response?.let {
loginViewModel.insertLicences(response.licence)
}
//here I need to wait for the insertion to end
loginViewModel.methodToCheckForTheFieldOnDatabase()
}
})
和
suspend fun insertLicences(licences: List<Licence>) {
roomRepository.deleteAllLicences()
licences.forEach { licence ->
roomRepository.insertLicence(licence).also { insertedLicenceId ->
licence.config?.forEach { licenceConfiguration ->
roomRepository.insertLicenceConfiguration(
licenceConfiguration.apply { licenceReference = insertedLicenceId.toInt() }
)
}
}
}
}
替代解决方案
您可以将观察器中存在的所有代码移入ViewModel。
loginViewModel.apiResponse.observe(this, Observer { response ->
loginViewModel.refreshLicenses(response)
})
和在ViewModel
中fun refreshLicenses(response:Response?){
viewModelScope.launch{
response?.let {
insertLicences(response.licence)
}
methodToCheckForTheFieldOnDatabase()
}
}
,并将insertLicences用作暂停函数
suspend fun insertLicences(licences: List<Licence>) {
roomRepository.deleteAllLicences()
licences.forEach { licence ->
roomRepository.insertLicence(licence).also { insertedLicenceId ->
licence.config?.forEach { licenceConfiguration ->
roomRepository.insertLicenceConfiguration(
licenceConfiguration.apply { licenceReference = insertedLicenceId.toInt() }
)
}
}
}
}
答案 1 :(得分:0)
编辑:在我回复之前没有阅读您的结论,但是,我仍然认为您的答案在于协程
使用回调或Promise,插入查询完成后是否会执行您的函数?
回调
对于回调,其想法是将一个函数作为参数传递给 另一个功能,并在流程执行完之后调用此功能 完成。
fun postItem(item: Item) {
preparePostAsync { token ->
submitPostAsync(token, item) { post ->
processPost(post)
}
}
}
fun preparePostAsync(callback: (Token) -> Unit) {
// make request and return immediately
// arrange callback to be invoked later
}
我希望诺言诚实
承诺
期货或承诺背后的想法(这些还有其他术语 可以根据语言/平台来引用),就是当我们 打个电话,我们保证在某个时候它将返回 称为Promise的对象,然后可以对其进行操作。
fun postItem(item: Item) {
preparePostAsync()
.thenCompose { token ->
submitPostAsync(token, item)
}
.thenAccept { post ->
processPost(post)
}
}
fun preparePostAsync(): Promise<Token> {
// makes request an returns a promise that is completed later
return promise
}
做好您的工作,当诺言完成后,继续进行数据验证。
您可以详细了解协程here