Kotlin-协程

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 以上方法创建的协程都可以指定执行的线程;

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容

  • 第一个协程 根据官方文档 可以了解到 以下例子: 上面的例子 运行后 : 为何直接打印“主线程执行结束”而没有执行...
    码农修行之路阅读 441评论 0 3
  • Why 简化异步代码的编写。 执行严格主线程安全确保你的代码永远不会意外阻塞主线程,并增强了代码的可读性。 提升代...
    熹哥阅读 557评论 0 3
  • 协程的定义 协程可以理解为一种轻量级的线程。协程和线程的区别是线程是依靠操作系统的调度才能实现不同线程之间的切换的...
    在下陈小村阅读 123评论 0 0
  • 构建器 runBlocking 顶层函数非挂起函数返回T,Lambda表达值最后一行 阻塞当前线程,会等待所有其中...
    AilurusFulgens阅读 481评论 0 3
  • 一、开启协程的方式 使用 runBlocking 顶层函数。它会 在协程作用域内的所有代码和子协程没有全部执行完成...
    335a40e49285阅读 1,702评论 0 1