简体   繁体   English

GlobalScope 与 CoroutineScope 与生命周期范围

[英]GlobalScope vs CoroutineScope vs lifecycleScope

I am used to working with AsyncTask and understand it pretty well due to its simplicity.我习惯于使用AsyncTask并且由于它的简单性而很好地理解它。 But Coroutines are confusing to me.但是Coroutines让我感到困惑。 Can you please explain to me in a simple way what is the difference and purpose of each of the following?您能否以简单的方式向我解释以下各项的区别和目的是什么?

  1. GlobalScope.launch(Dispatchers.IO) {}
  2. GlobalScope.launch{}
  3. CoroutineScope(Dispatchers.IO).launch{}
  4. lifecycleScope.launch(Dispatchers.IO){}
  5. lifecycleScope.launch{}

First, let's start with definitions to make it clear.首先,让我们从定义开始说清楚。 If you need a tutorial or playground for Coroutines and Coroutines Flow you can check out this tutorial/playground i created.如果您需要 Coroutines 和 Coroutines Flow 的教程或 Playground,您可以查看我创建的这个 教程/playground

Scope is object you use to launch coroutines that only contains one object which is CoroutineContext Scope是 object 用于启动仅包含一个 object 的协程,即CoroutineContext

public interface CoroutineScope {
    /**
     * The context of this scope.
     * Context is encapsulated by the scope and used for implementation of coroutine builders that are extensions on the scope.
     * Accessing this property in general code is not recommended for any purposes except accessing the [Job] instance for advanced usages.
     *
     * By convention, should contain an instance of a [job][Job] to enforce structured concurrency.
     */
    public val coroutineContext: CoroutineContext
}

The coroutine context is a set of rules and configurations that define how the coroutine will be executed.协程上下文是一组定义协程如何执行的规则和配置。 Under the hood, it's a kind of map, with a set of possible keys and values.在引擎盖下,它是一种 map,具有一组可能的键和值。

Coroutine context is immutable, but you can add elements to a context using plus operator, just like you add elements to a set, producing a new context instance协程上下文是不可变的,但是您可以使用加号运算符将元素添加到上下文中,就像您将元素添加到集合中一样,生成新的上下文实例

The set of elements that define the behavior of a coroutine are:定义协程行为的元素集是:

  • CoroutineDispatcher — dispatches work to the appropriate thread. CoroutineDispatcher — 将工作分派到适当的线程。
  • Job — controls the lifecycle of the coroutine. Job——控制协程的生命周期。
  • CoroutineName — name of the coroutine, useful for debugging. CoroutineName — 协程的名称,用于调试。
  • CoroutineExceptionHandler — handles uncaught exceptions CoroutineExceptionHandler — 处理未捕获的异常

Dispatchers Dispatchers determine which thread pool should be used. Dispatchers Dispatchers 确定应该使用哪个线程池。 Dispatchers class is also CoroutineContext which can be added to CoroutineContext调度程序 class 也是CoroutineContext可以添加到 CoroutineContext

  • Dispatchers.Default : CPU-intensive work, such as sorting large lists, doing complex calculations and similar. Dispatchers.Default :CPU 密集型工作,例如对大型列表进行排序、进行复杂计算等。 A shared pool of threads on the JVM backs it. JVM 上的共享线程池支持它。

  • Dispatchers.IO : networking or reading and writing from files. Dispatchers.IO :联网或从文件读写。 In short – any input and output, as the name states简而言之 - 任何输入和 output,如名称所述

  • Dispatchers.Main : mandatory dispatcher for performing UI-related events in Android's main or UI thread. Dispatchers.Main :用于在 Android 的主线程或 UI 线程中执行 UI 相关事件的强制调度程序。

For example, showing lists in a RecyclerView, updating Views and so on.例如,在 RecyclerView 中显示列表、更新 Views 等。

You can check out Android's official documents for more info on dispatchers.您可以查看Android 的官方文档以获取有关调度程序的更多信息。

Edit Even though official document states that 编辑即使官方文件指出

Dispatchers.IO - This dispatcher is optimized to perform disk or network I/O outside of the main thread. Dispatchers.IO - 此调度程序经过优化,可在主线程之外执行磁盘或网络 I/O。 Examples include using the Room component, reading from or writing to files, and running any network operations.示例包括使用 Room 组件、读取或写入文件以及运行任何网络操作。

Answer from Marko Topolnic Marko Topolnic的回答

IO runs the coroutine on a special, flexible thread pool. IO 在一个特殊的、灵活的线程池上运行协程。 It exists only as a workaround when you are forced to use a legacy, blocking IO API that would block its calling thread.它仅作为一种解决方法存在,当您被迫使用传统的阻塞 IO API 会阻塞其调用线程时。

might be right either.也可能是对的。

Job A coroutine itself is represented by a Job. Job协程本身由 Job 表示。 A Job is a handle to a coroutine. Job 是协程的句柄。 For every coroutine that you create (by launch or async), it returns a Job instance that uniquely identifies the coroutine and manages its lifecycle.对于您创建的每个协程(通过启动或异步),它会返回一个 Job 实例,该实例唯一地标识协程并管理其生命周期。 You can also pass a Job to a CoroutineScope to keep a handle on its lifecycle.您还可以将 Job 传递给 CoroutineScope 以掌握其生命周期。

It is responsible for coroutine's lifecycle, cancellation, and parent-child relations.它负责协程的生命周期、取消和父子关系。 A current job can be retrieved from a current coroutine's context: A Job can go through a set of states: New, Active, Completing, Completed, Cancelling and Cancelled.可以从当前协程的上下文中检索当前作业:作业可以 go 通过一组状态:新建、活动、完成、完成、取消和取消。 while we don't have access to the states themselves, we can access properties of a Job: isActive, isCancelled and isCompleted.虽然我们无法访问状态本身,但我们可以访问 Job 的属性:isActive、isCancelled 和 isCompleted。

CoroutineScope It is defined a simple factory function that takes CoroutineContext s as arguments to create wrapper around the combined CoroutineContext as CoroutineScope定义了一个简单的工厂 function ,它将CoroutineContext s 作为 arguments 来围绕组合的 CoroutineContext 创建包装器为

public fun CoroutineScope(context: CoroutineContext): CoroutineScope = ContextScope(if (context[Job]:= null) context else context + Job()) internal class ContextScope(context: CoroutineContext): CoroutineScope { override val coroutineContext: CoroutineContext = context // CoroutineScope is used intentionally for user-friendly representation override fun toString(): String = "CoroutineScope(coroutineContext=$coroutineContext)" }

and creates a Job element if the provide context does not have one already.如果提供的上下文还没有,则创建一个Job元素。

Let's look at GlobalScope source code我们来看一下 GlobalScope 源码

/** * A global [CoroutineScope] not bound to any job. * * Global scope is used to launch top-level coroutines which are operating on the whole application lifetime * and are not cancelled prematurely. * Another use of the global scope is operators running in [Dispatchers.Unconfined], which don't have any job associated with them. * * Application code usually should use an application-defined [CoroutineScope]. Using * [async][CoroutineScope.async] or [launch][CoroutineScope.launch] * on the instance of [GlobalScope] is highly discouraged. * * Usage of this interface may look like this: * * ``` * fun ReceiveChannel<Int>.sqrt(): ReceiveChannel<Double> = GlobalScope.produce(Dispatchers.Unconfined) { * for (number in this) { * send(Math.sqrt(number)) * } * } * ``` */ public object GlobalScope: CoroutineScope { /** * Returns [EmptyCoroutineContext]. */ override val coroutineContext: CoroutineContext get() = EmptyCoroutineContext }

As you can see it extends CoroutineScope如您所见,它扩展CoroutineScope

1- GlobalScope is alive as long as you app is alive, if you doing some counting for instance in this scope and rotate your device it will continue the task/process. 1-只要您的应用程序还活着,GlobalScope 就活着,如果您在此 scope 中进行一些计数并旋转您的设备,它将继续执行任务/过程。

 GlobalScope.launch(Dispatchers.IO) {}

runs as long as your app is alive but in IO thread because of using Dispatchers.IO只要您的应用程序还活着,但在 IO 线程中运行,因为使用了Dispatchers.IO

2- It's same as the first one but by default, if you don't have any context, launch uses EmptyCoroutineContext which uses Dispatchers.Default, so only difference is thread with first one. 2- 它与第一个相同,但默认情况下,如果您没有任何上下文,则启动使用 EmptyCoroutineContext,它使用 Dispatchers.Default,因此唯一的区别是线程与第一个。

3- This one is the same as first one with only syntax difference. 3-这个与第一个相同,只有语法不同。

4- lifecycleScope is an extention for LifeCycleOwner and bound to Actvity or Fragment's lifCycle where scope is canceled when that Activity or Fragment is destroyed. 4- lifecycleScopeLifeCycleOwner的扩展,并绑定到 Activity 或 Fragment 的 lifCycle,其中 scope 在该 Activity 或 Fragment 被销毁时被取消。

class Activity3CoroutineLifecycle : AppCompatActivity(), CoroutineScope {

    private lateinit var job: Job

    override val coroutineContext: CoroutineContext
        get() = job + Dispatchers.Main + CoroutineName("🙄 Activity Scope") + CoroutineExceptionHandler { coroutineContext, throwable ->
            println("🤬 Exception $throwable in context:$coroutineContext")
        }


    private val dataBinding by lazy {
        Activity3CoroutineLifecycleBinding.inflate(layoutInflater)
    }


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(dataBinding.root)
    
        job = Job()

        dataBinding. button.setOnClickListener {

            // This scope lives as long as Application is alive
            GlobalScope.launch {
                for (i in 0..300) {
                    println("🤪 Global Progress: $i in thread: ${Thread.currentThread().name}, scope: $this")
                    delay(300)
                }
            }

            // This scope is canceled whenever this Activity's onDestroy method is called
            launch {
                for (i in 0..300) {
                    println("😍 Activity Scope Progress: $i in thread: ${Thread.currentThread().name}, scope: $this")
                    withContext(Dispatchers.Main) {
                        dataBinding.tvResult.text = "😍 Activity Scope Progress: $i in thread: ${Thread.currentThread().name}, scope: $this"
                    }
                    delay(300)
                }
            }
        }

    }

    override fun onDestroy() {
        super.onDestroy()
        job.cancel()
    }

}

You can also use this as您也可以将其用作

class Activity3CoroutineLifecycle: AppCompatActivity(), CoroutineScope { private lateinit var job: Job override val coroutineContext: CoroutineContext get() = job + Dispatchers.Main + CoroutineName(" Activity Scope") + CoroutineExceptionHandler { coroutineContext, throwable -> println(" Exception $throwable in context:$coroutineContext") } private val dataBinding by lazy { Activity3CoroutineLifecycleBinding.inflate(layoutInflater) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(dataBinding.root) job = Job() dataBinding. button.setOnClickListener { // This scope lives as long as Application is alive GlobalScope.launch { for (i in 0..300) { println(" Global Progress: $i in thread: ${Thread.currentThread().name}, scope: $this") delay(300) } } // This scope is canceled whenever this Activity's onDestroy method is called launch { for (i in 0..300) { println(" Activity Scope Progress: $i in thread: ${Thread.currentThread().name}, scope: $this") withContext(Dispatchers.Main) { dataBinding.tvResult.text = " Activity Scope Progress: $i in thread: ${Thread.currentThread().name}, scope: $this" } delay(300) } } } } override fun onDestroy() { super.onDestroy() job.cancel() } }

TL;DR TL;博士

  1. GlobalScope.launch(Dispatchers.IO) : Launches a top-level coroutine on Dispatchers.IO . GlobalScope.launch(Dispatchers.IO) :在Dispatchers.IO上启动顶级协程。 Coroutine is unbound and keeps running until finished or cancelled.协程未绑定并一直运行直到完成或取消。 Often discouraged since programmer has to maintain a reference to join() or cancel() .由于程序员必须维护对join()cancel()的引用,因此通常不鼓励。

  2. GlobalScope.launch : Same as above, but GlobalScope uses Dispatchers.Default if not specified. GlobalScope.launch :与上面相同,但GlobalScope使用Dispatchers.Default如果未指定。 Often discouraged.经常气馁。

  3. CoroutineScope(Dispatchers.IO).launch : Creates a coroutine scope which uses Dispatchers.IO unless a dispatcher is specified in the coroutine builder ie launch CoroutineScope(Dispatchers.IO).launch :创建一个使用Dispatchers.IO的协程 scope ,除非在协程构建器中指定了调度程序,即launch

  4. CoroutineScope(Dispatchers.IO).launch(Dispatchers.Main) : Bonus one. CoroutineScope(Dispatchers.IO).launch(Dispatchers.Main) :奖励一。 Uses the same coroutine scope as above (if the scope instance is same!) but overrides Dispatcher.IO with Dispatchers.Main for this coroutine.使用与上述相同的协程 scope (如果 scope 实例相同!)但使用Dispatchers.Main覆盖Dispatcher.IO

  5. lifecycleScope.launch(Dispatchers.IO) : Launches a coroutine within the lifecycleScope provided by AndroidX. lifecycleScope.launch(Dispatchers.IO) :在 AndroidX 提供的生命周期范围内启动协程。 Coroutine gets cancelled as soon as lifecycle is invalidated (ie user navigates away from a fragment).一旦生命周期无效(即用户离开片段),协程就会被取消。 Uses Dispatchers.IO as thread pool.使用Dispatchers.IO作为线程池。

  6. lifecycleScope.launch : Same as above, but uses Dispatchers.Main if not specified. lifecycleScope.launch :与上面相同,但如果未指定,则使用Dispatchers.Main

Explantion解释

Coroutine scope promotes structured concurrency , whereby you can launch multiple coroutines in the same scope and cancel the scope (which in turn cancels all the coroutines within that scope) if the need be.协程 scope促进了结构化并发,因此您可以在同一个 scope 中启动多个协程并取消 scope(这反过来又取消了该范围内的所有协程)。 On the contrary, a GlobalScope coroutine is akin to a thread, where you need to keep a reference in-order to join() or cancel() it.相反,GlobalScope 协程类似于线程,您需要在其中保留引用才能join()cancel()它。 Here's an excellent article by Roman Elizarov on Medium .这是Roman Elizarov 在 Medium 上的一篇优秀文章。

CoroutineDispatcher tells the coroutine builder (in our case launch {} ) as to which pool of threads is to be used. CoroutineDispatcher告诉协程构建器(在我们的例子中是launch {} )要使用哪个线程池。 There are a few predefined Dispatchers available.有一些预定义的 Dispatcher 可用。

  • Dispatchers.Default - Uses a thread pool equivalent to number of CPU cores. Dispatchers.Default - 使用与 CPU 核心数相等的线程池。 Should be used for CPU bound workload.应该用于 CPU 密集型工作负载。
  • Dispatchers.IO - Uses a pool of 64 threads. Dispatchers.IO - 使用 64 个线程池。 Ideal for IO bound workload, where the thread is usually waiting;非常适合线程通常在等待的 IO 绑定工作负载; maybe for network request or disk read/write.可能用于网络请求或磁盘读/写。
  • Dispatchers.Main (Android only): Uses main thread to execute the coroutines. Dispatchers.Main (仅限 Android):使用主线程执行协程。 Ideal for updating UI elements.非常适合更新 UI 元素。

Example例子

I've written a small demo fragment with 6 functions corresponding to the above 6 scenarios.我编写了一个小演示片段,其中有 6 个函数对应于上述 6 个场景。 If you run the below fragment on an Android device;如果您在 Android 设备上运行以下片段; open the fragment and then leave the fragment;打开片段,然后离开片段; you'll notice that only the GlobalScope coroutines are still alive.您会注意到只有 GlobalScope 协程仍然存在。 Lifecycle coroutines are cancelled by lifecycleScope when the lifecycle is invalid.当生命周期无效时,生命周期协程会被生命周期范围取消。 On the other hand, CoroutineScope ones are cancelled on onPause() invocation which is explicitly done by us.另一方面,CoroutineScope 会在我们明确完成的onPause()调用上被取消。

class DemoFragment : Fragment() {

    private val coroutineScope = CoroutineScope(Dispatchers.IO)

    init {
        printGlobalScopeWithIO()
        printGlobalScope()
        printCoroutineScope()
        printCoroutineScopeWithMain()
        printLifecycleScope()
        printLifecycleScopeWithIO()
    }

    override fun onPause() {
        super.onPause()
        coroutineScope.cancel()
    }

    private fun printGlobalScopeWithIO() = GlobalScope.launch(Dispatchers.IO) {
        while (isActive) {
            delay(1000)
            Log.d("CoroutineDemo", "[GlobalScope-IO] I'm alive on thread ${Thread.currentThread().name}!")
        }
    }

    private fun printGlobalScope() = GlobalScope.launch {
        while (isActive) {
            delay(1000)
            Log.d("CoroutineDemo", "[GlobalScope] I'm alive on ${Thread.currentThread().name}!")
        }
    }
    
    private fun printCoroutineScope() = coroutineScope.launch {
        while (isActive) {
            delay(1000)
            Log.d("CoroutineDemo", "[CoroutineScope] I'm alive on ${Thread.currentThread().name}!")
        }
        Log.d("CoroutineDemo", "[CoroutineScope] I'm exiting!")
    }

    private fun printCoroutineScopeWithMain() = coroutineScope.launch(Dispatchers.Main) {
        while (isActive) {
            delay(1000)
            Log.d("CoroutineDemo", "[CoroutineScope-Main] I'm alive on ${Thread.currentThread().name}!")
        }
        Log.d("CoroutineDemo", "[CoroutineScope-Main] I'm exiting!")
    }

    private fun printLifecycleScopeWithIO() = lifecycleScope.launch(Dispatchers.IO) {
        while (isActive) {
            delay(1000)
            Log.d("CoroutineDemo", "[LifecycleScope-IO] I'm alive on ${Thread.currentThread().name}!")
        }
        Log.d("CoroutineDemo", "[LifecycleScope-IO]  I'm exiting!")
    }

    private fun printLifecycleScope() = lifecycleScope.launch {
        while (isActive) {
            delay(1000)
            Log.d("CoroutineDemo", "[LifecycleScope] I'm alive on ${Thread.currentThread().name}!")
        }
        Log.d("CoroutineDemo", "[LifecycleScope] I'm exiting!")
    }

}

I'd organize your list along three axes:我会沿着三个轴组织你的列表:

  1. GlobalScope vs. CoroutineScope() vs. lifecycleScope GlobalScopeCoroutineScope()lifecycleScope范围
  2. Dispatchers.IO vs. inherited (implicit) dispatcher Dispatchers.IO与继承(隐式)调度程序
  3. Specify the dispatcher in the scope vs. as an argument to launch在 scope vs. 中指定调度程序作为launch的参数

1. Choice of Scope 1. Scope的选择

A big part of Kotlin's take on coroutines is structured concurrency , which means all the coroutines are organized into a hierarchy that follows their dependencies. Kotlin 对协程的很大一部分是结构化并发,这意味着所有协程都被组织成一个层次结构,遵循它们的依赖关系。 If you're launching some background work, we assume you expect its results to appear at some point while the current "unit of work" is still active, ie, the user hasn't navigated away from it and doesn't care anymore about its result.如果您正在启动一些后台工作,我们假设您希望它的结果出现在当前“工作单元”仍然处于活动状态的某个时间点,即用户没有离开它并且不再关心它的结果。

On Android, you have the lifecycleScope at your disposal that automatically follows the user's navigation across UI activities, so you should use it as the parent of background work whose results will become visible to the user.在 Android 上,您拥有可以自动跟随用户在 UI 活动中导航的lifecycleScope范围,因此您应该将其用作后台工作的父级,其结果将对用户可见。

You may also have some fire-and-forget work, that you just need to finish eventually but the user doesn't await its result.您可能还有一些即发即弃的工作,您只需要最终完成,但用户不会等待其结果。 For this you should use Android's WorkManager or similar features that can safely go on even if the user switches to another application.为此,您应该使用 Android 的WorkManager或类似的功能,即使用户切换到另一个应用程序也可以安全地打开 go。 These are usually tasks that synchronize your local state with the state kept on the server side.这些通常是将本地 state 与保存在服务器端的 state 同步的任务。

In this picture, GlobalScope is basically an escape hatch from structured concurrency.在这张图中, GlobalScope基本上是结构化并发的一个逃生舱。 It allows you to satisfy the form of supplying a scope, but defeats all the mechanisms it's supposed to implement.它允许您满足提供 scope 的形式,但破坏了它应该实现的所有机制。 GlobalScope can never be cancelled and it has no parent. GlobalScope永远不能被取消,并且它没有父级。

Writing CoroutineScope(...).launch is just wrong because you create a scope object without a parent that you immediately forget, and thus have no way of cancelling it.编写CoroutineScope(...).launch是错误的,因为您创建的 scope object 没有您立即忘记的父级,因此无法取消它。 It's similar to using GlobalScope but even more hacky.它与使用GlobalScope类似,但更 hacky。

2. Choice of Dispatcher 2.调度员的选择

The coroutine dispatcher decides which threads your coroutine may run on.协程调度器决定您的协程可以在哪些线程上运行。 On Android, there are three dispatchers you should care about:在 Android 上,您应该关心三个调度程序:

  1. Main runs everything on the single GUI thread. Main在单个 GUI 线程上运行所有内容。 It should be your main choice.它应该是你的主要选择。
  2. IO runs the coroutine on a special, flexible thread pool. IO在一个特殊的、灵活的线程池上运行协程。 It exists only as a workaround when you are forced to use a legacy, blocking IO API that would block its calling thread.它仅作为一种解决方法存在,当您被迫使用传统的阻塞 IO API 会阻塞其调用线程时。
  3. Default also uses a thread pool, but of fixed size, equal to the number of CPU cores. Default也使用线程池,但大小固定,等于 CPU 核心数。 Use it for computation-intensive work that would take long enough to cause a glitch in the GUI (for example, image compression/decompression).将它用于计算密集型工作,这些工作需要足够长的时间导致 GUI 出现故障(例如,图像压缩/解压缩)。

3. Where to Specify the Dispatcher 3. 在哪里指定 Dispatcher

First, you should be aware of the dispatcher specified in the coroutine scope you're using.首先,您应该了解您正在使用的协程 scope 中指定的调度程序。 GlobalScope doesn't specify any, so the general default is in effect, the Default dispatcher. GlobalScope没有指定任何内容,因此一般默认值是有效的,即Default调度程序。 lifecycleScope specifies the Main dispatcher. lifecycleScope指定Main调度程序。

We already explained that you shouldn't create ad-hoc scopes using the CoroutineScope constructor, so the proper place to specify an explicit dispatcher is as a parameter to launch .我们已经解释过您不应该使用CoroutineScope构造函数创建临时作用域,因此指定显式调度程序的正确位置是作为launch的参数。

In technical detail, when you write someScope.launch(someDispatcher) , the someDispatcher argument is actually a full-fledged coroutine context object which happens to have a single element, the dispatcher.在技术细节上,当您编写someScope.launch(someDispatcher)时, someDispatcher参数实际上是一个成熟的协程上下文 object,它恰好有一个元素,即调度程序。 The coroutine you're launching creates a new context for itself by combining the one in the coroutine scope and the one you supply as a parameter.您正在启动的协程通过将协程 scope 中的一个和您作为参数提供的一个组合来为自己创建一个新的上下文。 On top of that, it creates a fresh Job for itself and adds it to the context.最重要的是,它为自己创建了一个新的Job并将其添加到上下文中。 The job is a child of the one inherited in the context.该作业是在上下文中继承的作业的子作业。

You should know that if you want to launch suspend function you need to do it in CoroutineScope .你应该知道,如果你想启动suspend function 你需要在CoroutineScope中进行。 Every CoroutineScope have CoroutineContext .每个CoroutineScope都有CoroutineContext Where CoroutineContext is a map that can contain Dispatcher (dispatches work to the appropriate thread), Job (controls the lifecycle of the coroutine), CoroutineExceptionHandler (handles uncaught exceptions), CoroutineName (name of the coroutine, useful for debugging).其中CoroutineContext是一个 map,它可以包含Dispatcher (将工作分派到适当的线程)、 Job (控制协程的生命周期)、 CoroutineExceptionHandler (处理未捕获的异常)、 CoroutineName (协程的名称,用于调试)。

  1. GlobalScope.launch(Dispatchers.IO) {} - GlobalScope.launch creates global coroutines and using for operations that should not be canceled, but a better alternative would be creating a custom scope in the Application class, and inject it to the class that needs it. GlobalScope.launch(Dispatchers.IO) {} - GlobalScope.launch创建全局协程并用于不应取消的操作,但更好的选择是在应用程序 class 中创建自定义 scope,并将其注入需要的 ZA2F2ED4F8EBC2CBBDC214它。 This has the advantage of giving you the ability to use CoroutineExceptionHandler or replace the CoroutineDispatcher for testing.这样做的好处是让您能够使用CoroutineExceptionHandler或替换CoroutineDispatcher进行测试。
  2. GlobalScope.launch{} - same as GlobalScope.launch(Dispatchers.IO) {} but runs coroutines on Dispatchers.Default . GlobalScope.launch{} - 与GlobalScope.launch(Dispatchers.IO) {}相同,但在Dispatchers.Default上运行coroutines Dispatchers.Default is a default Dispatcher that is used if no dispatchers is specified in their context. Dispatchers.Default是一个默认Dispatcher ,如果在其上下文中未指定任何调度程序,则使用该调度程序。
  3. CoroutineScope(Dispatchers.IO).launch{} - it's create scope with one parameter and launch new coroutine in it on IO thread. CoroutineScope(Dispatchers.IO).launch{} - 它使用一个参数创建 scope 并在IO线程上启动新的coroutine Will be destroyed with object where it was launched.将与启动它的 object 一起销毁。 But you should manually call .cancel() for CoroutineScope if you want to end your work properly.但是如果你想正确地结束你的工作,你应该为CoroutineScope手动调用.cancel()
  4. lifecycleScope.launch(Dispatchers.IO){} - it is existing scopes that available from a Lifecycle or from a LifecycleOwner ( Activity or Fragment ) and comes in your project with dependency androidx.lifecycle:lifecycle-runtime-ktx:* . lifecycleScope.launch(Dispatchers.IO){} - 它是可从LifecycleLifecycleOwner ( ActivityFragment ) 获得的现有范围,并以依赖androidx.lifecycle:lifecycle-runtime-ktx:*出现在您的项目中。 Using it you can rid from manualy creating CoroutineScope .使用它,您可以摆脱手动创建CoroutineScope It will run your job in Dispatchers.IO without blocking MainThread , and be sure that your jobs will be cancelled when your lifecycle is destroyed.它将在Dispatchers.IO中运行您的作业而不阻塞MainThread ,并确保您的作业将在您的lifecycle被销毁时被取消。
  5. lifecycleScope.launch{} - same as lifecycleScope.launch(Dispatchers.IO){} that create CoroutinesScope for you with default Dispatchers.Main parameter and runs your coroutines in Dispatcher.Main that mean you can work with UI . lifecycleScope.launch{} - 与lifecycleScope.launch(Dispatchers.IO){}相同,它使用默认的Dispatchers.Main参数为您创建CoroutinesScope并在Dispatcher.Main中运行您的coroutines ,这意味着您可以使用UI

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM