Kotlin从入门到放弃(四)——协程下

简介:

上一篇的kotlin讲到了协程的启动、等待和取消,这一篇对kotlin协程部分内容的补充。

挂起函数

   接触到协程之后出现的一个新型的函数,以特殊修饰符suspend修饰的函数被称为挂起函数。挂起函数只能在协程中和其他挂起函数中调用,不能在其他部分使用。并且要启动一个协程,挂起函数是必须的,为了验证可以查看上一篇中提到的launch源码。

/**
 * launch实现了Job接口
 * @param context 协程上下文,默认值是 [DefaultDispatcher].
 * @param start 协程启动方式,默认值是 [CoroutineStart.DEFAULT].
 * @param parent 显示指出父Job, 通过[context]重写job.
 * @param block 具体协程的代码.
 */
public actual fun launch(
    context: CoroutineContext = DefaultDispatcher,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    parent: Job? = null,
    block: suspend CoroutineScope.() -> Unit
): Job {
    ...
}

   可以看到第四个参数block是一个挂起函数,当将一个lambda表达式传给launch时,它就是一个挂起函数。不过这一部分需要引入另一个协程启动函数async,它的源码和launch很类似,不过async是返回了一个Deferred对象,而Deferred又是继承自Job。

public actual fun <T> async(
    context: CoroutineContext = DefaultDispatcher,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    parent: Job? = null,
    block: suspend CoroutineScope.() -> T
): Deferred<T> {
    ...
}

   为什么已经有了一套Job-launch-join还需要Deferred-async-await,问题的关键就在Defered虽说继承于Job,但是他略有不同:

public actual interface Deferred<out T> : Job {
...
    public actual suspend fun await(): T
}

  从上面的代码中明显的看出,Deferred继承Job的基础上增加了out参数T(即有输出),而await函数的作用就是挂起一个协程并返回最后的执行结果,而join函数并没有返回这一项。如果挂起函数是有返回值的,那协程的启动和挂起当然要选择async+await的组合。下面提供一个例子,最后输出的结果是3:

fun main(args: Array<String>) {
    runBlocking<Unit> {
        val one = async(coroutineContext) { job1() }
        val two = async(coroutineContext) { job2() }
        println("结果是:${one.await() + two.await()}")
    }
}
suspend fun job1(): Int {
    return 1
}
suspend fun job2(): Int {
    return 2
}

Job与Deferred的对比

  已经学习到了两种协程启动的方式,但是他们是有着多种的状态和之间的切换,其实源代码中作者已经把这些都罗列好了,只需要复制粘贴就有了下面的几小结。

Job的几种状态

| 状态 | [isActive] | [isCompleted] | [isCancelled] |
| --------------------------------------- | ---------- | ------------- | ------------- |
| New (optional initial state) | false | false | false |
| Active (default initial state) | true | false | false |
| Completing (optional transient state) | true | false | false |
| Cancelling (optional transient state) | false | false | true |
| Cancelled (final state) | false | true | true |
| Completed (final state) | false | true | false |

Job状态的切换流程

这里写图片描述

Deferred的几种状态

| 状态 | [isActive] | [isCompleted] | [isCompletedExceptionally] | [isCancelled] |
| --------------------------------------- | ---------- | ------------- | -------------------------- | ------------- |
| New (optional initial state) | false | false | false | false |
| Active (default initial state) | true | false | false | false |
| Completing (optional transient state) | true | false | false | false |
| Cancelling (optional transient state) | false | false | false | true |
| Cancelled (final state) | false | true | true | true |
| Resolved (final state) | false | true | false | false |
| Failed (final state) | false | true | true | false |

Deferred状态的切换流程

这里写图片描述

调度和线程

   协程上下文包括一个协程调度程序CoroutineDispatcher,他可以指定由哪个线程来执行协程。调度器可以将协程调度到一个线程池,限制在特定的线程中;也可以不做任何限制,让它无约束运行。要去理解这句话,就必须参考launch或async构造函数中的第一个参数(可见第一章)。

runBlocking {
    val jobs = arrayListOf<Job>()
    jobs += async(Unconfined) {  // 1
        println("Unconfined is worked in ${Thread.currentThread()}")
    }
    jobs += async(coroutineContext) { // 2
        println("coroutineContext is worked in ${Thread.currentThread()}")
    }
    jobs += async(CommonPool) {  // 3
        println("CommonPool is worked in ${Thread.currentThread()}")
    }
    jobs += async(newSingleThreadContext("newThread")) { // 3
        println("newThread is worked in ${Thread.currentThread()}")
    }
    jobs.forEach { it.join() }
}
  • 注释1,使用的Unconfined,一种无限制上下文,协程会在当前调用的栈中执行直到第一次挂起,挂起之后该协程可以被任意一个线程调用以重启;
  • 注释2,coroutineContext继承CoroutineContext,在主线程中执行;
  • 注释3,CommonPool,上一篇讲过,直接继承于CoroutineDispatcher,在内部创建了一个ExecutorService类型线程池;
  • 注释4,新建一个ThreadPoolDispatcher类型的包含一个线程的协程上下文,他可以调度协程到一个固定大小的线程池中。
  • 有了上面的解释,程序运行之后的结果也能猜出个几分:
    这里写图片描述

协程的内部机制

   协程完全通过编译技术实现(不需要来自VM或OS端的支持),挂起通过代码来生效。基 本上,每个挂起函数(优化可能适用,但我们不在这里讨论)都转换为状态机,其中的状态 对应于挂起调用。刚好在挂起前,下一状态与相关局部变量等一起存储在编译器生成的类的 字段中。在恢复该协程时,恢复局部变量并且状态机从刚好挂起之后的状态进行。
   挂起的协程可以作为保持其挂起状态与局部变量的对象来存储和传递。这种对象的类型是 Continuation,而这里描述的整个代码转换对应于经典的延续性传递风格(Continuationpassing style)。因此,挂起函数有一个Continuation类型的额外参数作为高级选项。

总结

   协程的一些用法基本上可以先总结这些了,目前协程的一些api仍然是实验中的,不过也是出现了很多新名词。

  • runBlocking函数,主要用于桥接阻塞代码(如Thread)和挂起风格非阻塞(如delay)代码,这样可以保证编码风格的统一;
  • launch和async函数,都是启动协程的函数,async有返回值,分别返回Job和Deferred对象;
  • CoroutineDispatcher函数,作为CoroutineContext的子类,用于对协程的调度。
目录
相关文章
|
10天前
|
安全 Java Android开发
Kotlin入门实用开发技巧与注意事项
本文源自公众号“AntDream”。Kotlin是由JetBrains开发的现代编程语言,自2017年成为Android官方开发语言后迅速流行。本文作者分享了Kotlin的实用技巧,包括变量声明、空安全、扩展函数等,帮助初学者避免常见问题。
42 15
|
20天前
|
调度 开发者 UED
Kotlin 中的协程是什么?
【8月更文挑战第31天】
37 0
|
3月前
|
监控 程序员 调度
协程实现单线程并发(入门)
协程实现单线程并发(入门)
39 1
|
3月前
|
存储 Java 调度
Android面试题之Kotlin 协程的挂起、执行和恢复过程
了解Kotlin协程的挂起、执行和恢复机制。挂起时,状态和上下文(局部变量、调用栈、调度器等)被保存;挂起点通过`Continuation`对象处理,释放线程控制权。当恢复条件满足,调度器重新分配线程,调用`resumeWith`恢复执行。关注公众号“AntDream”获取更多并发知识。
71 2
|
4月前
|
移动开发 Android开发 开发者
构建高效Android应用:Kotlin与协程的完美融合
【5月更文挑战第25天】 在移动开发的世界中,性能和响应性是衡量应用质量的关键指标。随着Kotlin的流行和协程的引入,Android开发者现在有了更强大的工具来提升应用的性能和用户体验。本文深入探讨了Kotlin语言如何与协程相结合,为Android应用开发带来异步处理能力的同时,保持代码的简洁性和可读性。我们将通过实际案例分析,展示如何在Android项目中实现协程,以及它们如何帮助开发者更有效地管理后台任务和用户界面的流畅交互。
|
4月前
|
移动开发 监控 Android开发
构建高效安卓应用:Kotlin 协程的实践与优化
【5月更文挑战第16天】 在移动开发领域,性能优化一直是开发者们追求的重要目标。特别是对于安卓平台来说,由于设备多样性和系统资源的限制,如何提升应用的响应性和流畅度成为了一个关键议题。近年来,Kotlin 语言因其简洁、安全和高效的特点,在安卓开发中得到了广泛的应用。其中,Kotlin 协程作为一种轻量级的并发解决方案,为异步编程提供了强大支持,成为提升安卓应用性能的有效手段。本文将深入探讨 Kotlin 协程在安卓开发中的应用实践,以及通过合理设计和使用协程来优化应用性能的策略。
48 8
|
4月前
|
移动开发 数据库 Android开发
构建高效Android应用:探究Kotlin的协程优势
【5月更文挑战第22天】随着移动开发技术的不断进步,Android平台的性能优化已经成为开发者关注的焦点。在众多提升应用性能的手段中,Kotlin语言提供的协程概念因其轻量级线程管理和异步编程能力而受到广泛关注。本文将深入探讨Kotlin协程在Android开发中的应用,以及它如何帮助开发者构建出更高效、响应更快的应用,同时保持代码的简洁性和可读性。
|
4月前
|
移动开发 Android开发 开发者
构建高效安卓应用:Kotlin 协程的实践指南
【5月更文挑战第18天】 随着移动开发技术的不断进步,安卓平台亟需一种高效的异步编程解决方案来应对日益复杂的应用需求。Kotlin 协程作为一种新兴的轻量级线程管理机制,以其简洁的语法和强大的功能,成为解决这一问题的关键。本文将深入探讨Kotlin协程在安卓开发中的实际应用,从基本概念到高级技巧,为开发者提供一份全面的实践指南,旨在帮助读者构建更加高效、稳定的安卓应用。
|
4月前
|
移动开发 安全 Android开发
构建高效Android应用:Kotlin与协程的完美结合
【5月更文挑战第17天】 在移动开发领域,性能优化和流畅的用户体验是关键。对于Android平台而言,Kotlin语言凭借其简洁性和功能安全性成为开发的首选。与此同时,协程作为一种新的并发处理方式,在简化异步编程方面展现出巨大潜力。本文将深入探讨如何通过Kotlin语言以及协程技术,提升Android应用的性能和响应能力,并确保用户界面的流畅性。
|
4月前
|
移动开发 数据库 Android开发
构建高效Android应用:Kotlin与协程的完美结合
【5月更文挑战第7天】 在移动开发领域,性能优化和资源管理始终是核心议题。随着Kotlin语言的普及,其提供的协程特性为Android开发者带来了异步编程的新范式。本文将深入探讨如何通过Kotlin协程来优化Android应用的性能,实现流畅的用户体验,并减少资源消耗。我们将分析协程的核心概念,并通过实际案例演示其在Android开发中的应用场景和优势。