如何在使用协程的Room数据库中获取插入查询的ID

3

我希望在使用协程向Roomdb插入数据成功时获取回调。


MyDao.kt

@Dao
interface MyDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(obj: Task): Long
}


TaskViewModel.kt

class TaskViewModel(var context:Context) : ViewModel{
    private var appDao: AppDao

    init {
        val db = AppDatabase.getInstance(context)
        appDao = db.appDao()
    }

    fun insertTask(tast: Task) {
        GlobalScope.launch {
          val mID = appDao.insert(task)
        }
    }
}

如何从 insertTask() 方法中返回 mID
谢谢您的提前预约。
1个回答

6

使用 withContext 包装调用,并将函数标记为挂起

suspend insertTask(task: Task) = withContext(Dispatchers.IO) { appDao.insertTask(task) }

请问您的看法是:

fun saveTask(t: Task) = lifecycleScope.launch {
    val id = viewModel.insertTask(t)
    Toast.makeText(context, "Task $id has been inserted", Toast.LENGTH_SHORT).show()
}

您可以返回一个 LiveData,在完成时触发回调:
fun insertTask(task: Task): LiveData<Long> {
    val liveData = MutableLiveData<Long>()
    viewModelScope.launch {
        liveData.value = dao.insertTask(task)
    }
    return liveData
}

进一步建议

不要在ViewModel中保留Context的引用。如果需要使用Context,请使用AndroidViewModelAndroidViewModel#getApplication()


1
我相信,如果不调用“launch”或类似的方法,很难执行操作val id = viewModel.insertTask(t)。 要调用suspend fun,您必须在CoroutineScope内部或另一个suspend fun内部。 如果您严格要避免编写lifecycleScope.launch,则可以返回LiveData并观察它以在协程完成其工作时收到通知,就像我所说的那样。 这样您就不会阻塞Android上的MainThread。 另一方面,如果要阻塞MainThread,则可以调用runBlocking {},但这将击败在后台线程中插入的整个目的。 - Some random IT boy
如果你只是想避免使用 lifecycleScope.launch{},你可以编写以下 Kotlin 扩展函数来为你包装启动: fun Fragment.launch(coroutine: suspend CoroutineScope.() -> Unit) = lifecycleScope.launch(coroutine)。这样你就可以在任何 Fragment 中调用它,例如:fun doSomething(): = launch { /*你的协程代码放在这里*/} - Some random IT boy
你可以看到在TaskViewModel中我使用了CoroutineScope,但是当我想要从Fragment调用该方法时,我不想使用launch或其他东西。我只想像这样调用val id = viewModel.insertTask(t) - Sanjayrajsinh
1
问题在于MainThread和GlobalScope没有共享执行上下文,因此您必须通过TaskViewModel#insertTask传递回调、使用可观察模式或从Fragment调用launch。所以你可以这样做:fun insertTask(task: Task, callback: (Long) -> Unit) { GlobalScope.launch { val id = dao.insert(task) callback.invoke(id) } } - Some random IT boy
1
同时,避免使用GlobalScope;使用viewModelScope ktx可确保协程在调用ViewModel#onCleared时被取消,因此您不会泄漏任何协程。 - Some random IT boy
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接