Kotlin-协程简介

335 阅读2分钟
什么是协程框架

一套基于线程的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:

  1. withContext可以返回执行结果,而launch不行;
  2. 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

  1. 作用域最后一行代码可以作为返回值;
  2. 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是顶级函数。

以上分析有不对的地方,请指出,互相学习,谢谢哦!