Kotlin协程笔记

引入携程所需库

第一步:项目级build.gradle中

buildscript {
 ext.kotlin_coroutines = '1.3.1'
}

第二步:Module级别build.gradle中

dependencies {
 //                                        依赖协程核心库
 implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines"
 //                                        依赖当前平台所对应的平台库,如Android或js
 implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines"
 //协程的声明周期库,该库可以选加,是让协程的生命周期和Activity的周期保持一致
 implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.0-alpha02'
}

注:核心库和平台库版本需要保持一致

协程是什么

  • 协程:是一个线程框架,最核心的部分是【非阻塞式】和【挂起】;就是launch{协程部分}async{协程部分}

相关名词

1、GloabScope:全局范围
2、Coroutine:协程
3、suspend:挂起、暂停

  • a、代码执行到用suspend标识的函数时会挂起,且这种挂起是不会阻塞的,不会影响当前线程的执行,而这种所谓的挂起,其实就是切一个线程,且在函数执行完毕会自动将线程切回来的调度工作。
  • b、用suspend标识的函数只能运行在协程中;或者被另一个被suspend标识的函数调用,其实最终还是运行在一个协程中。
  • c、suspend本身是不能真正实现挂起的,他的作用主要是一个提醒,是创建者对使用者的提醒,该函数是一个耗时函数,且是用挂起的方式在后台运行,所以请在协程中调用我
  • d、suspend的意义:传递CoroutineContext

相关方法介绍

runBlocking :一般用于 单元测试中,是线程阻塞的

launch(Dispatchers.IO){}:可以用来切线程

coroutineScope.launch(Dispatchers.IO) {
    ...
    launch(Dispatchers.Main){
        ...
        launch(Dispatchers.IO) {
            ...
            launch(Dispatchers.Main) {
                ...
            }
        }
    }
}

withContext(){}:切换线程,当需要频繁的进行线程切换时,因为可以自动切回原来的线程,所以相应的代码嵌套层次没那么多,并在闭包内的逻辑执行结束后自动切回原来的线程,继续执行

coroutineScope.launch(Dispatchers.Main) {
    ...
    withContext(Dispatchers.IO) {
        ...
    }
    ...
    withContext(Dispatchers.IO) {
        ...
    }
    ...
}

Async{}:返回的协程实现了Deferred。可以使用wait()方法

创建携程的方法

方法一:使用runBlocking 顶层函数,适用于单元测试的情况,是线程阻塞的,一般正常的业务开发不会使用

runBlocking{个人理解:此次相当于开启一个线程,但是会阻塞当前线程,相当于一个耗时任务???
  getImage(imageId)
}

方法二:使用GlobalScope 单例对象,直接调用launch开启协程,不会阻塞线程,但是实际开发中也一般不会使用,因为它的生命周期和APP的生命周期是一致的

GlobalScope.launch{
  getImage(imageId)
}

方法三:通过CoroutineContext创建一个CoroutineScope(接口)对象,需要一个类型为CoroutineContext的参数。开发中推荐这种方法,--coroutineContext?????

val coroutineScope = CoroutineScope(coroutineContext)
coroutineScope.launch{
  getImage(imageId)
}

方法四:主要用于在一个协程中开启另一个协程,直接在普通代码中无法使用

launch{
  
}

方法五:该方法与launch类似,也是在协程中使用

async{
  
}

async 和 launch对比

相同点:都可以用来启动一个协程,返回的都是Coroutine

不同点:

  • async返回的Coroutine还实现了Deferred(意思是延迟,也就是结果稍后才能拿到。)接口。可以使用await暂停函数来返回result

  • launch可启动新协程而不将结果返回给调用方

async使用场景:如现在需要展示公司LOGO和个人头像,但是需要等待两者都返回了,才可以去显示,两者同时进行,然后等较慢的执行完毕就可以显示了,所花费的时间就是两个中较长的一个,而不是两者之和

coroutineScope.launch(Dispatchers.Main) {
    //                        async 函数启动新的协程
    val avatar: Deferred = async { api.getAvatar(user) }    // 获取用户头像
    val logo: Deferred = async { api.getCompanyLogo(user) } // 获取用户所在公司的 logo
    //                       获取返回值
    show(avatar.await(), logo.await())                     // 更新 UI
}

挂起的本质

什么是挂起:挂起就是稍后会自动切回来的操作

在协程中,我们挂起的对象既不是线程,也不是函数,而是我们挂起的对象是协程,就是launch和async函数中的闭包的代码块,而launch和async或者其他函数创建的协程在执行到某一个suspend函数的时候,这个函数就会被挂起,或者说从当前线程中脱离。

自定义suspend函数

  • 什么时候需要自定义suspend函数

    比如某个函数时比较函数的,可以将他协程suspend函数,这就是原则
    耗时操作分为:I/O操作和CPU计算工作,比如文件读写,网络交互,图片的模糊处理。
    另一种耗时操作:如事情本身并不慢,但是因为某些原因需要等5s或者特定时间再执行,这种也可以定义为suspend
    
  • 具体如何实现自定义

    1、给函数加上suspend关键字
    2、用withContext将函数内容包住(方便执行完毕,自动切回原始线程)
    3、针对需要等待的耗时操作,我们可以直接使用另一个挂起函数delay()
    

你可能感兴趣的:(Kotlin协程笔记)