launch(Dispachers.Main){
//ui操作
launch(Dispachers.IO) {
//io操作
launch(Dispacher.Main) {
//ui操作
}
}
}
}
这个嵌套???不是说协程可以不用写嵌套代码的吗
于是协程中有一个很实用的函数:withContext。这个函数可以切换到指定的线程,并在闭包内的逻辑执行结束之后,自动把线程切回去继续执行,
用 withContext 改写一下,它的结构大致就长这个亚子:
launch(Dispachers.Main) {
…
withContext(Dispachers.IO) {
…
}
…
withContext(Dispachers.IO) {
…
}
…
}
复制代码
比如上面的登录的栗子就可以改写成这样:
GlobalScope.launch(Dispatchers.Main) {
var result: WanResponse?=null
withContext(Dispatchers.IO){
//请求登录
result = repository.login(userName,userPassword).await()
}
//更新ui
btnLogin.text = result?.data?.username
}
好像的确变得简洁了许多,但离我们的目标:看起来同步的方式写出异步的代码还差那么一点。
既然不需要嵌套了,那就可以把io线程的操作,拿出来单独作为函数,就可以写成这样:
suspend fun login(name: String,password: String): WanResponse {
return withContext(Dispatchers.IO) {
val repository = ApiRepository()
repository.login(name, password).await()
}
}
这个函数和普通函数不一样,多出来一个关键字suspend,直译过来是挂起的意思,那这个关键字真正的作用到底什么呢?这个下面会详细解释,这里先跳过。
挂起函数写好了,那开启协程部分的代码就可以改写一下
GlobalScope.launch (Dispatchers.Main){
val result =login(userName,userPassword)
btnLogin.text = result.data.username
}
这样看起来就和同步方式的代码一样了
supspend 关键字的作用
上面提到了挂起函数中的suspend,那它的作用是什么呢?是挂起作用?
如果是挂起作用,那它挂起的对象是什么?是当前线程还是所在的函数?
答案是都不是,协程中的挂起,本质上挂起的对象是协程。协程是啥?就是launch函数包起来的代码块。
GlobalScope.launch (Dispatchers.Main){
//login是个suspend函数
val result = login(userName,userPassword)
btnLogin.text = result.data.username
}
//Next
…
suspend fun login(name: String,password: String): WanResponse {
return withContext(Dispatchers.IO) {
val repository = ApiRepository()
repository.login(name, password).await()
}
}
当执行到suspend函数的时候,该协程就会在当前线程中被挂起,通俗一点理解,当前线程暂时不管这个协程了。
那当前线程它去做什么呢?它该去做啥就做啥,比如还是上面的例子,由于该协程是在主线程中的,在请求登录时协程就会被挂起,主线程就从这个协程中脱离出来,继续走NEXT之后的代码。当登录请求成功之后,挂起函数又会将其切换到主线程中。
敲黑板,敲黑板啦
挂起其实做的就是稍后会将线程自动切换回来的操作,切换回来的动作就叫恢复(resume),它是协程里的功能,所以我们要在协程里面(这个协程当然也可以是一个挂起函数),去调用自带的挂起函数,比如常用的withContext()
到这里你很可能就认为suspend关键字的作用就是挂起协程的作用了,那就太高估它,它并没有这么神奇的功能。
suspend它本质上只是一个提醒,那么是谁对谁的提醒呢?
它是函数创建者对函数调用者的提醒,告诉函数调用者我是个挂起函数,是个耗时函数,请你在协程里面调用我。表面上它是一个要求,实际上它是一个提醒。
supend它并没有做挂起操作的功能,真正做挂起的是这个函数里面挂起函数,比如我们这里用的withContext这个自带的挂起函数。
所以值得注意的是:如果我们没有在supsend这个函数里面去使用挂起函数,那这个挂起函数就没有意义。因为一旦你使用了suspended关键字,就意味着它只能在协程中被调用。其实很容易理解:你又不需要挂起,还加个suspend让调用者只能在协程里面被调用,这不就相当于占着茅坑不拉shi一样[手动狗头]
简单总结一下:supsend的关键字它存在的意义就是提醒,在某种程度来说它可以限制调用者不在主线程做耗时操作。
如果创建一个 suspend 函数但它内部不包含真正的挂起逻辑,编译器会给一个提醒:redundant suspend modifier,告诉你这个 suspend 是多余的。
避开误区
1. 协程的挂起是非阻塞式,而线程是阻塞式的?
首先什么是非阻塞式?
简单点说非阻塞式就是不卡当前线程。这样看协程的确是非阻塞式,比如你在主线程遇到一个挂起函数,就被切到另一个线程去做操作了,那么你的主线程当然就不会被卡了。
那么问题来了,用Thread去切换线程是阻塞式的吗?
线程是阻塞式的这句话只在单线程情况下是对的,单线程的耗时操作肯定会卡线程,所以是非阻塞式的。在多线程下,多线程的切换线程是不会卡线程的,所以肯定是非阻塞式的。
那单协程的挂起是阻塞式的吗?
它也是非阻塞式,因为它可以利用挂起函数来切线程。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
写在最后
很多人在刚接触这个行业的时候或者是在遇到瓶颈期的时候,总会遇到一些问题,比如学了一段时间感觉没有方向感,不知道该从哪里入手去学习,对此我整理了一些资料,需要的可以免费分享给大家
我的【Github】会分享一些关于Android进阶方面的知识,也会分享一下最新的面试题~
如果你熟练掌握GitHub中列出的知识点,相信将会大大增加你通过前两轮技术面试的几率!这些内容都供大家参考,互相学习。
①「Android面试真题解析大全」PDF完整高清版+②「Android面试知识体系」学习思维导图压缩包——————可以在我的【Github】阅读下载,最后觉得有帮助、有需要的朋友可以点个赞
id进阶方面的知识,也会分享一下最新的面试题~
如果你熟练掌握GitHub中列出的知识点,相信将会大大增加你通过前两轮技术面试的几率!这些内容都供大家参考,互相学习。
①「Android面试真题解析大全」PDF完整高清版+②「Android面试知识体系」学习思维导图压缩包——————可以在我的【Github】阅读下载,最后觉得有帮助、有需要的朋友可以点个赞
[外链图片转存中…(img-vJrQjrcu-1710844276908)]
[外链图片转存中…(img-2D4ezuKX-1710844276908)]