先理清楚三种说法:
1、协程是轻量级线程、比线程耗费资源少
错。竟然还是官方说法。协程是语言层面的东西,而线程是操作系统层面的,就没啥可比性。 kotlin协程,和操作系统概念的协程不一样,理念有一点点像,但是没有任何关系。
2、协程是线程框架
对。就是对线程的封装,模糊了线程。
这个问题可能不太直观。为便于理解协程的优势,贴一段代码。 代码1的执行线程,和代码4执行的线程,有可能是不一样的。从这个例子,你应该能理解协程的优势了吧? 这就是宣传的“用同步的方式,写异步的代码”的意思。
- fun drive() {
- GlobalScope.launch(Dispatchers.IO) { //Dispatchers.IO,指定线程类型。后面是线程池。
- println("我坐车,我快乐")//1,开启协程1
- withContext(Dispatchers.Default) {//2, 开启协程2,并且切换到其他线程
- println("导游去订酒店")//3
- }
- println("车继续开,剩下团友在车上")//4, 再次执行协程1,线程切换到Dispatchers.IO线程池.
- }
- }
3、协程效率高于线程
错。与第一点类似,协程与线程没有可比性。协程在运行方面的高效率,换成回调方式也是能够达成同样的效果。 协程内部本来就是回调实现的。只是在编译阶段封装了回调的细节而已。
在介绍如何使用前,先介绍一下,协程中几个重要的概念类。
1. 协程作用域。它决定了线程调度池的范围和生命周期。 2. 开发者可以在作用域里开协程干事情,相当于你创建了一个Task,然后submit给线程调度池。 3. 最后用cancel()方法关闭整个任务调度。类比于线程池的shutdown()。 4. Android kotlin扩展库中,有两个比较好用的协程作用域,viewModelScope和lifecyceScope, 分别针对ViewModel和Activity/Fragment。当退出时,会自动帮你关闭协程作用域里的所有协程。
下面的示例,是开发者创建协程作用域时, 可以遵循的方式。
- class ExampleClass {
-
- // 定义作用域,指定默认的协程执行线程。
- //Job()下节再讨论。
- val scope = CoroutineScope(Job() + Dispatchers.Main)
-
- fun exampleMethod() {
- // 用launch在作用域里创建一个协程任务
- scope.launch {
- // 协程任务执行的内容。
- fetchDocs()
- }
- }
-
- fun cleanUp() {
- // 取消作用域,并且会关闭作用域里所有的协程任务。
- scope.cancel()
- }
- }
协程上下文,用来设置协程属性值,比如,控制协程的生命周期(Job&),指定在哪类线程中执行(Dispatchers),设置协程的名字(CoroutineName), 捕获协程抛出的异常(CoroutineExceptionHandler)等。
它们都有一个共同的特点,都是继承自CoroutineContext,是为了方便用一个链表串起来。CoroutineContext重载了Plus符号,下面的运算符重载函数目的,就是用+号做成一个链表。
- public operator fun plus(context: CoroutineContext): CoroutineContext =
- if (context === EmptyCoroutineContext) this else // fast path -- avoid lambda creation
- context.fold(this) { acc, element ->
- val removed = acc.minusKey(element.key)
- if (removed === EmptyCoroutineContext) element else {
- // make sure interceptor is always last in the context (and thus is fast to get when present)
- val interceptor = removed[ContinuationInterceptor]
- if (interceptor == null) CombinedContext(removed, element) else {
- val left = removed.minusKey(ContinuationInterceptor)
- if (left === EmptyCoroutineContext) CombinedContext(element, interceptor) else
- CombinedContext(CombinedContext(left, element), interceptor)
- }
- }
- }
下面代码是设置CoroutineContext的范例:
- override fun init() {
-
- val coroutineExceptionHandler = CoroutineExceptionHandler { coroutineContext, Throwable ->
-
- }
-
- viewModelScope.launch(SupervisorJob() + Dispatchers.IO + CoroutineName("alan") + coroutineExceptionHandler) {
-
- }
-
- }
- fun testAsync() {
- runBlocking {
- //启动协程
- var job = GlobalScope.async {
- println("job1 start")
- Thread.sleep(10000)
- //返回值
- "fish"
- }
- //等待协程执行结束,并返回协程结果
- var result = job.await()
- println("result:$result")
- }
- }
和线程一样,协程也会遇到,需要中止和异常中止,这两种。与线程处理方式类似:对于阻塞状态的协程,我们可以捕获异常,对于非阻塞的地方我们使用状态判断。
需要中止,
线程里就是:Thread.Interrupt()/Thread.Interrupted().
相应的协程里就是:job.isCancelled/job.cancel()
- //属性
- job.isActive //协程是否活跃
- job.isCancelled //协程是否被取消
- job.isCompleted//协程是否执行完成
- ...
- //函数
- job.join()//等待协程完成
- job.cancel()//取消协程
- job.invokeOnCompletion()//注册协程完成回调
- ...
- fun testCancel5() {
- runBlocking() {
- var job1 = launch(Dispatchers.IO) {
- try {
- //挂起函数
- } catch (e : Exception) {
- println("delay exception:$e")
- }
- if (!isActive) {
- println("cancel")
- }
- }
- }
- }
异常中止:
也就是对抛异常的处理。分为在子协程里捕获和全局捕获。
子协程里捕获:try/catch不要加到协程外面,因为主线程不能捕获子线程的异常。
- fun testException2() {
- runBlocking {
- var job1 = launch(Dispatchers.IO) {
- try {
- println("job1 start")
- //异常
- 1 / 0
- println("job1 end")
- } catch (e : Exception) {
- println("e=$e")
- }
- }
- }
- }
全局捕获:
与线程类似,协程也可以全局捕获异常。就是设置我们前面讲到的CoroutineExceptionHandler。虽然能够捕获异常,但是发生异常的协程还是不能往下执行了。
- //创建处理异常对象
- val exceptionHandler = CoroutineExceptionHandler { _, exception ->
- println("handle exception:$exception")
- }
- fun testException3() {
- runBlocking {
- //声明协程作用域
- var scope = CoroutineScope(Job() + exceptionHandler)
- var job1 = scope.launch(Dispatchers.IO) {
- println("job1 start")
- //异常
- 1 / 0
- println("job1 end")
- }
- }
- }
SupervisorJob:
关于异常不得不讲一下SupervisorJob。
当默认或者设置Job()的时候。子协程发生异常后,会取消父协程、同级兄弟协程的执行,这在有些场景是不合理的,因为伤害范围太广,明明是一个子协程的锅,非得所有协程来背。
SupervisorJob它的作用,就是谁的锅谁背。
好了,协程的简单讲解就到此了。虽然是简单,但也触及了核心,是理解协程的基础。
引用: