overview
Kotlin 协程的基本概念:协程是靠编译器实现的,并不需要操作系统和硬件的支持。
CoroutineContext 协程上下文,它包含一个默认的协程调度器,所有协程都必须在CoroutineContext中执行。
CoroutineScope 协程作用域,它一个接口只包含一个属性CoroutineContext,它定义了一个有生命周期的实体上的实现。
GlobalScope 一个全局的作用域,用于启动顶级的协程,这些协程会在整个应用程序的生命周期内运行。
CoroutineDispatcher 协程调度器,它用来调度和处理任务,决定了协程应该在哪个或哪些线程中执行。
lifecycleScope:生命周期范围,用于activity等有生命周期的组件,在DESTROYED的时候会自动结束。
viewModelScope:viewModel范围,用于ViewModel中,在ViewModel被回收时会自动结束。
CoroutineScope的作用域类型:
顶级作用域:没有父协程的协程所在的作用域为顶级作用域。
协同作用域:协程中启动新的协程,新协程为所在协程的子协程,这种情况下子协程抛出的未捕获异常将传递给父协程处理,父协程同时也会被取消。
主从作用域:与协同作用域类似,但子协程的异常不会向上传递给父协程。
CoroutineScope的声明方式:(它们都遵循CoroutineScope interface,所以你可以自定义一个协程)
GlobalScope.launch:声明顶级作用域。
GlobalScope.async:声明顶级作用域。
runBlocking:声明顶级作用域。
coroutineScope:声明协同作用域。
supervisorScope:声明主从作用域,异常不会向上传递。
Kotlin中包含四种协程调度器,它们的作用分别是:
Dispatchers.Default:这是默认调度器,适合处理CPU密集型任务,如大量计算。它是一个CPU密集型任务调度器,通常用于后台计算。
Dispatchers.IO:这是IO调度器,适合执行IO相关操作,如文件读写、网络请求等。它是一个IO密集型任务调度器,通常用于处理文件操作和网络IO操作。
Dispatchers.Main:这是UI调度器,通常在主线程上执行任务,如更新UI。在Android平台上,这通常用于处理UI相关的操作。
Dispatchers.Unconfined:这是非受限制调度器,不会要求协程在哪个线程上执行。它会在启动它的线程上执行,直到第一个挂起点,然后恢复执行。
协程调度器的使用场景和特点:
- Dispatchers.Default:适用于需要大量计算的任务,如数据排序、解析等。
- Dispatchers.IO:适用于文件读写、数据库操作和网络请求等IO密集型任务。
- Dispatchers.Main:适用于需要在主线程上执行的UI更新操作
... 开始代码示例:将会涉及到几乎所有的协程
- launch && async
val job1 = GlobalScope.launch(start = CoroutineStart.LAZY) {
delay(1000)
println("hello launch")
// async
val result1 = async(start = CoroutineStart.DEFAULT) {
delay(1000)
println("hello async")
}
// invakeOnCompletion
result1.invokeOnCompletion {
if (it != null) {
// result1.cancelAndJoin() 添加之后执行这里
println("exception: ${it.message}")
}
else {
println("result is complete")
}
}
result1.cancelAndJoin()
}
// 默认是 CoroutineStart.DEFAULT,不需要调用job1.start()
// CoroutineStart.LAZY时需要手动调用job1.start(),相当于懒加载
// 使用 start 方法来启动协程,不等待协程完成
val what1 = job1.start()
- runBlocking
val what2 = runBlocking {
delay(1000)
println("hello runBlocking")
launch {
delay(1000)
println("hello launch")
}
}
- runBlocking + yield
yield 用于挂起当前协程,将当前协程分发到CoroutineDispatcher队列,等其他协程完成/挂起之后,在继续执行先前的协程。
val what3 = runBlocking {
val job1 = launch {
println('1')
yield()
println('3')
yield()
println('5')
}
val job2 = launch {
println('2')
yield()
println('4')
yield()
println('6')
}
println('0')
// 无论是否调用以下两句,上面两个协程都会执行
job1.join()
job2.join()
// 执行结果为:0 1 2 3 4 5 6
}
- withContext + CoroutineScope
val what4 = CoroutineScope(context = Dispatchers.Default).launch {
val result1 = withContext(this.coroutineContext){
delay(2000)
1
}
val result2 = withContext(Dispatchers.IO) {
delay(1000)
2
}
val result3 = result1 + result2
println("${result3}")
}
- withContext + CoroutineScope + NonCancellable
val what5 = CoroutineScope(Dispatchers.Default).launch {
withContext(NonCancellable){
delay(2000)
println("this code can not cancel")
}
}
// 取消也没用,what5会正常执行
val CanNot = what5.cancel()
- CoroutineScope + coroutineScope
val what6 = CoroutineScope(Dispatchers.Default).launch {
val result1 = withContext(this.coroutineContext){
delay(2000)
1
}
// coroutineScope
val result2 = coroutineScope {
delay(2000)
2
}
val result3 = result1 + result2
println("${result3}")
}
- CoroutineScope + jobs
var jobs = ArrayList<Job>()
var isAdd = false
val what7 = CoroutineScope(Dispatchers.Default).launch {
val job1 = this.launch(context = Dispatchers.Unconfined) {
println("Unconfined : I am is working in thread ${Thread.currentThread()}")
}
isAdd = jobs.add(job1)
val job2 = this.launch (context = this.coroutineContext){
// 使用父级上下文,也就是runBlocking的上下文
println("coroutineContext : I am is working in thread ${Thread.currentThread()}")
}
isAdd = jobs.add(job2)
val job3 = this.launch (context = Dispatchers.Default){
// 使用父级上下文,也就是runBlocking的上下文
println("'Dispatchers.Default' : I am is working in thread ${Thread.currentThread()}")
}
isAdd = jobs.add(job3)
val job4 = this.launch {
println("'default' : I am is working in thread ${Thread.currentThread()}")
}
isAdd = jobs.add(job4)
val job5 = this.launch(context = newSingleThreadContext("ThreadForHong")) {
println("'TreadForHong': I am working in thread ${Thread.currentThread()}")
}
isAdd = jobs.add(job5)
if(isAdd) {
jobs.forEach {
it.join()
}
}
// 执行结果如下:(每次执行顺序不同)
/*
* Unconfined : I am is working in thread Main
* 'Dispatchers.Default' : I am is working in thread DefaultDispatcher-worker-1
* 'TreadForHong': I am working in thread ThreadForHong
* coroutineContext : I am is working in thread Main
* 'default' : I am is working in thread Main
* */
}
- 父子协程
val what8 = GlobalScope.launch {
// 第一个使用不同的上下文
val job1 = GlobalScope.launch {
println("job1")
delay(1000)
println("job1 context ...")
}
// 不用写this.coroutineContext,默认也是 this.coroutineContext
val job2 = this.launch(context = this.coroutineContext) {
println("job2")
delay(1000)
println("job2 context ...")
}
job1.join()
job2.join()
}
val what8in = what8.cancel()
// 执行结果:
/*
* job1
* job2
* job1 context ...
*
* job2 context ...它没有被打印,原因:父级Job取消执行,what8.cancel()
* 如果job2 context = Dispatchers.Default,则不会跟随父级Job取消而取消,依然执行
* */
- 父子协程完善执行代码:
val what9 = runBlocking {
val job1 = this.launch(context = this.coroutineContext) {
println("job1 is run")
delay(1000)
println("job1 done")
}
val job2 = this.launch(context = this.coroutineContext) {
println("job2 is run")
delay(1000)
println("job2 done")
}
val job3 = this.launch(context = this.coroutineContext) {
println("job3 is run")
delay(1000)
println("job3 done")
}
val job4 = this.launch(context = this.coroutineContext) {
println("job4 is run")
delay(1000)
println("job4 done")
}
job1.join()
job2.join()
job3.join()
job4.join()
}
// 执行结果:
/*
* job1 is run
* job2 is run
* job3 is run
* job4 is run
* job1 done
* job2 done
* job3 done
* job4 done
* */
- CoroutineContext "+" 操作
val what10 = CoroutineScope(Dispatchers.Unconfined).launch {
val childJob = CoroutineScope(Dispatchers.Unconfined + coroutineContext).launch {
println("childJob is run")
delay(1000)
println("childJob done")
}
}
val what10in = what10.cancel()
// 执行结果:childJob is run, 纸打印一句,因为变成了父子协程,父级取消,suspend的一切跟着取消
// 这里的delay(1000)可以理解成suspend,或者说就是suspend
- CoroutineContext "+Job" 操作
val what11 = runBlocking {
val whatJob1 = Job()
this.launch(context = coroutineContext + whatJob1) {
delay(500)
println("job1 dene")
}
this.launch(context = coroutineContext + whatJob1) {
delay(1000)
println("job2 dene")
}
this.launch(context = Dispatchers.Default + whatJob1) {
delay(1500)
println("job3 done")
}
this.launch(context = Dispatchers.Default + whatJob1) {
delay(2000)
println("job4 done")
}
this.launch(context = Dispatchers.Default + whatJob1) {
delay(4000)
println("job5 done")
}
delay(1888)
whatJob1.cancel()// 取消任务
}
// 执行结果
/*
* job1 dene
* job2 dene
* job3 dene
* */
- 安全的使用 CoroutineScope,自定义的都比较安全,尽量少用GlobalScope(作用域越小越安全)
比方说:你家里有个老婆叫红旗
,我外面有一群女人叫彩旗
,彩旗越少就越安全。但是不刺激啊...
val what12 = SafeCoroutineScope(Dispatchers.Unconfined).launch {
val whatJob1 = Job()
this.launch(context = coroutineContext + whatJob1) {
delay(500)
println("job1 dene")
}
this.launch(context = coroutineContext + whatJob1) {
delay(1000)
println("job2 dene")
}
delay(800)
whatJob1.cancel()
}
- viewModelScope
viewModelScope 会在对象销毁时,自动销毁,也可手动提前销毁优化内存
internal class MyViewModel : ViewModel() {
val whatScope = viewModelScope
fun fetchData() {
// 在 viewModelScope 中启动一个新的协程
whatScope.launch {
// 这里执行异步操作,例如网络请求或数据库查询
val data = performNetworkOperation() // 假设这是一个挂起函数
// 使用数据
processData(data)
}
}
private suspend fun performNetworkOperation(): String {
// 这里执行网络请求或其他耗时操作
return "new value"
}
private fun processData(data: String) {
// 处理数据
whatScope.cancel()
}
}
- Channel 协程之间的数据通讯 Channel
Channel相当于之前博客《kotlin多线程》篇章中的BlockingQueue
val what13 = runBlocking {
val channel = Channel<Int>() // 相当于BlockingQueue
this.launch(Dispatchers.Default) {
repeat(5){
delay(200)
channel.send(it)
if (it == 4) channel.close()
}
}
this.launch(Dispatchers.Default) {
repeat(5){
try {
val element = channel.receive()
println(element)
}
catch (e:ClosedReceiveChannelException) {
// 必须捕获这个异常否则程序会崩
println("error: ${e.message}")
}
}
}
}
- Channel 管道
// Channel 管道
fun produce1() = SafeCoroutineScope(Dispatchers.Default).produce<Int> {
repeat(5){
send(it) // 发送0~4
}
}
fun produce2(numbers:ReceiveChannel<Int>) = SafeCoroutineScope(Dispatchers.Default).produce<Int> {
for (x in numbers) {
send(x * x)
}
}
fun produce3(numbers:ReceiveChannel<Int>) = SafeCoroutineScope(Dispatchers.Default).produce<Int> {
for (x in numbers) {
send(x+1)
}
}
fun HongMain() = runBlocking<Unit> {
val numbers1 = produce1()
val numbers2 = produce2(numbers1)
val numbers3 = produce3(numbers2)
numbers3.consumeEach {
println("consume: $it")
}
println("receive done")
// 接收完成之后,关闭所有produce
numbers3.cancel()
numbers2.cancel()
numbers1.cancel()
}
- channel 缓冲
fun HongCache() = runBlocking {
val cacheSize = 3
val channel = Channel<Int>(cacheSize)// 创建带有缓冲区的channel
this.launch(this.coroutineContext) {
repeat(6){
delay(50)
println("send $it")
channel.send(it)
}
}
this.launch {
delay(1000)
repeat(6){
println("receive: ${channel.receive()}")
}
}
}
/*
* 第一个协程先发送两个消息,需要等到第二个协程时间到了,第二个协程才开始消费信息
* 等到缓冲区的信息消费完毕后,第一个协程继续发送信息,第二个协程继续消费信息,
* 以此类推直到结束。
* */
- actor
actor 本身就是一个协程,内部包含一个channel,通过channel与其他协程通讯,这个内部channel只做接收信息
fun HongActor() = runBlocking {
val summer = actor<Int>(context = this.coroutineContext) {
var sm = 0
for (x in channel) {
sm += x
println("sim : $sm")
}
}
repeat(10){
summer.send(it)
}
}
- select
fun produceSelect1(context:CoroutineContext) = SafeCoroutineScope(context).produce<String> {
while (true) {
delay(400)
send("hong")
}
}
fun produceSelect2(context:CoroutineContext) = SafeCoroutineScope(context).produce<String> {
while (true) {
delay(600)
send("lina")
}
}
suspend fun selectProduces(channel1:ReceiveChannel<String>,channel2:ReceiveChannel<String>) {
select<Unit> {
channel1.onReceive {
println(it)
}
channel2.onReceive {
println(it)
}
}
}
fun HongSelectMain() = runBlocking {
val hong = produceSelect1(this.coroutineContext)
val lina = produceSelect2(this.coroutineContext)
repeat(10) {
selectProduces(hong,lina)
}
this.coroutineContext.cancelChildren()
// ok
}
- lifecycleScope + Activity
class MiLifecycleCoroutineScope:AppCompatActivity(){
override fun onStart() {
super.onStart()
}
fun loadData(){
lifecycleScope.launch {
val data = fetchDataFromNetwork()
println(data)
}
}
suspend fun fetchDataFromNetwork():Any {
delay(2000)
return "data"
}
}
// Activity销毁,lifecycleScope自动销毁,和viewmodelScope类似,属于绑定关系的CoroutineScope
- rememberCoroutineScope + @Composable
@Composable
fun MyComposable() {
val coroutineScope = rememberCoroutineScope()
// 在这里,你可以使用 coroutineScope.launch 来启动一个新的协程
// 这个协程会在组件销毁时自动取消,并且会在每次组件重组时保持相同的实例
coroutineScope.launch {
// 在这里执行异步操作
}
}
- 自定义的 SafeCoroutineScope
class SafeCoroutineScope(context:CoroutineContext):CoroutineScope,Closeable {
val handler = CoroutineExceptionHandler { context, exception ->
println("异常捕获并处理: $exception")
}
override val coroutineContext: CoroutineContext = SupervisorJob() + context + handler
override fun close() {
coroutineContext.cancelChildren()
}
}
以上实乃androidx和kotlinx的内功心法,持有它Android在你面前就像褪去衣服的美女,发挥你的原始兽性去攻略它吧.
thank..