前言
上周在内部分享会上大佬同事分享了关于 Kotlin 协程的知识,之前有看过 Kotlin 协程的一些知识,以为自己还挺了解协程的,结果…
在这一次分享中,发现 Flow 和 Channel 这一块儿知识是自己不怎么了解的,本文也将着重和大家聊一聊这一块儿的内容,协程部分将分为三篇,本文是第一篇:
“
《即学即用Kotlin - 协程》 《抽丝剥茧Kotlin - 协程基础篇》 《抽丝剥茧Kotlin - 协程Flow篇》
目录
一、基础
1. 概念
相信大家或多或少的都了解过,协程是什么,官网上这么说:
Essentially, coroutines are light-weight threads.
协程是轻量级的线程,为什么是轻量的?可以先告诉大家结论,因为它基于线程池API,所以在处理并发任务这件事上它真的游刃有余。
有可能有的同学问了,既然它基于线程池,那我直接使用线程池或者使用 Android 中其他的异步任务解决方式,比如 Handler、RxJava等,不更好吗?
协程可以使用阻塞的方式写出非阻塞式的代码,解决并发中常见的回调地狱,这是其最大的优点,后面介绍。
2. 使用
GlobalScope.launch(Dispatchers.Main) {
val res = getResult(2)
mNumTv.text = res.toString()
}
启动协程的代码就是如此的简单。上面的代码中可以分为三部分,分别是 GlobalScope、Dispatcher 和 launch,他们分别对应着协程的作用域、调度器和协程构建器,我们挨个儿介绍。
协程作用域
协程的作用域有三种,他们分别是:
- runBlocking:顶层函数,它和 coroutineScope 不一样,它会阻塞当前线程来等待,所以这个方法在业务中并不适用 。
- GlobalScope:全局协程作用域,可以在整个应用的声明周期中操作,且不能取消,所以仍不适用于业务开发。
- 自定义作用域:自定义协程的作用域,不会造成内存泄漏。
显然,我们不能在 Activity 中调用 GlobalScope,这样可能会造成内存泄漏,看一下如何自定义作用域,具体的步骤我在注释中已给出:
class MainActivity : AppCompatActivity() {
// 1\. 创建一个 MainScope
val scope = MainScope()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 2\. 启动协程
scope.launch(Dispatchers.Unconfined) {
val one = getResult(20)
val two = getResult(40)
mNumTv.text = (one + two).toString()
}
}
// 3\. 销毁的时候释放
override fun onDestroy() {
super.onDestroy()
scope.cancel()
}
private suspend fun getResult(num: Int): Int {
delay(5000)
return num * num
}
}
调度器
调度器的作用是将协程限制在特定的线程执行。主要的调度器类型有:
- Dispatchers.Main:指定执行的线程是主线程,如上面的代码。
- Dispatchers.IO:指定执行的线程是 IO 线程。
- Dispatchers.Default:默认的调度器,适合执行 CPU 密集性的任务。
- Dispatchers.Unconfined:非限制的调度器,指定的线程可能会随着挂起的函数的发生变化。
什么是挂起?我们就以九心吃饭为例,如果到公司对面的广场吃饭,九心得经过:
- 走到广场 10min > 点餐 5min > 等待上餐 10min > 就餐 30min > 回来 10 min
如果九心点广场的外卖呢?
- 九心:下单 5min > 等待(等