美文网首页
Kotlin-协程

Kotlin-协程

作者: 杨0612 | 来源:发表于2021-06-07 18:03 被阅读0次
1.什么是协程?

是一套基于线程的API框架,重点:还是基于线程。

2.协程有什么用?

可以灵活地切换线程,用同步的方式写出异步代码,解决回调地狱问题,重点:不用回调就可以处理异步任务返回。

3.协程作用域是什么?

以下代码,GlobalScope.launch大括号内就是协程作用域。

        GlobalScope.launch {//作用域
            Log.d("test", "1")
        }
        Log.d("test", "2")
4.如何使用协程?
4.1 GlobalScope.launch

创建顶级协程作用域,因为GlobalScope是单例,所以这样的协程生命周期跟应用一致,只有应用进程结束,协程才会结束,使用时需要注意内存泄露问题。
以下代码可以很轻松敌将for循环任务切换到另外的线程执行。

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        GlobalScope.launch {//作用域
            for (index in 1..1000) {//切换到另外的线程
                Log.d("test", "$index")
            }
        }
    }
解决内存泄露问题

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()
    }
4.2 launch

创建子协程,也就是在其他协程作用域内创建,例如在GlobalScope.launch内。子协程是不会影响父协程的执行,可以说这两个协程是并发的。

       GlobalScope.launch {
            launch {
                Log.d("test", "launch")
            }
            Log.d("test", " GlobalScope.launch")//不受被子协程的影响
        }
4.3 withContext

同样是创建子协程,与launch不同的是:
4.3.1 withContext可以返回执行结果,而launch不行;
4.3.2 withContext是一个suspend挂起函数,会影响父协程的执行;
以下代码,从打印日志可以看出,子协程执行完父协程才会继续执行,因为父协程执行到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
4.4 runBlocking

跟GlobalScope.launch一样,创建顶级协程,不过runBlocking可以保证作用域代码在线程结束前一定执行完。
以下代码,在runBlocking内代码没有执行完,线程最后一行打印是不会执行的。

        Thread {
            Log.i("----test", "thread start")
            runBlocking {
                delay(5 * 1000)
                Log.i("----test", "runBlocking")
            }
            Log.i("----test", "thread end")
        }.start()
4.4 async

创建子协程,与withContext相同,作用域最后一行代码可以作为返回值,不同的是,async可以做并发处理,而withContext不行,因为其是挂起函数会影响父协程的执行。
以下代码,任务1和任务2的打印交替进行。注意:如果任务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并发网络请求,合并请求做后续业务处理。

        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结果 切换到主线程去做其他业务处理
        }
5.挂起函数
5.1 什么是挂起?

简单的说,就是切线程,将函数切换到另外的线程执行,保存当前状态,以便恢复。

5.2 到底挂起的是什么?

挂起的不是线程,不是函数,而是协程,也就是协程作用域内的代码。当遇到挂起,那么suspend函数后的协程代码需要等待函数返回才会执行。

5.2 suspend的作用?

只是起一个提醒作用,没错,只是提醒作用。提醒什么呢?提醒函数的调用者,这个函数是耗时函数,必须在协程内执行,另外还提醒函数的创建者,函数必须执行耗时任务或者其他suspend函数,如果没有包含此内容,那么函数放在协程内执行就显得多余,编译器会提示suspend描述多余。

6.delay函数

也是一个挂起函数,将协程挂起,该函数后的代码将延迟执行。
delay是非阻塞式挂起,不会阻塞线程。
Thread.sleep()同样也有延迟执行的作用,但它会阻塞线程。
以下代码,在主线程做测试,Dispatchers.Main指定协程在主线程执行,delay延迟10秒,但是协程外同样是在主线程的for循环任务以及点击事件不受影响。
如果换成 Thread.sleep(10 * 1000),那么主线程的绘制、for循环任务以及点击事件都会收到影响。

        GlobalScope.launch(Dispatchers.Main) {
            delay(10 * 1000)//挂起线程,延迟执行后续操场
            if (Looper.getMainLooper() == Looper.myLooper()) {
                Log.i("test", "主线程");
            }
            Log.i("test", "Dispatchers.Main");
        }

        for (index in 0 until 1000) {//任务不会被堵塞
            Log.i("test", "$index");
        }

        testTv.setOnClickListener {//点击事件不会被堵塞
            Log.i("test", "setOnClickListener");
        }
7.总结

创建协程的方法有很多,这里只列举了部分,

7.1 GlobalScope.launch创建顶级协程,返回值是Job,可以用于取消协程,其生命周期跟应用一致,注意内存泄露问题;
7.2 launch创建子协程,返回值是Job,可以用于取消协程;
7.3 withContext创建子协程,是挂起函数,会影响父协程,可以有返回结果;
7.4 async创建子协程,可以有返回结果,可以处理并发;
7.5 以上方法创建的协程都可以指定执行的线程;

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

相关文章

  • Kotlin-协程

    开启线程的方式 使用 runBlocking 顶层函数。一般用于测试。 使用 GlobalScope 单例对象。也...

  • kotlin-协程

    Why 简化异步代码的编写。 执行严格主线程安全确保你的代码永远不会意外阻塞主线程,并增强了代码的可读性。 提升代...

  • Kotlin-协程

    协程的定义 协程可以理解为一种轻量级的线程。协程和线程的区别是线程是依靠操作系统的调度才能实现不同线程之间的切换的...

  • Kotlin-协程

    1.什么是协程? 是一套基于线程的API框架,重点:还是基于线程。 2.协程有什么用? 可以灵活地切换线程,用同步...

  • Kotlin - Lambda 表达式

    下一篇:Kotlin-协程[https://www.jianshu.com/p/ccb372840eec] Kot...

  • Kotlin-协程的取消关键技术分析

    Kotlin-协程的取消关键技术分析 RUN> ??????hello: 0hello: 1hello: 2hel...

  • Kotlin-协程基础

    第一个协程 根据官方文档 可以了解到 以下例子: 上面的例子 运行后 : 为何直接打印“主线程执行结束”而没有执行...

  • (转)Kotlin-协程

    上一篇:Kotlin - Lambda 表达式[https://www.jianshu.com/p/6899025...

  • Kotlin-协程-构建器

    构建器 runBlocking 顶层函数非挂起函数返回T,Lambda表达值最后一行 阻塞当前线程,会等待所有其中...

  • Kotlin-协程网络请求封装

    依赖 封装步骤 1.配置Retrofit、okhttp 2.请求数据转换 2.1 创建请求接口apiService...

网友评论

      本文标题:Kotlin-协程

      本文链接:https://www.haomeiwen.com/subject/cwjcsltx.html