美文网首页
Kotlin协程笔记

Kotlin协程笔记

作者: 没有故事的男童鞋 | 来源:发表于2020-05-15 10:02 被阅读0次

    引入携程所需库

    第一步:项目级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协程笔记

          本文链接:https://www.haomeiwen.com/subject/uzvqohtx.html