Kotin协程的基础

协程是什么?

      就是同步方式去编写异步执行的代码。协程是依赖于线程,但是协程挂起的时候不需要阻塞线程。几乎没有任何代价。

协程的创建

      一个线程可以创建多个协程。协程的创建是通过CoroutineScope创建,协程的启动方式有三种。

  1. runBlocking: T 启动一个新的协程并阻塞调用它的线程,直到协程里面的代码执行完毕,返回值是范型T,就是协程体中最后一行返回的是什么类型,那么T就是什么类型。
  2. launch: job 启动一个协程但不会阻塞调用它的线程,必须在协程作用域(CoroutineScope)中才能调用,返回的是一个job。
  3. async: Deferred<T>启动一个协程但不会阻塞调用它的线程,必须在协程作用域(CoroutineScope)中才能调用。以Deferred对象的形式返回协程任务。返回值范型T同runBlocking类似都是协程体的最后一行。

上述内容提到了Job、Deferred,它们是什么东西呢?

job

       job可以认为就是一个协程作业是通过CoroutineScope.launch生成,同时它运行一个指定的代码块,并在该代码块完成时完成。可以通过isActive、isCompleted、isCancelled来获取Job当前状态。

截屏2023-07-26 22.59.32.png

Deferred

Deferred继承job,可以把它看成一个带有返回值的job

public interface Deferred<out T>: job {
    // 返回结果值,或者如果延迟被取消,则抛出响应的异常
    public suspend fun await(): T
    public val onAwait: SelectClause1<T>
    public fun getCompleted(): T
    public fun getCompletionExceptionOrNull():Trowable?
}

作用域

        协程作用域(CoroutineScope)是协程运行的作用范围。launch、async都是CoroutineScope的扩展函数。CoroutineScope定义了新启动的协程作用域范围,同时会继承了它的CoroutineContext自动传播其所有的elements和取消操作。也就是说,如果这个作用域销毁了,那么里面的协程也就失效了

协程的基础用法
private fun start(){
    val runBlockingJob = runBlocking {
        Log.d("runBlocking", "启动一个协程")
        41
    }
    Log.d("runBlockingJob", "$runBlockingJob")
    val launchJob = GlobalScope.launch{
        Log.d("launch", "启动一个协程")
    }
    Log.d("launchJob", "$launchJob")
    val asyncJob = GlobalScope.async{
        Log.d("async", "启动一个协程")
        "我是返回值"
    }
    Log.d("asyncJob", "$asyncJob")
}

输出为:

D/runBlocking: 启动一个协程
D/runBlockingJob: 41
D/launchJob: StandaloneCoroutine{Active}@3b8b871
D/launch: 启动一个协程
D/async: 启动一个协程
D/asyncJob: DeferredCoroutine{Active}@63f2656

或者

D/runBlocking: 启动一个协程
D/runBlockingJob: 41
D/launchJob: StandaloneCoroutine{Active}@1344515
D/asyncJob: DeferredCoroutine{Active}@38c002a
D/async: 启动一个协程
D/launch: 启动一个协程

或者

D/runBlocking: 启动一个协程
D/runBlockingJob: 41
D/launch: 启动一个协程
D/launchJob: StandaloneCoroutine{Active}@b94e973
D/async: 启动一个协程
D/ asyncJob: DeferredCoroutine{Active}@f7aa030

        由于runBlocking启动的是一个新的协程并阻塞调用它的线程。因此可以看到runBlocking的相关日志输出的位置是不会变化的。这就是说runBlocking会阻塞调用它的线程,直到runBlocking运行结束才能继续执行下去。

        我们看到后面四条日志是无序的,但是launchJob始终在asyncJob前面。而launch和async协程体内的日志输出是无序的。每执行一次看到的顺序都有可能跟之前的不一样。我们前面提到过launch和async都是启动一个协程但不会阻塞调用线程,所以launchJob始终在asyncJob前面。

runBlocking的返回值

        由上述的日志可以看到runBlockingJob的输出结果为41,为什么是41呢?其他的默认返回值一个协程作业的当前状态。

截屏2023-07-29 17.45.22.png

最后的返回值是调用了coroutine.joinBlocking()方法。

截屏2023-07-29 17.46.28.png

coroutine.joinBlocking()方法将state 强转为了范型T。

runBlocking它的设计目的就是将常规的阻塞代码连接到一起,主要用于main函数和测试中。

launch函数

上述日志看到launchJob输出的是一个standaLoneCoroutine对象,不是说输出是一个job吗?

截屏2023-07-29 17.54.55.png

 可以看到launch方法最终返回的是coroutine对象,最后返回的是standaLoneCoroutine对象,其实standaLoneCoroutine对象就是一个job。

截屏2023-07-29 18.00.09.png

 

async 函数

asyncJob返回的是DeferredCoroutine对象

截屏2023-07-29 18.03.33.png

asyncJob返回 DeferredCoroutine不仅继承AbstractCoroutine<T>,同样也继承Deferred<T>接口。那么DeferredCoroutine就是一个Deferred<T>,一个携带返回值的job。 

截屏2023-07-29 18.04.54.png

 我们一开始需要关注的Deferredawait()方法,可以通过返回的Deferred对象,调用await()方法来获取返回值。

public suspend fun awiat(): T

挂起函数

suspend是协程的关键字,表示是一个挂起函数,每一个被suspend修饰的方法只能在suspend方法或者协程中调用。

private fun start(){
    GlobalScope.launch{
        val launchJob = launch{
            Log.d("launch", "启动一个协程")
        }
        Log.d("launchJob", "$launchJob")
        val asyncJob = async{
            Log.d("async", "启动一个协程")
            "我是async返回值"
        }
        Log.d("asyncJob.await", ":${asyncJob.await()}")
        Log.d("asyncJob", "$asyncJob")
    }
}

输出:

D/launchJob: StandaloneCoroutine{Active}@f3d8da3
D/launch: 启动一个协程
D/async: 启动一个协程
D/asyncJob.await:我是async返回值
D/asyncJob: DeferredCoroutine{Completed}@d6f28a0

或者

D/launchJob: StandaloneCoroutine{Active}@f3d8da3
D/async: 启动一个协程
D/launch: 启动一个协程
D/asyncJob.await: :我是async返回值
D/asyncJob: DeferredCoroutine{Completed}@d6f28a0

可以看到asyncJob.await()输出的是我们之前定义好的返回值,同时DeferredCoroutine的状态变成了{Completed}。这是因为await()是在不阻塞线程的情况下等待该值完成并继续执行,当Deferred计算完成后返回结果值,或者deferred被取消,则抛出响应的异常CancellationException。但是又因为await()是挂起函数。他会挂起调用它的协程。因此我们看到Deferred的状态是{Completed}。同时输出的await()日志也在后面。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值