我有以下代码,在其中我启动一个协程以处理数据的检索并将其存储到本地数据库中:
private var lastRequestedPage = 1
private var isRequestInProgress = false
private val viewModelScope = CoroutineScope(viewModelJob + Dispatchers.Main)
// function that is called from several places
private fun requestAndSaveData(){
if(isRequestInProgress) return
isRequestInProgress = true
viewModelScope.launch{
withContext(Dispatchers.IO){
// 2 heavyweight operations here:
// retrieve some data via Retrofit & place it into the local data via Room Persistence Library
}
}
lastRequestedPage++
isRequestInProgress = false
}
网络调用和数据库操作基于名为isRequestInProgress
的布尔值完成。如果设置为false,则将其设置为true,协程可以启动网络和数据库操作,然后在将lastRequestedPage
再次设置为false之前递增isRequestInProgress
,以便整个过程可以可以从任何地方再次由程序启动。
请注意,lastRequestedPage
作为参数传递给Retrofit网络调用函数,因为数据所来自的Web服务使用了分页(为简洁起见,我省略了它)。
我可以假定这种逻辑可以与这样的协程一起工作吗?这样的解决方案可以解决一些不好的线程问题吗?我之所以问是因为我对协程的概念不熟悉,因此我从另一个我的项目中改编了此逻辑,在该项目中,我使用了带有Retrofit的侦听器和回调来执行异步工作(每当Retrofit#onResponse
方法被称为我递增{{1} }变量,并将lastRequestedPage
设置为false)。
答案 0 :(得分:1)
简短的回答:不,这行不通。不对。
当您呼叫viewModelScope.launch { }
或为此事GlobalScope.launch { }
时,launch
内部的块将被挂起。程序流程移至下一条语句。
对于您而言,viewModelScope.launch { }
将暂停对withContext(...)
的调用,然后继续执行lastRequestedPage++
语句。
在实际开始请求之前,它将立即增加lastRequestedPage
并切换isRequestInProgress
标志。
您要做的是将这些语句移到launch { }
块中。
这是流程的工作方式。
要更好地了解其工作原理,请尝试以下代码。
Log.d("cor", "Hello, world!") // Main thread
GlobalScope.launch {
// Suspend this block. Wait for Main thread to be available
Log.d("cor", "I am inside the launch block") // Main thread
withContext(Dispatchers.IO) {
delay(100L) // IO thread
Log.d("cor", "I am inside the io block") // IO thread
}
Log.d("cor", "IO work is done") // Back to main thread
}
Log.d("cor", "I am outside the launch block") // Main thread
输出为
D/cor: Hello, world!
D/cor: I am outside the launch block
D/cor: I am inside the launch block
D/cor: I am inside the io block
D/cor: IO work is done