协程使用指南

协程使用指南

协程介绍

协程是一种并发设计模式,是一套基于线程来实现的API,可以看作是轻量级线程。 协程的一个好处是,当涉及到开发人员时,编写非阻塞代码与编写阻塞代码基本相同。编程模型本身并没有真正改变。

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1'
// 可选 Android中使用协程
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1'

协程的用法

创建一个协程,并执行

import kotlinx.coroutines.*

fun main() {
    // 在后台启动一个新的协程并执行
    GlobalScope.launch { 
        // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
        delay(1000L) 
        // 在延迟后打印输出
        println("World!") 

    }
    // 协程已在等待时主线程还在继续
    println("Hello,") 
    // 阻塞主线程 2 秒钟来保证 JVM 存活
    Thread.sleep(2000L) 
}

代码运行的结果:

Hello,
World!

我们使用GlobalScope.launch创建一个协程,并执行它,后面大括号里包着的是协程要执行的内容,类似于线程中的run方法体。

  • delay方法只能在协程内部使用,它用于挂起协程,但是不会阻塞当前协程所在的线程。
  • Thread.sleep会阻塞当前线程,也就是说当前线程被阻塞在那暂时不能做别的事情了

关于协程的方法只能在协程体里面执行,所以为了方便测试协程代码,我准备使用runBlocking把main方法包起来。

/*
 *显式指定了其返回类型 Unit,
 * 因为在 Kotlin 中 main 函数必须返回 Unit 类型。
 */
fun main()= runBlocking<Unit> {
    // 打印当前线程名称
    println(Thread.currentThread().name)
}

执行结果:

main

runBlocking方法运行一个新的协程并且阻塞当前线程,上面的代码中包裹的是main方法,所以阻塞的是main线程

协程挂起

我们用suspend修饰符来标记函数 挂起函数只被允许在协程或另一个挂起函数中调用

我们先看一个先获取token,再登录的例子,里面用到挂起函数

import kotlinx.coroutines.*
import kotlin.system.measureTimeMillis

/*
 * main函数
 */
fun main()= runBlocking<Unit> {
    var time = measureTimeMillis{
        val token = getToken()
        val loginInfo = login(token)
        println(loginInfo)
    }
    println("Completed in $time ms")
}

/**
 * 模拟接口获取token
 */
suspend fun getToken():String{
    delay(1000L)
    return "woshitoken"
}

/**
 * 模拟接口登录
 */
suspend fun login(token:String):String {
    delay(1000L)
    return "my token is: $token , login success"
}

执行结果:

my token is: woshitoken , login success
Completed in 2012 ms
  • 我对挂起的理解:

    在上面的代码中有两个挂起函数getToken和login,当程序逻辑走到挂起函数getToken()那里,当前协程被挂起,getToken()被抛出去执行,你可以指定在哪一个线程池中执行,当getToken在外部执行完后再恢复刚才挂起的协程,协程代码继续执行。

async 并发

在概念上,async 就类似于 launch。它启动了一个单独的协程,这是一个轻量级的线程并与其它所有的协程一起并发的工作。不同之处在于 launch 返回一个 Job 并且不附带任何结果值,而 async 返回一个 Deferred —— 一个轻量级的非阻塞 future, 这代表了一个将会在稍后提供结果的 promise。你可以使用 .await() 在一个延期的值上得到它的最终结果, 但是 Deferred 也是一个 Job,所以如果需要的话,你可以取消它。转自 kotlincn.net

import kotlinx.coroutines.*
import kotlin.system.*

fun main() = runBlocking<Unit> {
    val time = measureTimeMillis {
        val one = async { doSomethingUsefulOne() }
        val two = async { doSomethingUsefulTwo() }
        println("The answer is ${one.await() + two.await()}")
    }
    println("Completed in $time ms")    
}

suspend fun doSomethingUsefulOne(): Int {
    // 假设我们在这里做了些有用的事
    delay(1000L) 
    return 13
}

suspend fun doSomethingUsefulTwo(): Int {
    // 假设我们在这里也做了些有用的事
    delay(1000L) 
    return 29
}

运行结果:

The answer is 42
Completed in 1055 ms

两个协程是并发执行。 惰性启动的-async

Flow

参考链接

官方协程指南 kaixue 如何正确的在 Android 上使用协程 ? 协程博客

links

social