在 Android 中将全局变量与协程一起使用

Using Global Variables with Coroutines in Android

我有以下代码,我在其中启动协程来处理数据检索并将它们存储到本地数据库中:

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 递增,然后我们再次将 isRequestInProgress 设置为 false,这样整个过程就可以由程序从任何地方再次启动。 请注意,lastRequestedPage 作为参数传递给 Retrofit 网络调用函数,因为数据来自的 Web 服务使用分页(为了简洁起见,我将其省略)。

我的问题

我可以假设此逻辑适用于这样的协程吗?我可以用这样的解决方案遇到一些不好的线程问题吗?我问是因为我是协程概念的新手,我正在从我的另一个项目中改编这个逻辑,在这个项目中,我使用带有 Retrofit 的侦听器和回调来执行异步工作(每当调用 Retrofit#onResponse 方法时,我都会递增 lastRequestedPage 变量并将 isRequestInProgress 设置回 false)。

简短回答:不,这行不通。这是不正确的。

当您调用 viewModelScope.launch { }GlobalScope.launch { } 时,launch 中的块将被挂起。然后程序流转到下一条语句。

在您的情况下,viewModelScope.launch { } 将暂停对 withContext(...) 的调用并继续执行 lastRequestedPage++ 语句。

它会立即增加 lastRequestedPage 并切换 isRequestInProgress 标志,甚至在实际开始请求之前。

您要做的是将这些语句移到 launch { } 块中。

流程是这样的。

  • 主线程
  • 暂停块(启动调用)
  • 继续前进 - 不关心挂起的块 - 进行 UI 更改等

要更好地了解其工作原理,请尝试此代码。

    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