什么是协程框架
一套基于线程的API框架
什么是协程?
是运行在线程之上更加轻量级的任务,它不是线程,只是一个任务而已。
为什么说是任务?
协程的本质的Runnable,是运行在线程之上。后续的分析协程本质,有兴趣的可以关注下。
为什么说是轻量级?
- 开启协程不等于开启线程,创建协程由协程框架调度;
- 协程是非堵塞的,在执行挂起函数的时候,充分利用IO,当然如果挂起函数是CPU密集型例外。
协程有什么用?
可以灵活地切换线程,用同步的方式写出异步代码(不用回调),解决回调地狱问题。
协程作用域是什么?
以下代码,GlobalScope.launch大括号内就是协程作用域。
GlobalScope.launch {//作用域
Log.d("test", "1")
}
Log.d("test", "2")
4.如何使用协程?
- GlobalScope.launch 创建顶级协程;因为GlobalScope是单例,所以协程的生命周期跟应用一致,使用时需要注意内存泄露问题;其实官方已经将此Api标记为废弃Delicate。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
GlobalScope.launch {//作用域
for (index in 1..1000) {//切换到另外的线程默认Dispatchers.Default)
Log.d("test", "$index")
}
}
}
- 解决GlobalScope.launch内存泄露问题 GlobalScope.launch会返回Job对象,在onDestroy方法中调用其cancel,协程内增加isActive判断或者try-catch处理,根据信息结束代码执行,这样协程才会结束,这个跟中断线程类似;
class MainActivity : AppCompatActivity() {
var job: Job? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
job = GlobalScope.launch {//作用域
for (index in 1..1000) {
if (isActive) {//判断是否被cancel
delay(200)
Log.d("test", "$index")
}
}
}
}
override fun onDestroy() {
super.onDestroy()
job?.cancel()
}
withContext
对比launch:
- withContext可以返回执行结果,而launch不行;
- withContext是一个suspend挂起函数; 以下代码,从打印日志可以看出,子协程执行完父协程才会继续执行,因为父协程执行到withContext会将协程挂起,等待withContext执行结束。
GlobalScope.launch {
val result = withContext(Dispatchers.IO) {//指定协切换到IO线程执行
delay(1000)
Log.d("test", "launch")
1//最后一行代码把1返回,接受参数类型会自动推导
}
Log.d("test", "$result")
}
//打印结果
2021-05-25 19:19:25.662 812-6239/com.example.myapplication D/test: launch
2021-05-25 19:19:25.665 812-6241/com.example.myapplication D/test: 1
runBlocking
创建顶级协程,它可以保证协程体内代码一定执行完。 以下代码,在runBlocking内代码没有执行完,最后一行打印是不会执行的,相当于等待协程体内执行完再继续逻辑。
println("start")
runBlocking {
delay(5 * 1000)
println("runBlocking")
}
println("end")
async
创建协程 对比withContext
- 作用域最后一行代码可以作为返回值;
- async可以做并发处理,而withContext不行; 以下代码,任务1和任务2的打印交替进行。注意:如果await不调用,协程体也会执行,是否有点颠覆想象。
val result1 = async(Dispatchers.IO) {//任务1
for (index in 0 until 10 * 1000) {
delay(500)
Log.i("test", "result2 $index")
}
}
val result2 = async(Dispatchers.IO) {//任务2
for (index in 0 until 10 * 1000) {
delay(500)
Log.i("test", "result2 $index")
}
}
result1.await()
result2.await()
async使用场景
使用async并发网络请求,合并请求做后续业务处理。
GlobalScope.launch(Dispatchers.Main) {
val asyn1 = async(Dispatchers.IO) {
//网络请求1
1
}
val asyn2 = async(Dispatchers.IO) {
val result1 = async(Dispatchers.IO) {
//网络请求2
2
}
}
val result1 = asyn1.await()
val result2 = asyn2.await()
//并发拿到请求1 请求2结果 切换到主线程去做其他业务处理
}
总结
- GlobalScope.launch 可以在任意代码位置创建顶级协程(默认在Dispatchers.Defatult线程池执行),返回值是Job,可以用于取消协程,其生命周期跟应用一致,注意内存泄露问题;
- withContext 创建协程,必须在挂起函数或者协程内创建,是挂起函数,会影响父协程,可以有返回结果;
- runBlocking 可以在任意代码位置创建顶部协程,可以保证协程体内的代码一定执行完;
- async 创建协程,可以有返回结果,可以用来处理并发任务;
- run、launch、async都是CoroutineScope的扩展函数,runBlocking是顶级函数。
以上分析有不对的地方,请指出,互相学习,谢谢哦!