在 Android 上启动协程的正确方法是什么?

What is the proper way to launch a coroutine on Android?

我正在尝试弄清楚如何启动协程。我想让它依次调用两个挂起函数。

我阅读的第一个文档说要这样做:

class Something {
  fun initialize() {
    launch {
      suspendFun1()
      suspendFun2()
    }
}

但是 Android Studio 没有找到 launch 方法。然后我了解到 official coroutine docs 建议使用 GlobalScope.launch:

class Something {
  fun initialize() {
    GlobalScope.launch {
      suspendFun1()
      suspendFun2()
    }
}

但后来我在 中读到您不应该使用 GlobalScope.launch

所以我找到另一个 blog post 解释说我需要一个 CoroutineScope 来调用 launch。但它没有解释如何构建一个。

然后我发现这个 blog post 解释了如何通过在 class 上实现 CoroutineScope 来为 Activity 和 ViewModels 构建一个:

class Something : CoroutineScope {
  private lateinit var job: Job
  override val coroutineContext: CoroutineContext
        get() = job + Dispatchers.Main

  fun initialize() {
    job = Job()
    launch {
      suspendFun1()
      suspendFun2()
    }
}

然后我读到这个 ​​blog post 说我不应该实施 CoroutineScope

class Something {
  protected val scope = CoroutineScope(
    Job() + Dispatchers.Main
  )

  fun initialize() {
    scope.launch {
      suspendFun1()
      suspendFun2()
    }
}

但我不确定我是否理解 Job() + Dispatchers.Main 的含义,因为这似乎也有效:

class Something {
  protected val scope = CoroutineScope(Dispatchers.Main)

  fun initialize() {
    scope.launch {
      suspendFun1()
      suspendFun2()
    }
}

有人可以简单地向我解释一下执行此操作的最佳方法吗?是不是和上面一样我确定已经有人问过这个问题,所以如果这是一个重复的问题,我深表歉意。但如您所见,对此没有明确的答案。我想听听您的意见。

最后两个解决方案没问题。 CoroutineScope(context: CoroutineContext) 如果您传递一个没有上下文的上下文,则会创建一个空的 Job

指定 job + dispatcher 只是构建它的更明确的方式,因为在某些情况下您可能希望使用 SupervisorJob 来防止在其中一个子作业失败时取消整个范围。

至于在 ActivitiesViewModels 中构建作用域,您可以通过导入 KTX 片段模块使用 KTX 库中内置的作用域,而不是声明您自己的作用域:

// add to your apps dependencies
implementation 'androidx.fragment:fragment-ktx:1.2.0-rc02'

现在,在您的 ActivityFragment 中,您可以使用 lifecycleScope and inside ViewModel a viewModelScope,它们是由 SupervisorJob + Dispatchers.Main.immediate 支持的范围,并且在它们各自的生命周期被破坏时自动取消。

我有Coroutines.kt

object Coroutines {

    fun main(work: suspend (() -> Unit)) {
        CoroutineScope(Dispatchers.Main).launch {
            work()
        }
    }

}

我通过调用 Couroutines.main

在我的视图模型中使用它

我处于非常相似的困境中,OP 出色地捕捉到了新手对各种资源的困惑。就我而言,除了创建自己的 CoroutineScope 之外,我别无选择,因为我需要一个 long-运行 作用域,它的生命周期与应用程序进程一样长(它使用挂起函数处理与 Retrofit 的交互) , 它位于 Android 库中,无法访问 ViewModel 和其他 LifecycleOwner 项。

/**
 * A [CoroutineScope] for HTTP cloud implementations to launch coroutines in. A coroutine
 * dispatcher is not specified because Retrofit will switch to its own custom dispatcher
 * when performing suspending network calls.
 */
internal val networkingScope = CoroutineScope(SupervisorJob())

对于那些只是在寻找一种简单的方法来启动带有 Android Activity 的协程的人来说,这里有一个快速简单的方法(但你仍然应该研究学习如何正确使用协程,即使用 ViewModels 和 CoroutineScope)

  1. androidx.lifecycle 添加到您的 app-level build.gradle:

implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.1"(查看上面的 Link 了解当前版本)

  1. 在Activity中,使用Activity-providedLifecycleScope启动协程(主要是pseudo-code):
import androidx.lifecycle.lifecycleScope

class YourActivity {

  override fun onCreate() {
    lifeCycleScope.launch {
      // do your Coroutine Stuff here, i.e. call a suspend fun:
      coroutineFunction()
    }
  }

  suspend fun coroutineFunction() {
    // Use a different CoroutineScope, etc
    CoroutineScope(Dispatchers.IO).launch {
      // do some long running operation or something
    }
  }

}