Kotlin协程-从理论到实战

这篇具有很好参考价值的文章主要介绍了Kotlin协程-从理论到实战。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

上一篇文章从理论上对Kotlin协程进行了部分说明,本文将在上一篇的基础上,从实战出发,继续协程之旅。

从源头说起

在Kotlin中,要想使用协程,首先需要使用协程创建器创建,但还有个前提——协程作用域(CoroutineScope)。在早期的Kotlin实现中,协程创建器是一等函数,也就是说我们随时随地可以通过协程创建器创建协程。但在协程正式发布以后,协程创建器需要在协程作用域对象上才能创建了,Kotlin添加了协程作用域来实现结构化并发。什么是结构化并发呢,通俗地说就是正确实施多个协程监控、管理的能力。在实际业务中,我们可能需要创建多个协程对象来完成不同的工作。为了对这些不相关的协程管理起来,Kotlin引入了协程作用域,通过某个协程作用域创建的协程都会被它管理着,在条件满足的时候,执行每个协程的取消工作或者结束自己。

为了方便我们直接上手,官方提供了MainScopeGlobalScope供我们使用。正如名字那样,他们分别有不同的应用场景,前者比较适合用在UI相关的类中,而后者适用于在整个应用生命周期中都需要存活的类中。当然,对于Android开发者,其实我们有更好的选择——使用ViewModel的Kotlin扩展,它不仅有着全部的协程作用域功能,开箱即用,而且还在onCleared方法中实现了自动取消。

创建协程

有了协程作用域,那我们来创建一个最简单的协程吧。

viewModelScope.launch{
    //这里就是协程代码啦啦啦啦
    delay(2000)
    System.out.println("Hello World")
}

launch创建并启动了一个协程,协程启动两秒后,在控制抬打印了Hello World,然后协程就结束了(协程是有完整生命周期的)。这个协程完成的工作有限,我们可以使用线程完成相同的功能:

thread {
            Thread.sleep(2000)
            System.out.println("Hello World")
        }

我们可以看到,除去构建函数,两段代码唯一的区别就是延迟函数——delayThread.sleep.从功能上看他们都是让后面的代码延迟执行,但是效果却是不一样的,前者不会阻塞线程。这段代码其实是放在主线程里面执行的,但是它不会影响到UI的绘制,而后者假如把它放在主线程执行的话,应用会出现两秒的无响应。Kotlin把这种不会阻塞当前线程执行的函数称之为挂起函数,挂起函数可以在挂起点断开与当前线程的联系,让线程空闲下来完成其他的操作,当条件满足后,挂起函数重新在挂起点恢复,接着往下执行后面的代码。

还有个小问题没有解决,在上一篇文章中,我曾经说过,挂起函数只能在挂起函数中执行,既然delay是挂起函数,那么反推,我们的代码块也应该是个挂起函数,而这个挂起函数就是所谓的协程体。

让协程跨线程工作

如果你看到上面的代码,然后转手在协程体里面写个网络请求的话,你会发现,你的应用崩溃了,这是怎么回事呢?因为协程虽然不会阻塞主线程,但是主线程是不允许进行网络请求的。如果这时你就急着下了协程没啥用的结论,那么你就肤浅了。让我们稍微改一改上面的代码,让它运行在子线程吧。

viewModelScope.launch (Dispatchers.IO){
    //这里就是协程代码啦啦啦啦
    delay(2000)
    System.out.println("Hello World")
}

很好,现在协程体里面的网络请求可以顺利执行了,但是很快有读者就会发现新问题了——我怎么把网络请求的结果传回主线程呢,难不成还搞个Handler,那和直接使用线程有什么区别,辣鸡协程。嘿,别急,这个协程其实也为客官处理好了。让我们再次改造一下代码:

viewModelScope.launch (Dispatchers.IO){
    //这里就是协程代码啦啦啦啦,这里是在子线程执行的代码哦
    //假装这个是网络请求吧
    delay(2000)
    withContext(Dispatchers.Main) {
        //哦豁豁,这里竟然运行在主线程哦
        System.out.println("Hello World")
    }
}

很好,我们可以愉快地使用协程处理网络请求了,那么这些魔法是怎么发生的呢,停下脚步,我们来重新审视一下上面的代码。

首先,相比于最开始的代码,我们的代码里多了两个对象,一个方法调用。首先我们来看那两个对象,从名字中我们不难猜到它就是调度线程的。
Kotlin提供了四个常用的实现

  • Default,它是标准协程构建者默认使用的调度器,使用共享的线程池工作,适用于计算型的任务;

  • Main,它是代表UI线程的调度器,通常来说只有一个线程,使用这个调度器就可以直接在协程中操作UI;

  • Unconfined,它没有限定线程范围,它在哪个线程中被调用就会在哪个线程里执行完初始的代码,直到遇到挂起函数,随后它会使用挂起函数指定的调度器恢复,这个过程可以一直持续下去。

  • IO,是用来承载阻塞的IO操作的,如文件读写,网络连接等,是我们比较常用的调度器。

所以那两个调度器对象是让协程切换工作环境的魔法。接下来还有一个方法调用没有解释。withContext的作用是将当前的协程调度器切换到指定的调度器上,用这个调度器接着执行构建块中的代码。同时它也是一个挂起函数。提到挂起函数,我们就该想到,它是可恢复的。所以当这个挂起函数的代码块执行完成之后,它会自动恢复成原来的调度器,接着往下执行。

用协程串联两个异步操作

在项目开发中,还有一种常见的应用场景,客户端需要先请求一些配置信息,然后利用配置信息再请求真正的内容信息。这个过程描述起来是串行的,但是代码写起来却是割裂的,需要在第一个网络请求的回调中处理和发起第二个请求,然后在第二个回调中获取真正需要展示的数据,可能这个过程还会加个存库,或者触发另外请求的工作,那么完了,这代码没法看了。这放在以前,这种情况通常会使用RxJava,但是RxJava的代码可读性也还是差点意思。那么Kotlin协程可以写成什么样呢?

viewModelScope.launch(Dispatchers.IO) {
            val retrofit=Retrofit.Builder().build()
            val apiUser=retrofit.create(APIUser::class.java)
            val user=api.current()
            val detail=api.userDetail(user.id)
            withContext(Dispatchers.Main) {
                userLiveData.value=detail
            }
        }

这和我们写一般的同步代码一摸一样,没有回调,也不需要付出其他代价,这个过程甚至可以一直加下去。其实我觉得这个才是协程的真正威力。

让多个协程一起工作

我们继续复杂化使用场景——我在做一个多端使用的笔记App,现在用户打开了某一个已存在的笔记,为了让用户能快速浏览到上一次的操作信息,一方面我需要从文件中读取上一次操作的结果,另一方面我要拉取远程的操作结果,然后对两个结果合并,决定最终的展示数据。考虑到这两个操作其实是并行的,上面我们让协程串联起来的思路已经不适用了,因为协程里面的操作都是串行的。既然一个协程解决不了,我们再加一个协程可不可以呢?看着好像是可以,但是,协程操作的结果我们怎么获取到呢?查阅API,我找到了另一个协程构建器async。它会返回一个协程对象,然后通过await方法获取到协程的计算结果。思路来了,我们马上动手

 val fileResult=viewModelScope.async(Dispatchers.IO) {
             //假装是读文件的代码吧
             1
         }
 val networkResult=viewModelScope.async(Dispatchers.IO) {
     //也是假装是网络请求的代码
     2
 }
 val fResult=fileResult.await()
val rResult=networkResult.await()
val result=if(fResult>rResult){
    fResult
}else{
    networkResult
}

然后你就会发现报错了,await是挂起函数。看来两个协程还完成不了,要三个,所以,让我们创建第三个协程吧

 //前面的两个协程不变
 viewModelScope.launch {
     val fResult=fileResult.await()
     val rResult=networkResult.await()
     val result=if(fResult>rResult){
         fResult
     }else{
         networkResult
     }
}

这就是协程间通信的基本写法啦,从这个基础之上,甚至还能衍生出更复杂的版本,但是万变不离其宗,都可以参考这种思路完成。

协程的取消

正如之前提到的一样,协程有着类似于线程的完整生命周期,包括创建,激活,完成中(取消中),已完成(已取消),刚才我们的示例都是正常状态,协程完成工作后会自动结束,但协程的另一条取消流程我们还没有提到。协程有自己的取消API——cancel可供使用,我们只需要保存好协程创建者返回的协程对象就行了。当然更常见的还是文章开篇提到的使用协程作用域取消。这个操作会取消所有的协程。

总结

本篇文章从协程创建开始,讲到了怎样用协程写出异步代码,怎么让多个协程共同工作,虽然覆盖了很大一部分使用场景,但是依然还有遗漏。由于篇幅限制,遗漏部分将在下一篇博文中继续讲解,希望大家持续关注。

青山不改,绿水长流,咱们下期见!文章来源地址https://www.toymoban.com/news/detail-486747.html

到了这里,关于Kotlin协程-从理论到实战的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • kotlin语法进阶 - 协程(一)协程基础

    协程并不是一个新的概念,而是一个非常老的概念,很多语言都支持协程,建议去浏览器去了解一下协程的历史和基本概念,这里我们只讲一下kotlin中的协程的作用。 从代码实现角度来看:kotlin协程底层是用线程实现的,是一个封装完善供开发者使用的线程框架。kotlin的一个

    2024年02月09日
    浏览(35)
  • Kotlin协程-从一到多

    上一篇文章,我介绍了Kotlin协程的创建,使用,协作等内容。本篇将引入更多的使用场景,继续带你走进协程世界。 常用编程语言都会内置对同一类型不同对象的数据集表示,我们通常称之为容器类。不同的容器类适用于不同的使用场景。Kotlin的 Flow 就是在异步计算的需求下

    2024年02月09日
    浏览(45)
  • Kotlin协程学习之-02

    协程的基本使用 GlobalScope.launch 生命周期与进程一致,且无法取消 runBlocking 会阻塞线程,一般在测试阶段可以使用 val coroutineScope = CoroutineScope(context) coroutineScope.launch 通过context参数去管理和控制协程的生命周期 用法 val coroutineScope = CoroutineScope(context) coroutineScope.launch(Dispatche

    2024年01月22日
    浏览(34)
  • Kotlin 协程 - 多路复用 select()

            又叫选择表达式,是一个挂起函数,可以同时等待多个挂起结果,只取用最快恢复的那个值(即多种方式获取数据,哪个更快返回结果就用哪个)。         同时到达 select() 会优先选择先写子表达式,想随机(公平)的话使用 selectUnbiased() 替换 。         能

    2024年02月10日
    浏览(37)
  • Android Kotlin 协程初探

    维基百科:协程,英文Coroutine [kəru’tin] (可入厅),是计算机程序的一类组件,推广了协作式多任务的子程序,允许执行被挂起与被恢复。 作为Google钦定的Android开发首选语言Kotlin,协程并不是 Kotlin 提出来的新概念,目前有协程概念的编程语言有Lua语言、Python语言、Go语言

    2024年02月08日
    浏览(32)
  • Kotlin 协程基础使用学习

    原文: Kotlin 协程基础使用学习-Stars-One的杂货小窝 本篇阅读可能需要以下知识,否则可能阅读会有些困难 客户端开发基础(Android开发或JavaFx开发) Java多线程基础 kotlin基础 本文尽量以使用为主,以代码为辅讲解,不提及过深协程底层代码逻辑,仅做一个基础入门来快速上手学习(断断

    2024年03月18日
    浏览(53)
  • 协程 VS 线程,Kotlin技术精讲

    协程(coroutines)是一种并发设计模式,您可以在Android 平台上使用它来简化异步执行的代码。协程是在版本 1.3 中添加到 Kotlin 的,它基于来自其他语言的既定概念。 在 Android 上,协程有助于管理长时间运行的任务,如果管理不当,这些任务可能会阻塞主线程并导致应用无响应。

    2024年02月09日
    浏览(29)
  • 【Kotlin】协程的字节码原理

    前言 协程是Koltin语言最重要的特性之一,也是最难理解的特性。网上关于kotlin协程的描述也是五花八门,有人说它是轻量级线程,有人说它是无阻塞式挂起,有人说它是一个异步框架等等,众说纷芸。甚至还有人出了书籍专门介绍kotlin协程。 笔者刚开始接触这个概念也是一

    2024年01月18日
    浏览(25)
  • Kotlin 协程 supervisorScope {} 运行崩溃解决

    简单介绍 supervisorScope 函数,它用于创建一个使用了 SupervisorJob 的 coroutineScope, 该作用域的特点:抛出的异常,不会 连锁取消 同级协程和父协程。 看过很多  supervisorScope {}   文档的使用,我照抄一摸一样的代码,运行就崩溃,最后找到了解决方法,应该是kotlin版本更新做过

    2024年01月25日
    浏览(29)
  • kotlin协程async与await

    kotlin协程async与await 输出: 3 3072 https://zhangphil.blog.csdn.net/article/details/129265638 https://zhangphil.blog.csdn.net/article/details/129265638 kotlin协程、线程切换,函数方法委托_zhangphil的博客-CSDN博客 runBlocking 内部启动的3个协程做耗时操作,从输出可以看到3个协程交叉并发执行,runBlocking 会等

    2024年02月05日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包