学习使用kotlin中的coroutines
学习背景
在学习第一行代码时的Service中的启动子线程来完成异步处理时,书中提到了使用AsyncTask来更方便地操作UI。
但由于Context泄露、回调遗漏、configuration变化导致奔溃、平台差异性等原因,AsyncTask在Android 11 中被正式弃用。
Google官方提供了两种替换思路:
- 使用java.util.concurrent下的包,如Executor、ThreadPoolExecutor、FutureTask
- 使用kotlin的Coroutines——协程
既然是在学习kotlin的Android编程,总不能照学书中已经过时的内容(Android更新换代也太快了吧555我要回去写后端了)此处就又给自己挖了一个坑啦。
学习路线
通过查阅资料如何正确的在Android上使用协程中,我得到了初步的学习思路:先按照Kotlin官方文档提供的example学习协程的概念以及初步使用。当然,在上文中也提到了一般不建议直接照着官方文档的方式使用协程,那么接下来我会再阅读一下文中提到的三篇文章试试看:
1.在 Android 上使用协程(一):Getting The Background
2.在 Android 上使用协程(二):Getting started
3.在 Android 上使用协程(三) :Real Work
kotlin官方文档学习之路
添加依赖
从README文档中可以得知kotlin在不同的环境中有不同的引入依赖方式,在安卓的开发环境中只需通过向项目app文件夹的build.gradle
中添加依赖
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2'
即可
协程基础
文章中介绍了协程是轻型的线程,并且可以使用一系列的CoroutineScope
启动(例如GlobalScope
);然后介绍了delay
和thread.sleep()
的区别,以及如何清晰地区分阻塞函数与非阻塞函数。类似于delay
的挂起函数只能在协程中使用。
之后通过设置一个对象,接收了一个GlobalScope.launch
返回的Lambda,可以通过调用join()
的方式在合适的时候启动该协程。
runBlocking
与 coroutineScope
作用域的区别
两个种类的代码块都可以等待它们内部的代码执行完成再进行后续操作。区别在于:
-
runBlocking
方法会阻塞当前线程, -
coroutineScope
方法仅仅是悬挂并释放底层线程供其他区域使用。 - 因此
runBlocking
是一个常规函数,coroutineScope
为悬挂函数。
#例1
fun main() = runBlocking {
runBlocking {
launch {
delay(300)
println("Task from ronBlocking nested launch" + order.toString())
order += 1
}
delay(200)
println("Task from runBlocking" + order.toString())
order += 1
}
coroutineScope {
launch {
delay(500)
println("Task from nested launch" + order.toString())
order += 1
}
delay(100)
println("Task from coroutine scope" + order.toString())
order += 1
}
println("Coroutine scope is over" + order.toString())
order += 1
}
# 结果
Task from runBlocking1
Task from ronBlocking nested launch2
Task from coroutine scope3
Task from nested launch4
Coroutine scope is over5
#例2
fun main() = runBlocking {
launch {
delay(300)
println("Task from runBlocking" + order.toString())
order += 1
launch {
delay(200)
println("Task from ronBlocking nested launch" + order.toString())
order += 1
}
}
coroutineScope {
launch {
delay(500)
println("Task from nested launch" + order.toString())
order += 1
}
delay(100)
println("Task from coroutine scope" + order.toString())
order += 1
}
println("Coroutine scope is over" + order.toString())
order += 1
}
# 结果
Task from coroutine scope1
Task from runBlocking2
Task from nested launch3
Coroutine scope is over4
Task from ronBlocking nested launch5
从例1中可以看出内部的runBlocking
代码块阻塞了main()线程,而从例2中可以发现delay
只会阻塞它所在的线程/协程。
GlobalScope :全局协程
由GlobalScope.launch
启动的协程相当于守护进程,生存周期为当前的Context的生存周期。