Kotlin协程学习
协程是什么
在Kotlin官方文档上面描述——“本质上,协程是轻量级的线程”,在Kotlin中的协程也可以说是Kotlin提供的一套线程封装的API。
一个简单的示例:
1 | launch({ |
在上面的代码中,我们可以看到第2行中做了一个请求网络数据的耗时的I/O操作。通过协程的方式,我们可以将这样的一个异步操作写得看起来和同步操作无异。
基本使用
示例中的launch
函数并不是一个顶层函数,不能直接调用,需要被对象所调用。可以通过三种方法来创建协程:
1 | // 使用runBlocking顶层函数 |
- 方法一适用于单元测试,因为
runBlocking
中的挂起方法以及调用层级会一直有效的阻塞当前线程 - 方法二不会阻塞当前显示,但是如果使用了
GlobalScope
,则该挂起方法的生命周期会和整个App一致,并且不能取消
在launch
方法中可以传入context
参数,该参数和Android中的context不是同一概念,我们可以使用Android平台封装好的context,如Dispatchers.IO
,Dispatchers.Main
,Dispatchers.Default
,以及Dispatchers.Unconfined
。在这里定义了对应的context,那么协程就会在该context上开启,比如在IO线程或者主线程中。
协程写法的好处
如果有这样一个需求,我们需要在一个方法体内同时获取用户的头像以及公司的信息,我们使用callback可能会这样写:
1 | api.getAvatar(user.avatarUri){ |
如果我们使用协程的写法:
1 | coroutineScope.launch(Dispatchers.Main){ |
这样对比可以看出,即使是复杂的并行网络请求,通过协程写出来的代码结构会更加清晰。
协程的正确使用
我们刚刚说过我们在launch
方法中传入不同的context
会决定该协程在哪个context
上开启,那么刚才获取头像和公司信息的协程示例代码中,我们api请求应该需要放在IO线程中,而后面的showMergeMessage
是需要将信息在页面上渲染,应该放在主线程中,类似于:
1 | coroutineScope.launch(Dispatchers.IO){ |
这样的话看起来确实好理解一些,但是还是有嵌套的代码。这里需要提到另一个函数——withContext
。这个函数可以做一件事情——切换到指定的线程,在闭包中的逻辑执行结束后,自动切换回到之前的线程。如果我们把withContext放到getAvatar方法中,那么写法就变回了之前同步的写法。
结尾
文档内容是大部分引用于朱凯的关于协程的文章,做的一次学习总结。原文地址