Kotlin协程(2)✔️创建协程

  • kotlin 协程 API
  • 创建支持 kotlinx.coroutines 的项目
  • 第一个协程程序
  • launch 函数
  • Job 对象
  • runBlocking 函数
  • 挂起函数

kotlin 协程 API

Kotlin 支持协程,并提供了丰富的协程编程所需的 API,主要是三个方面的支持:
(1) 语言支持。kotlin 语言本身提供一些对协程的支持,例如 kotlin 中的 suspend 关键字可以声明一个挂起函数。
(2) 底层 API。kotlin 标准库中包含协程编程核心底层 API,来自于 kotlin. coroutines 包,这些底层 API 虽然也可以编写携程代码,但是使用起来非常麻烦,不推荐直接使用这些底层 API。
(3) 高级 API。高级 API 使用起来很简单,但 kotlin 标准库中没有高级 API,它来自于 kotlin 的扩展项目 kotlinx.coroutines 框架(https://github.com/Kotlin/kotlinx.coroutines),使用时需要额外配置项目依赖关系。

创建支持 kotlinx.coroutines 的项目

  kotlinx.coroutines 提供了协程开发的高级 API,使用起来比标准库中的底层 API 要简单得多。但使用 kotlinx.coroutines需要额外在项目中配置依赖关系。下面是在 build.gradle 文件中添加 kotlinx.coroutines 依赖关系的配置。

apply plugin: 'java-library'
apply plugin: 'kotlin'

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinx_coroutines" // 1️⃣
}

sourceCompatibility = "8"
targetCompatibility = "8"

buildscript {
    ext.kotlin_version = '1.3.31' // 2️⃣
    ext.kotlinx_coroutines = '1.3.0-M1'
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

repositories {
    mavenCentral()
}

compileKotlin {
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

compileTestKotlin {
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

  上述代码第1️⃣行的 implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinx_coroutines" 是刚刚添加的依赖关系。另外,还需要检测代码第2️⃣行的 ext.kotlin_version 是否为最新的 kotlin 版本。

第一个协程程序

  协程是轻量级的线程,因此协程也是由主线程管理的,如果主线程结束那么协程也就结束了。

fun main(args: Array?) {
    GlobalScope.launch { // 1️⃣
        for (i in 1..10) {
            println("子协程执行第${i}次")
            val sleepTime = (random() * 1000).toLong()
            delay(sleepTime) // 2️⃣
        }
        println("子协程执行结束")
    }
    sleep(10 * 1000) // 3️⃣
    println("主程序结束...")
}

运行结果:

子协程执行第1次
子协程执行第2次
子协程执行第3次
子协程执行第4次
子协程执行第5次
子协程执行第6次
子协程执行第7次
子协程执行第8次
子协程执行第9次
子协程执行第10次
子协程执行结束
主程序结束...

  上述代码第1️⃣行的 GlobalScope.launch 函数创建并启动了一个协程,类似于线程的 thread 函数。代码第2️⃣行的 delay 函数是挂起协程,类似于线程的 sleep 函数,但不同的是 delay 函数不会阻塞线程,而 sleep 函数会阻塞线程。代码第3️⃣行时候让主线程休眠 10s。如果这里主线程不休眠,主线程就直接结束了,其他的线程或协程没有机会运行。

launch 函数

  上面示例中的 GlobalScope.launch 函数是非常重要的,它的定义如下:

public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job
  • CoroutineScope 可以理解为协程本身,包含了CoroutineContext

  • context 协程上下文,是一些元素的集合,主要包括 JobCoroutineDispatcher 元素,可以代表一个协程的场景。

  • CoroutineDispatcher协程调度器,决定协程所在的线程或线程池。它可以指定协程运行于特定的一个线程、一个线程池或者不指定任何线程(这样协程就会运行于当前线程)。coroutines-coreCoroutineDispatcher 有以下实现 Dispatchers.DefaultDispatchers.IODispatchers.MainDispatchers.UnconfinedUnconfined 就是不指定线程。launch 函数定义如果不指定 CoroutineDispatcher或者 没有其他的ContinuationInterceptor,默认的协程调度器就是Dispatchers.DefaultDefault 是一个协程调度器,其指定的线程为共有的线程池,线程数量至少为 2 最大与 CPU 数相同。

  • start 参数设置协程启动。

  • block 参数是协程体,类似于线程体,协程执行的核心代码在此编写,在协程体中执行的函数应该都是挂起函数,例如 delay 函数就是挂起函数。

  • launch 函数的返回是一个 Job 对象,Job 是协程要执行的任务,可以将 Job 对象看作协程本身,所有对协程的操作都是通过 Job 对象完成的,协程的状态和生命周期都是通过 Job 反应出来的。

Job 对象

Job 对象中常用的属性和函数如下:

  • isActive 属性:判断 Job 是否处于获得状态。
  • isCompleted 属性:判断 Job 是否处于完成状态。
  • isCancelled 属性:判断 Job 是否处于取消状态。
  • start 函数:开始 Job。
  • cancel 函数:取消 Job。
  • join 函数:使当前协程处于等待状态,直到 Job 完成,join 是一个挂起函数,只能在协程体或其他的挂起函数中调用。
fun main(args: Array?) {
    val job = GlobalScope.launch {
        for (i in 1..10) {
            println("子协程执行第${i}次")
            val sleepTime = (random() * 1000).toLong()
            delay(sleepTime)
        }
        println("子协程执行结束")
    }
    println(job.isActive)
    println(job.isCompleted)
    sleep(10 * 1000)
    println("主程序结束...")
    println(job.isCompleted)
}
// 运行结果:
true
false
子协程执行第1次
子协程执行第2次
子协程执行第3次
子协程执行第4次
子协程执行第5次
子协程执行第6次
子协程执行第7次
子协程执行第8次
子协程执行第9次
子协程执行第10次
子协程执行结束
主程序结束...
true

runBlocking 函数

  前面的例子为了保持其他线程处于活动状态,示例中都使用了 sleep 函数。sleep 函数是线程提供的函数,最好不要在协程中使用,应该使用协程自己的 delay 函数,但 delay 是挂起函数,必须在协程体 或 其他的挂起函数中使用。

fun main(args: Array?) = runBlocking {
    GlobalScope.launch {
        for (i in 1..10) {
            println("子协程执行第${i}次")
            val sleepTime = (random() * 1000).toLong()
            delay(sleepTime)
        }
        println("子协程执行结束")
    }
    delay(10 * 1000) // 1️⃣
    println("主程序结束...")
}
// 运行结果
子协程执行第1次
子协程执行第2次
子协程执行第3次
子协程执行第4次
子协程执行第5次
子协程执行第6次
子协程执行第7次
子协程执行第8次
子协程执行第9次
子协程执行第10次
子协程执行结束
主程序结束...

  上述代码将 main 代码放到 runBlocking 函数中,runBlocking 函数也是启动并创建一个协程,可以与顶层函数一起使用。代码第1️⃣行使用 delay 函数挂起主协程。

挂起函数

  如果开发人员需要编写一个挂起函数,可以使用 suspend 关键字声明,语法如下:

suspend fun 函数名(参数列表): 返回类型 {
    // 函数体
}

  注意:挂起函数只能在协程体中 或 其他的挂起函数中调用,不能在普通函数中调用。

  挂起函数不仅可以是顶层函数,还可以是成员函数和抽象函数,子类重写挂起函数后还应该是挂起的。

abstract class SuperClass {
    suspend abstract fun run()
}

class SubClass: SuperClass() {
    override suspend fun run() { }
}

  上述代码 SubClass 类实现了抽象类 SuperClass 的抽象挂起函数 run,重写后它还是挂起函数。

fun main(args: Array?) = runBlocking {
    GlobalScope.launch {
        run("job1")
    }
    GlobalScope.launch {
        run("job2")
    }
    delay(10 * 1000)
    println("主程序结束...")
}

suspend fun run(name:String) {
    for (i in 1..10) {
        println("子协程 ${name} 执行第${i}次")
        val sleepTime = (random() * 1000).toLong()
        delay(sleepTime)
    }
    println("子协程 ${name} 执行结束")
}
// 运行结果
子协程 job1 执行第1次
子协程 job2 执行第1次
子协程 job1 执行第2次
子协程 job1 执行第3次
子协程 job1 执行第4次
子协程 job2 执行第2次
子协程 job2 执行第3次
子协程 job1 执行第5次
子协程 job1 执行第6次
子协程 job1 执行第7次
子协程 job2 执行第4次
子协程 job2 执行第5次
子协程 job2 执行第6次
子协程 job1 执行第8次
子协程 job1 执行第9次
子协程 job2 执行第7次
子协程 job2 执行第8次
子协程 job1 执行第10次
子协程 job1 执行结束
子协程 job2 执行第9次
子协程 job2 执行第10次
子协程 job2 执行结束
主程序结束...

你可能感兴趣的:(Kotlin协程(2)✔️创建协程)