Kotlin协程简介(一) Hello,coroutines!

简介: ## 协程的作用 协程并不是一个新鲜概念,相信大家都有所了解,它的好处是可以极大程度的复用线程,通过让线程满载运行,达到最大程度的利用CPU,进而提升应用性能。它和反应式编程一样都可以有效的提高资源的利用率,并且让我们脱离`callback hell`。目前JAVA里还没有原生的协程库(AJDK里对协程提供了支持)。Kotlin从1.1开始支持协程,不过目前还处于试验阶段,感兴趣的同学可以查看[

协程的作用

协程并不是一个新鲜概念,相信大家都有所了解,它的好处是可以极大程度的复用线程,通过让线程满载运行,达到最大程度的利用CPU,进而提升应用性能。它和反应式编程一样都可以有效的提高资源的利用率,并且让我们脱离callback hell。目前JAVA里还没有原生的协程库(AJDK里对协程提供了支持)。Kotlin从1.1开始支持协程,不过目前还处于试验阶段,感兴趣的同学可以查看这篇文档

如何在kotlin里使用协程

launch

由于协程还处于试验阶段,没有在Kotlin的标准库里提供,因此我们需要先引入一下相关的Jar包,Maven引用如下(其它同理)

<dependency>
    <groupId>org.jetbrains.kotlinx</groupId>
    <artifactId>kotlinx-coroutines-core</artifactId>
    <version>0.25.0</version>
</dependency>

然后直接上代码,先把官网上最简单的Hello world抄过来看看:

fun main(args: Array<String>) {
    launch { // 开启协程
        delay(1000L) // 阻塞协程,只能在协程里调用
        println("World!")  
    }
    println("Hello,") 
    Thread.sleep(3000L) // 等待协程执行
}

运行会得到如下结果

Hello,
World!

我们发现代码后边的 println("Hello,") 先执行了,可以肯定是launchdelay搞的鬼,如果隐藏在launch后边的是线程,也能得到完全同样的效果,不过这里隐藏的是协程,它在这里和线程有着同样的表现,确是非常轻量级的异步实现。

launch是协程的启动函数,注意它本身并不是一个协程,大括号里的部分才是协程的本体,我们知道大括号内部其实是个lambda表达式,而lambda表达式会被编译成一个匿名内部类的对象,反编译一下这个匿名内部类,大概是这样的:

final class Demo1Kt$main$1 extends CoroutineImpl implements Function2 {

   private CoroutineScope p$;


   @Nullable
   public final Object doResume(@Nullable Object data, @Nullable Throwable throwable) {
      Object var5 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
      switch(super.label) {
      case 0:
         if(throwable != null) {
            throw throwable;
         }

         CoroutineScope var3 = this.p$;
         super.label = 1;
         if(DelayKt.delay$default(1000L, (TimeUnit)null, this, 2, (Object)null) == var5) {
            return var5;
         }
         break;
      case 1:
         if(throwable != null) {
            throw throwable;
         }
         break;
      default:
         throw new IllegalStateException("call to \'resume\' before \'invoke\' with coroutine");
      }

      String var4 = "World!";
      System.out.println(var4);
      return Unit.INSTANCE;
   }

   Demo1Kt$main$1(Continuation var1) {
      super(2, var1);
   }

   @NotNull
   public final Continuation create(@NotNull CoroutineScope $receiver, @NotNull Continuation continuation) {
      Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
      Intrinsics.checkParameterIsNotNull(continuation, "continuation");
      Demo1Kt$main$1 var3 = new Demo1Kt$main$1(continuation);
      var3.p$ = $receiver;
      return var3;
   }

   @Nullable
   public final Object invoke(@NotNull CoroutineScope $receiver, @NotNull Continuation continuation) {
      Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
      Intrinsics.checkParameterIsNotNull(continuation, "continuation");
      return ((Demo1Kt$main$1)this.create((CoroutineScope)$receiver, continuation)).doResume(Unit.INSTANCE, (Throwable)null);
   }
}

也不知道都是什么鬼(以后再说),我们只需要知道它实现了CoroutineImpl即可,CoroutineImpl实现了Continuation接口,Continuation代表了Kotlin里的协程,协程运行,其实就是运行Continuation

我们再看下launch的定义,

public fun launch(context: CoroutineContext , start: CoroutineStart , parent: Job?, onCompletion: CompletionHandler? , block: suspend CoroutineScope.() -> Unit):Job

先忽略它的参数,看看它的返回值Job,Job最常用的方法有三个,分别是start,joincancel,cancel没啥好说的,我们看看如何使用startjob:

fun main(args: Array<String>) = runBlocking(){
    val job = launch(EmptyCoroutineContext,CoroutineStart.LAZY) { // 开启协程
        println("execuse me!")
        doSomeDelay()
    }
    Thread.sleep(2000)
    println("Hello,")
    job.start()
    job.join()
}

suspend fun doSomeDelay() {
    delay(1000)
    println("World!")
}

这段代码的输出是

Hello,
execuse me!
World!

它除了调用了job的start和join方法外,还给launch传递了两个参数,引入了suspendrunBlocking两个关键字。我们一一看来:
CoroutineContext这玩意从名字看是协程上下文,我们先忽略,传个默认值,
CoroutineStart代表协程的启动方式,它是个枚举,如果它的值是LAZY,则协程不会立即启动,需要我们手工启动,这也是job.start()的意义。看输出,虽然先调用了launch方法,但是协程里的第一行代码哪怕主线程sleep了2000ms,也没有执行,而是一直等到调用start方法之后才执行。可以证明协程并没有随着launch的调用启动。

join方法,表示当前协程等待job所在的协程执行结束。它和线程里的join方法有同样的语义,大家对比着学习即可。这里需要注意的是join方法是suspend关键字修饰的,我们看下它的声明:

public abstract suspend fun join(): kotlin.Unit

一个协程方法必须被suspend修饰,且suspend修饰的方法必须在协程体里或者被suspend修饰的方法调用,比如第一段程序的代码,delay也是suspend修饰的,把delay那行放在launch外边,就会产生编译错误。这也是为什么我们把上边的代码里用了runBlocking方法。runBlocking会开启一个新的协程,并阻塞当前的线程,直到里面的代码执行完毕。

官方说法:

Runs new coroutine and blocks current thread interruptibly until its completion.

async

写到这里想必大家发现launch没法获取协程里代码的返回值,它有点像线程里的Runnable,但是很多情况下我们需要返回值,这时就需要async方法出场了,我们再来一个hello world:

fun main(args: Array<String>) = runBlocking {

    val deffered = async {
        doSomeThing()
    }
    println("Hello,")
    println(deffered.await())

}

suspend fun doSomeThing(): String {
    delay(1000)
    return "world!"
}

它的输出如下:

Hello,
world!

Deferred继承了Job,它除了拥有Job的方法外,还有一个await方法,它也是被suspend修饰的方法,因此我们在runBlocking里调用它。其它方面基本都和launch以及Job大同小异,就不赘述了。

总结

我们回顾一下,这里讲了

  1. 启动无返回值协程的launch方法和它的返回值Job,可以把它理解成线程里的Runnable
  2. 有返回值协程的async方法和它的返回值Deferred,可以把它理解成线程里的Callable
  3. 声明协程的关键字suspend
  4. 阻塞执行的runBlocking

有了这些,我们已经基本可以用协程实现功能了,更高级的东西下篇再说。

参考文献

https://kymjs.com/code/2017/11/24/01/
https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md#composing-suspending-functions
http://kotlinlang.org/docs/reference/coroutines.html#experimental-status-of-coroutines
https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/run-blocking.html

AD Time

国际惯例,给我们的读书群[独来读往]打个广告。欢迎喜欢读书的小伙伴加入我们,一起交流,一起成长.详见
https://lark.alipay.com/growth/notes/ewqntu

好久没打广告了o( ̄︶ ̄)o

目录
相关文章
|
14天前
|
调度 开发者 UED
Kotlin 中的协程是什么?
【8月更文挑战第31天】
36 0
|
14天前
|
移动开发 Java Android开发
探索安卓应用开发的新趋势:Kotlin与Coroutines
【8月更文挑战第31天】本文旨在为读者揭示安卓开发中的最新潮流,特别是Kotlin语言及其协程特性如何重塑移动应用的构建方式。我们将通过实际代码示例深入理解Kotlin简洁语法背后的强大功能,并探讨如何利用Coroutines优雅地处理异步任务,从而提升应用性能和用户体验。无论你是安卓开发的新手还是资深开发者,这篇文章都将为你带来新的启示和灵感。
|
3月前
|
存储 Java 调度
Android面试题之Kotlin 协程的挂起、执行和恢复过程
了解Kotlin协程的挂起、执行和恢复机制。挂起时,状态和上下文(局部变量、调用栈、调度器等)被保存;挂起点通过`Continuation`对象处理,释放线程控制权。当恢复条件满足,调度器重新分配线程,调用`resumeWith`恢复执行。关注公众号“AntDream”获取更多并发知识。
71 2
|
3月前
|
Java Kotlin
Kotlin 中的 with 函数简介
Kotlin 中的 with 函数简介
196 0
|
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开发中的应用场景和优势。