Kotlin协程 ----- 基本概念

2021-06-07  本文已影响0人  dashingqi
Android_Banner.jpg

Kotlin中的协程

在说协程之前,简单介绍下Kotlin

Kotlin

Kotlin是2011年起 由JetBrain公司开始开发;

到2017年GoogleIO大会上宣布Kotlin将称成为Android应用开发的第一语言;

更是在2019年IO大会上,宣布”Kotlin First“的口号,在Android应用开发中,Kotlin从此开始它的辉煌时刻了!

Kotlin中协程的定义

协程是在Kotlin1.3版本引入的;

Kotlin中的协程就是一套由Kotlin官方提供的线程框架API,就像Java的Executor和Android中的AsyncTask系列的API;

在Android的官方文档中也有提到:协程是我们在Android上进行异步编程的推荐解决方案;

那么既然有了Executor以及AsyncTask系列的API,为什么Android官方说它是异步编程的推荐解决方案呢?

Kotlin中协程好在哪里
开始之前-闭包

在之前先说下【闭包】这个概念,因为在协程的使用过程中会涉及到这个概念;

我们在使用Kotlin的API的时候也会经常用到闭包;

【闭包】这个东西不是Kotlin中特有的特性,是从Java8开始就出现了;

我们就以Thread为例子讲述一下

// 创建一个Thread,内部传递一个Runnable的匿名内部类对象
// 在Java8之后,我们将只有单一方法的接口称为SAM(Single Abstract Method)接口
Thread(object:Runnable{
  override fun run(){
    // ....doWork....
  }
})
// 同样在Java8之后我们可以使用lambda简化 SAM接口的写法
Thread({
  // ....doWork....
})
// 在Kotlin中有这样一个语法糖:当函数最后一个参数Lambda表达式时,可以将Lambda写在括号外面,这就是Kotlin的闭包原则
Thread{
  // .... doWork ....
}
基本的使用
// 方法一:这种方式开启的协程是线程阻塞的,不能在实际业务中使用;
runBlocking{
  
}
// 方法二:使用GlobalScope,直接调用launch开启协程
// 该方式开启的协程是不会阻塞线程的。但是它的生命周期是和app一致的。
GlobalScope.launch{
  
}
// 方法三:通过CoroutineScope创建一个协程对象
// 其中context是为CoroutineContext类型的参数
// 通常比较推荐使用这种方法,可以通过传递的context参数去管理和控制协程的生命周期;
// 这里的context可以理解为协程的上下文
// launch里面包裹的代码就是我们要开启的协程
val coroutineScope = CoroutineScope(context)
cortineScope.launch{
  // .... doWork ....
}
好在哪里

在给出结论之前,我们先看下面这两个场景

场景一:我们从Server拿到一个学生信息:

private fun getData(){
    getStudentInfo(object :InfoCallback{
        override fun onSuccess(student:Student) { 
          // 获取到学生信息
        }
        override fun onFailure() {      
        }
    })
}

怎么样,产品是要迭代,需求是要变动的,这时出现了下面这个场景二

场景二:拿到学生信息之后,需要查询她/他是在哪个班级?

private fun getData(){
    getStudentInfo(object :InfoCallback{
        override fun onSuccess(student:Student) {
                        //根据学生信息去查询班级信息
            getClassInfo(student:Student,object :InfoCallback{
                override fun onSuccess(class:Class) {
                    //获取到班级信息
                    }
                override fun onFailure() {
                    }
                })
        }
        override fun onFailure() {  
        }
    })
}

那么我们来看下使用协程代码是怎么写的

private fun getData(){
  // 创建一个CoroutineScope对象
  val coroutineScope = CoroutineScope(Dispatchers.Main)
  // 调用launch方法 开启一个协程,其中Dispatchers.Mian就制定我们的协程是跑在主线程中的
  cortineScope.launch(Dispatchers.Main){
    // async 是开启了一个新的协程,它返回的是Coroutine是实现了Deferred接口的
    val student = async { getStudent() }
    val class   = async { getClass(student.await()) }
    // 拿到班级信息,通过调用deferred.await()可以获取到方法的返回值
    val class1  = class.await()
    tv.text = class1.name
  }
}

/**
 * 获取学生信息
 */
private suspend fun getStudent(): Student {
    withContext(Dispatchers.IO){
      delay(100)
      return "zhangqi"
    } 
}

/**
 *获取班级信息
 */
private suspend fun getClass(): Class {
    withContext(Dispatchers.IO){
      delay(100)
      return "一班" 
    }
}

怎么样 是不是 没有对比就没有伤害!!!

我们可能使用回调这种方式使用习惯了,没觉得有什么不妥的,但是和Kotlin协程实现方式相比

这种回调嵌套回调,是不是有点代码可读性差,维护性也不好;

所以协程好在哪里:

使我们的代码可读性和可维护性变强了;

最重要的是:用同步代码的方式写出异步的代码,这也是Kotlin协程相比较于之前多线程框架的核心竞争力;

那么协程凭啥就能这么写呢,我们看上面的代码

  1. 使用了 async{} 和await
  2. 我们在我们定义的函数中使用了suspend关键字 ,被suspend修饰方法的我们称之为挂起函数

接着我们介绍下挂起函数

挂起函数

所谓的挂起就是能自动切线程并且能自动切回来的动作

自定义挂起函数 要使用 suspend 关键字

// 挂起函数只能由协程或者其他挂起函数进行调度
suspend fun method(){
  withContext(Dispatchers.IO){
    
  }
}

当我们使用launch或者async启动协程后,在协程中调用到挂起函数的时候,会被【suspend】也即就是挂起

当我们的挂起函数执行完毕之后,协程会帮助我们把线程切回来

就是当我们的协程运行在主线程上时,当调用到suspend函数,根据Dispatchers(调度器)制定线程的不同,就可能会发生线程切换,当我们的代码执行完毕后,协程会自动切回到原线程上;

【切回】这个动作在Kotlin中叫做 resume 也就是恢复,这个是协程的特性;

所以挂起函数要么在其他挂起函数中调用,要么在协程中调用;为啥呢?因为它要切过去还要切回来呢!

其实suspend关键字并不是起到挂起的作用,它单纯只是一个【提醒】的作用,告诉调用者我这个函数将会一个耗时的函数,请在协程中调用我;

什么时候需要挂起函数

执行耗时的任务

怎么写
suspend fun method(){
  // withContext 也是一个挂起函数 内部是由suspend关键字修饰的
    withContext(Dispatchers.IO){
    //在IO线程中,执行耗时的任务
  } 
}

在Kotlin中这个挂起是非阻塞式的?

非阻塞挂起

针对非阻塞挂起 我们可以拆分开 【非阻塞】【挂起】

【非阻塞】

非阻塞是相对于阻塞而言的;

对于线程的阻塞可以拿我们生活中一些事来说:

你开车在路上,不巧车坏了,你停在这里堵主后面的司机师傅了(你在执行耗时任务),

等到车修好了,可以出发了(耗时任务结束了);

或者你可以把车换到其他没有车路上,等车被修好(开启了其他线程,执行你这个任务)

其实非阻塞是针对挂起这个动作特点的一个描述;本身挂起就是能自动切线程并切回来的操作,就是不阻塞UI线程的;相比较于之前的线程框架 这个能自动切换回来;

在我们代码中的体现就是 我们看似以同步的方式在写代码,但是依靠协程却完成了异步任务;

总结一波

协程就是切线程;

挂起就是能自动切回来的切线程

挂起的非阻塞就是看似以阻塞的方式完成了非阻塞的任务

上一篇 下一篇

猜你喜欢

热点阅读