Kotlin 学习笔记(十四)浅读协程

上一篇-Kotlin 学习笔记(十三)高阶函数

为什么需要协程

  举例一个异步编程中最常见的场景:后台线程执行一个A任务,下一个B任务依赖于A任务的执行结果,所以必须等待上一个任务执行完成后才能开始执行。直接上代码。

// 模拟本地创建 Token
fun requestToken(cb: (Token) -> Unit){
    //todo
}

//组装Http请求
fun createHttpUtils(token: Token, item: Item, cb: (HttpUtils) -> Unit): {
    //todo 
}

//进行网络请求
fun postUrl(httpUtils: HttpUtils) {
    //todo 
}

  如果在代码中调用 会怎么样呢?

fun postItem() {
    requestToken{ token ->
        createHttpUtils(token) { httpUtils->
            postUrl(httpUtils)
        }
    }
}

   可以看到 我们 嵌套了好几层来实现一个网络请求,可读性变差,代码也很容易出现问题。kotlin中的 协程可以帮助我们减少代码嵌套,优化线程/线程池控制。

协程使用

修改示例的方法,引入kotlin 中的协程我们再看一下 代码会发生哪些变化

// 关于suspend  后边总结,挂起函数的重点
suspend fun requestToken(): Token { ... }   // 挂起函数

 // 挂起函数
suspend fun createHttpUtils(token: Token): HttpUtils{ ... } 

fun postUrl(httpUtils: HttpUtils) { ... }

// 代码使用
fun postItem() {
     //  注意:开发过程中不能直接使用GlobalScope.launch
    GlobalScope.launch {
        // 看似三个任务并行处理的,但其实是串联执行的
        val token = requestToken()
        val httpUtils= createHttpUtils(token)
        postUrl(httpUtils)
    }
}

  上述代码,像代码中的GlobalScope.launch不建议在日常开发中使用,此处只是为了简单代码展示。因为 GlobalScope 创建的协程没有父协程,除非执行完成或手动取消,该协程都会继续运行,而实际上在 Android 中协程的运行需要跟 Activity/Fragment 的生命周期绑定。
  看完上边的代码使用,就已经可以在项目中简单使用了

了解协程

项目中引入

            // 协程    https://github.com/Kotlin/kotlinx.coroutines
            kotlin_coroutines      : [group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-android', version: versions.kotlin_coroutines],
            kotlin_coroutines_core : [group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: versions.kotlin_coroutines],

协程概念

协程通过将复杂性放入库来简化异步编程。程序的逻辑可以在协程中顺序地表达,而底层库会为我们解决其异步性。该库可以将用户代码的相关部分包装为回调、订阅相关事件、在不同线程(甚至不同机器)上调度执行,而代码则保持如同顺序执行一样简单。 - 协程官方文档翻译

  协程的开发人员 Roman Elizarov 如下描述协程的:协程就像非常轻量级的线程。线程是由系统调度的,线程切换或线程阻塞的开销都比较大。而协程依赖于线程,但是协程挂起时不需要阻塞线程,几乎是无代价的,协程是由开发者控制的。所以协程也像用户态的线程,非常轻量级,一个线程中可以创建任意个协程。

协程知识点

根据官方网站了解重要的知识点

挂起函数

//有suspend修饰符标记,这表示两个函数都是挂起函数
suspend fun requestToken(): Token { ... } 
suspend fun createHttpUtils(token: Token): HttpUtils{ ... } 

  挂起函数能够以与普通函数相同的方式获取参数和返回值,但是调用函数可能挂起协程,挂起函数挂起协程时,不会阻塞协程所在的线程。挂起函数执行完成后会恢复协程,后面的代码才会继续执行。但是挂起函数只能在协程中或其他挂起函数中调用。

知识点

   要想明白协程的内在,还是需要看源码

GlobalScope.launch

// launch 函数源码
// CoroutineContext: 协程上下文 是一些元素的集合,包括 Job 和 CoroutineDispatcher 等元素。
// CoroutineDispatcher : 协程调度器,决定协程所在的线程或线程池  (类型 :Dispatchers.Default、Dispatchers.IO、Dispatchers.Main和Dispatchers.Unconfined)
// CoroutineScope.launch 默认使用Dispatchers.Default
public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job {
    ...
}

对于源码简单的解释 主要看注释部分。

创建方式

  1. CoroutineScope.launch : CoroutineScope.launch {} 是最常用的 Coroutine builders,不阻塞当前线程,在后台创建一个新协程,也可以指定协程调度器
  2. runBlocking : runBlocking {}是创建一个新的协程同时阻塞当前线程,直到协程结束。(个人很少用....)
  3. ** withContext**: withContext {}不会创建新的协程,在指定协程上运行挂起代码块,并挂起该协程直至代码块运行完成。
  4. **async ** : 类似CoroutineScope.launch,区别是 async 返回的Deferred 类型,Deferred 是 Job的子类,带有返回值,但是 CoroutineScope.launch 返回的 Job类型,所以 CoroutineScope.launch没有返回值。

你可能感兴趣的:(Kotlin 学习笔记(十四)浅读协程)