“吹Kotlin协程的,可能吹错了!”带你真正理解一波

很简单,我们做一个小实验就能知道结果:

fun main(){

//在没有开启协程前,先打印一下进程名称和进程id

println(

"Main: " +

"threadName = " + Thread.currentThread().name

  • " threadId = " + Thread.currentThread().id

)

//循环20次

repeat(20) {

GlobalScope.launch {

//开启协程后,先打印一下进程名称和进程id

println(

"IO: " +

"threadName = " + Thread.currentThread().name

  • " threadId = " + Thread.currentThread().id

)

delay(1000L)

}

}

}

日志打印结果:

发现了什么?所谓的协程完全就是开启了一个新的线程来执行任务,有些任务的线程名称和线程id还是完全一致的!这像不像java中的线程池?

看到这里,是不是有点颠覆你原来对协程的认识?难道网上的所有文章都是错误的?

其实网上大部分文章说的协程,指的可能都是其他语言的协程的特点,比如Go、Lua…

而我们要学的,是Kotlin协程,它不是真正意义上的协程,它也没有那么的神秘,本质上还是一套基于原生Java Thread API 的封装。只要你没有魔改JVM,start了几个线程,操作系统就会创建几个线程,Kotlin协程只是做了一个类似线程池的封装,根本谈不上什么性能更好。

总结下来Kotlin协程其实就是为了让我们更方便的来进行多线程开发而已,所以我们就抱着当初学习Handler、AsyncTask这些的心态来学习协程这个工具包就好了,不要想那么多复杂的东西来扰乱自己的思路。

Kotlin协程有那么好用吗

================================================================================

一个新的技术的出现,大家往往从学习到真正在项目中实践往往需要一个过程,这里面有非常多的因素,有个人学习成本的因素,有公司方面的因素等等,但是最重要的,其实还是这个新的技术,到底是不是真的有取代我们现有技术的必要。接下来我就带大家一起来用用协程吧。

okhttp异步请求

这是我们常规的一个异步请求,通过回调的方式来处理请求结果

fun enqueue() { ApiService.enqueue(“/test”, object : Callback {

override fun onResponse(call: Call, response: Response) {

runOnUiThread {

//切到主线程更新UI

tv_content.text = response.body.toString()

}

}

override fun onFailure(call: Call, e: IOException) {

}

})

}

使用协程的okhttp同步请求

这里先记住一句话,我们什么时候要用到协程的?

需要切换线程的时候要用到协程

所以想切到什么线程,就用GlobalScope.launch(Dispatchers.XX)切一下就好了,代码如下

fun execute() {

GlobalScope.launch(Dispatchers.Main) {

//切到子线程执行任务

var result = withContext(Dispatchers.IO) {

ApiService.execute(“/test”)

}

//任务执行完后自动回到主线程

tv_content.text = result

}

}

看到这里,有的人可能觉得有点怪怪的,这看起来完全不足以吸引我使用协程,用回调不好吗?

kotlin毕竟是一门比较新的语言,所以在协程中,它同时给我们提供了一些非常实用的函数,所以上面的代码可以写成下面这样:

fun execute() {

GlobalScope.launch(Dispatchers.Main) {

//切到子线程执行任务

var result = withContext(Dispatchers.IO) {

ApiService.execute(“/test”)

}

//任务执行完后自动回到主线程

tv_content.text = result

}

}

这个withContext函数的意义呢,就是能把耗时任务切到子线程去,然后任务执行完之后,又会自动切回主线程。

我们还可以继续优化一下代码,把数据请求和处理抽取到一个方法中,方便调用。

有人可能看到这里多了一个suspend关键字,这个其实就是告诉编译器这里要执行一个异步代码,调用者需要把我切到协程里,用什么切?

就是这个GlobalScope.launch(Dispatchers.Main) 。

不要想太多,就是一个编译检查而已,如果你不使用 GlobalScope.launch来调用suspend修饰的方法就不能编译通过

fun execute() {

GlobalScope.launch(Dispatchers.Main) {

val token = login()

val user = getUsrInfo(token)

tv_content.text = user.name

}

}

到了这里,我相信很多喜欢回调的朋友们心中依然觉得,这依然不足以让我来使用协程。

我们继续往下看。

利用接口回调处理有上下文关联的任务

这时候有一个需求,我们需要先获取用户的token,再通过token查询用户名称

fun execute() {

GlobalScope.launch(Dispatchers.Main) {

val token = login()

val user = getUsrInfo(token)

tv_content.text = user.name

}

}

使用协程处理有上下文关联的任务

fun execute() {

GlobalScope.launch(Dispatchers.Main) {

val token = login()

val user = getUsrInfo(token)

tv_content.text = user.name

}

}

这时候利用协程的优势就明显了很多。

但是,就是有人深深的爱着回调,而且我知道你心里想的是什么,反正用这种多层回调的场景也不多,应用程序能跑起来不影响性能就好了。

这么说确实也没错,那么我们继续往下看。

前面这种情况,getUserInfo接口是依赖于login接口返回的token的,所以不可避免的使用了回调。

但是现在有一个场景,我们需要将接口A中的接口B中的数据进行合并展示,但是这两个接口在服务端的接口设计上是没有非常强的关联的,这时候出现了两种人。

**第一种:**想了想觉得没啥办法,然后还是按照先调用接口A,成功后再调用接口B,然后在接口B的回调中进行数据合并

**第二种:**觉得第一种方式不合理,所以去找服务端“撕逼”,告诉服务端把这两个接口的数据合并到一个接口中返回,客户端处理不了就找服务端呗。这番“撕逼”下来,你有可能成功了,也有可能失败了,但最终的结果都不是非常好。

我们想象一下,假设接口A耗时100ms,接口B耗时120ms,那么实际上在并发处理的情况下,你最快只需要120ms就可以将两个数据进行合并,但是使用回调的方式需要220ms(100ms+120ms)。

之前讲的都只是代码的美观层面的东西,到这里就是性能问题了,各种小的性能问题不解决,一个app怎么可能有比较好的用户体验呢。

我们来看看通过协程可以怎么做:

fun execute() {

GlobalScope.launch(Dispatchers.Main) {

//使用async发起两个异步请求

val one = async { one() }

val two = async { two() }

//使用await进行合并

val result = one.await() + two.await()

tv_content.text = result

}

}

看到这里,我们再来看协程这个名字,英文名是Coroutine,中文全称叫做“协同程序”,结合我们前面说的内容,你是否对协程有了新的认识呢?

协程就是协同多个程序之间进行合作,帮助我们轻松的写出复杂的并发代码,甚至还能用非常简单的方式实现原本不可能实现的并发任务。这就是我们为什么要学习协程的理由。

到底什么是非阻塞式挂起

=============================================================================

网上很多文章都提到协程的挂起是非阻塞式的,挂起是什么呢?

就是我们前面说的withContext(Dispatchers.IO)挂起函数,当然还有delay、async,说白了只要不影响我们主线程的工作,那就是被挂起了,这里的”挂起“两个字用的非常玄乎,感觉像挂在我们的主线程,其实按照我上面的分析,更贴切的说法是”切到另一个线程“。

再来分析这个非阻塞式,阻塞很简单,就是字面意思,那既然是阻塞,那总得知道阻塞了什么吧?

在我们Android中,其实就是阻塞了主线程的运行,那反过来非阻塞式其实就是没有卡住主线程的运行.

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

在这里小编整理了一份Android大厂常见面试题,和一些Android架构视频解析,都已整理成文档,全部都已打包好了,希望能够对大家有所帮助,在面试中能顺利通过。

image

image

喜欢本文的话,不妨顺手给我点个小赞、评论区留言或者转发支持一下呗

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

在这里小编整理了一份Android大厂常见面试题,和一些Android架构视频解析,都已整理成文档,全部都已打包好了,希望能够对大家有所帮助,在面试中能顺利通过。

[外链图片转存中…(img-A1NaD0h9-1712680486954)]

[外链图片转存中…(img-wYRDyzHc-1712680486954)]

喜欢本文的话,不妨顺手给我点个小赞、评论区留言或者转发支持一下呗

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 7
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值