运行 协程超过 viewModelScope 生命周期

Run coroutine past viewModelScope lifecycle

我需要确保即使 viewModel 被清除,挂起功能也会完成。 例如,我的应用程序中有一个按钮,当单击它时会调用 viewModel.addItem(item),该函数会调用存储库,它会做两件事:

  1. 将项目插入本地数据库
  2. 向 API 发送请求以插入项目(并等待响应以更新项目)。

我想在 第 1 步 之后完成 activity,但即使在 activity 完成后仍继续第 2 步(这也会清除 viewModel 当然,这将取消 API 请求)。

我希望它表现得像这样,因为如果我只是等待两者都完成 用户的互联网连接速度很慢,它可能会超时并使用户等等 activity。相反,我觉得在本地完成任务(完成 activity 并让他在应用程序中做其他事情)并等待服务器响应然后同步发生的事情(当然如果任务根本没有完成,我必须在用户拥有更好的互联网连接时同步它,但这是题外话)。

目前我有这样的东西(它描述了超时的糟糕情况):

Activity:

viewModel.addItemLiveData.observe(this) { success ->
     if(success) //Toast well done
     else //Something went wrong

     finish()
}

button.setOnClickListener {
     val item = ... //Collected from the UI
     viewModel.addItem(item)
}

视图模型:

private val _addItemLiveData = MutableLiveData<Boolean>()
val addItemLiveData: LiveData<Boolean> = _addItemLiveData

fun addItem(item: Item) = viewModelScope.launch {
     val id = repository.addItem(item)
     _addItemLiveData.value = id > 0
}

存储库:

suspend fun addItem(item: Item) {
     val id = roomDao.insert(item)
     
     //This part needs to run after the viewModel's lifecycle
     val res = apiService.insert(item) //Wrapped in a convenient class to indicate Success or Error
     if(res is Resource.Success) {
          roomDao.update(res.value)
     }
}

(部分代码省略)

我想也许我可以以某种方式执行 GlobalScope.launch,但我读到它不是使用它的最佳实践(而且我对 kotlin 还是很陌生,所以我什至不完全确定该怎么做它)。

另外,也许我没有正确处理这个问题。感谢任何帮助。

您可以创建与您的应用程序生命周期相关联的协程作用域。 如果你正在使用依赖注入,你应该将这个范围注入到你的 viewModel 中。如果没有,您可以在自定义应用程序中创建一个范围 class。

class MyCustomApplication: Application() {
    val applicationScope = CoroutineScope(SupervisorJob())
}

class MyViewModel(private val app: MyCustomApplication) { // provide an instance of your custom application to view model

    suspend fun addItem(item: Item) {
        val id = roomDao.insert(item)
        app.applicationScope.launch {
            val res = apiService.insert(item) //Wrapped in a convenient class to indicate Success or Error
            if(res is Resource.Success) {
                roomDao.update(res.value)
            }
        } 
    }
}

您可以阅读 this 文章了解更多详情。

您可以使用 NonCancellable 上下文创建一个暂停块,如果作业或范围被取消,该块不会被中断:

suspend fun addItem(item: Item) {
     val id = roomDao.insert(item)
     
     //This part needs to run after the viewModel's lifecycle
     withContext(NonCancellable) {
         val res = apiService.insert(item) //Wrapped in a convenient class to indicate Success or Error
         if(res is Resource.Success) {
              roomDao.update(res.value)
         }
     }
}