相关概念和API可以直接进入[官网](https://kotlinlang.org/)查询,
不过不推荐kotlin中文网,文章翻译的略显生硬,看了更容易迷惑。
本文不讲基础(基础我也不怎么清楚。。),适合喜欢拿来主义的同学
协程作为轻量级的线程,是建立在线程之上的调度,比线程节省资源但不意味着不消耗资源,在Android这种系统资源相对珍贵的环境中,当异步繁琐的调用或者切换线程,如何及时的回收也是必要的工作。
### 一、添加依赖配置
coroutine 是在kotlin 1.3中发布的1.0正式版,所以建议优先使用这个版本。
项目根目录中gradle配置:
主module中gradle配置
假如你的其他module也使用kt代码,也需要添加上述依赖,不然有可能会出现找不到class或符号相关编译错误
二、替换RxJava和升级Retrofit
一般项目中会引用retrofit + RxJava的组合进行网络请求,或者你单独封装了一层RxJava来进行异步 *** 作、切换线程来处理你的业务
RxJava虽然 *** 作符众多(得有100个以上了吧),上手不易,但着实好用,一条链走下来搞定你的全部业务,如果有网络请求配合上retrofit这种支持Rx的库,不要太给力。
不过函数式开发有个通病,这条链子上会创建大量对象,回收内存抖动你不得不考虑,虽然使用了线程池,可开销不容小觑。
RxJava 虽然可以链式调用,但终究还是基于回调形式,而协程完全做到了同步方式写异步代码。
看下以前Retrofit的写法(简易)
RxJava 的调用链上使用RxLife来绑定生命周期(或者使用Uber的autodispose),很常见的代码。
对于RxJava你还有可能会封装一个小工具(主要是为了生命周期和回收问题)
现在呢,想用协程怎么办,假设你现在已经知道了如何启动一个协程
也知道了launch 和 async的区别(返回值不同),也知道了需要一个返回值和多个返回值的选择(async 和 reduce),那在Android中如何使用呢
三、封装
你可以像封装RxJava一样,封装一个工具来使用协程,同时遵循一些原则:
#### 1 调度器
你开启的协程需要有调度器,就像RxJava自由切换线程一样,但主线程调度器最好指定Main来避免不必要的麻烦。
举个例子,你使用RxJava+autodipose来绑定 生命周期,但你在子线程用RxJava又开启了一个新线程,调度回UI线程刷新view的时候会发生崩溃
#### 2 父协程和子协程有强烈的层级关系
cancel一个Job 会抛出 CancellationException ,但这个异常不需要开发人员管理,但如果协程内部发生了非 CancellationException ,则会使用这个异常来取消父Coroutine。为了保证稳定的Coroutine层级关系,这种行为不能被修改。 然后当所有的子Coroutine都终止后,父Coroutine会收到原来抛出的异常信息。
下边是个协程的简易封装案例
context 视业务情况而定是否传入
然后在你的act或Fragment中直接调用
对于Retrofit修改更为简单,2.6版本已经支持了协程,网上有很多介绍
将Observable返回值修改为你需要的bean, 加上suspend标记即可,
下边是示例代码:
### 四、混淆
网上的帖子大多以demo的思路来写,商用肯定要混淆了,不添加proguard你混淆后就死翘翘了,所以直接贴上来
coroutine的配置
假如release打包后你的Retrofit 请求结果发生了npe,检查下你的bean是否添加了不混淆配置
### 其他
研究协程没多久,有错误或建议欢迎指出,共同交流
另外推荐云在千峰的[博客](http://blog.chengyunfeng.com/?p=1088) ,是我目前看过的最友好的coroutine系列文章
还记得第一次听到 Coroutines 的时候,纳闷了一下,口瑞停,这是什么新的番号招式(误),之后其实也没有多在意了,好一段时间,因为一个档案的 I/O 会把 UI Thread 卡住,必须要用异步程序去处理,写 Handler Thread 可以避免,这也是最基础的方式,缺点也很明显某些时候还是避不掉,写 RX 又总觉得微妙感觉有点杀鸡用牛刀的感觉,后来看了一下决定用 Coroutines ,于是有了本篇文章。
普通情况下,执行的顺序会是很直白的 functionA() -> functionB() -> functionC() 。
如果只有一个 thread ,这样很顺畅没问题。
但假如这是一个跑在 main thread 上,而 ·function A· 是需要另一个 thread 的处理结果,而该结果是需要该 thread 耗费长时间作业才可以获得的。这边姑且称为 IO thread 好了。那不就意味着 function A 得等到 IO thread 处理结束并告知结果才能继续执行 function A 乃至 function B 之后才是 function C 呢?
那在等待 function A 的时候 main thread 啥事都不能做,只能 idle 在那边动也不动。
这如果是在 Android main thread 上,这样的行为会让画面 freeze ,时间稍微长一点就会 ANR 被 OS 当作坏掉进行异常排除了。
其实这个异步问题解决方案很多,诸如自己写一个 callback ,或者自干 Handler thread 去控管或者是用 RX ,去订阅之类。某些时候显得不直观,或者使用起来麻烦,总有种杀鸡何需使用牛刀的感觉。
那有没有可能?我就写成上面那样,但是当 function A 在等待 IO thread 时,让 main thread 去做其他的事情,等到 IO thread 结束耗时处理后,再回来继续执行 function A , function B 、 function C 呢?
是的,可以,这个解决方案便是 Coroutine 。
Coroutines ,这个单字会被标成错字,理由是他其实是两个单字合并而成的,分别是 cooperation + routine, cooperation 意指合作,routine 意指例行作业、惯例,照这里直接翻译就会是合作式例行作业。
想到辉夜姬让人想告白提到了惯例行为,也是念作 routine
那我们看到的翻译多半会是协程、协作程序…这样讲没啥前后感,谁协助程序?协助啥程序? 总之就是满头的问号。
这里 routine 指得是程序中被呼叫的 function、method ,也就是说,我们将 function 、method 协同其他更多的 function、method 共同作业这件事情称为 Coroutines 。
协同作业听起来还是很抽象,具体协同什么呢?
这便是 Coroutines 最典型的特色,允许 method 被暂停( suspended)执行之后再回复(resumed)执行,而暂停执行的 method 状态允许被保留,复原后再以暂停时的状态继续执行。
换句话说,就是我在 main thread 执行到 function A 需要等 IO thread 耗时处理的结果,那我先暂停 function A , 协调让出 main thread 让 main thread 去执行其他的事情,等到 IO thread 的耗时处理结束后得到结果后再回复 function A 继续执行,已得到我要的结果,这便是 Coroutines 的概念,这听起来不是很简单了呢?
事实上这个概念早在 1964 年就已经被提出了,而很多语言也都有这样的概念,只是 Android 上头类似的东西一直没有被积极推广,直到 Kotlin 成为官方语言后, Coroutines 以 Support Library 的形式被推广才又在 Android 业界流行起来。
首先,因为 Kotlin 的 Coroutine 并没有包含在原有包装中,而是以 Support Library 的形式提供开发者使用,所以需要另外导入该 Library。
这里选用这个版本进行演示,实际中可以根据自己的需要修改版本。
那因为是在 Android 上使用的, Android 上头的 main thread 跟普通 java 有点不一样,所以还需要另一个 implementation,不然会报错。
导入之后就可以开始使用了。
这边我想做的是画面上有一个会倒数的 Text ,用 Coroutines 可以简单地做到
那跑起来结果就像这样:
这样如果要 Thread 有相同的结果可以写成这样:
这样会有什么问题就是另一个故事了,至少现在这样不会马上出现 Exception (最常见的就是使用者离开画面没多久就出现一个 Exception),不过也并不是说用 Coroutines 就不会发生这些问题,记得这些做法没有什么优劣,差别在都选择就是了。
说回 Coroutines ,那跟 Thread 一样,某些时候我们会想要临时把它停住,那 GlobalScope.launch 会回传一个 Job class 的玩意
想要把它停住的话就用 cancel 即可
Scope 指得是范围, Coroutines 可以作用的范围。在 Main thread 上或是 IO thread 上,又或者希望开更多的 Worker thread,然后是可以在某个控制流(e.g Activity 的生命周期)中可被控制的。
原则上,在 Kotlin 里头使用任何标记 suspend 的 method(后面会提)都会在 Scope 里面,这样才可以控制 Coroutines 的行进与存活与否。
那这边举的例子, GlobalScope 继承自 CoroutineScope 。它是 CoroutineScope 的一个实作,它的概念就是最高层级的 Coroutines ,它的作用的范围伴随着 Application 的生命周期,那其实他的概念与 Dispatch.Unconfined 相同(待会会提到),用他其实可以避免 Coroutines 被过早结束,但也要记得是,这个用法类似直接呼叫静态函数,需要注意。
那如果直接实作 CoroutineScope 呢?
那会要求实作一个 CoroutineContext ,这是什么玩意?指的就是 Coroutines 作用的情景,这边可以指定他是在 Main thread 上或者就直接弄一个 Job 给他:
这样 launch 的时候就会使用这个 Job 来 *** 作了,如果没有特别定义,那这个 Job 就是跑在 Worker thread 上了,用它更新 UI 会出现 Exception ,这方面可以依据自己的需求去做调整。
不过更多时候我会希望他能够跑在 Main Thread 上, Koltinx Coroutine 有提供 CoroutineScope 的实作 - MainScrope
由于python是一种解释性脚本语言,python的多线程在运行过程中始终存在全局线程锁。简单的来说就是在实际的运行过程中,python只能利用一个线程,因此python的多线程并不达到C语言多线程的性能。
可以使用多进程来代替多线程,但需要注意的是多进程最好不要涉及到例如文件 *** 作的频繁 *** 作IO的功能。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)