协程1- 协程的优势

1. 先看一个下载图片的示例

传统代码

// 1.创建一个子线程,下载图片
Thread{
    val bitmap = getImage("http://baidu.com/美女图片")
    // 2.图片下载完成,切换到主线程
    runOnUIThread{
        // 3.把图片设置到控件上
        imageView.bitmap = bitmap
    }
}.start()

协程代码

// 1. 通过scope 启动一个协程
GlobalScope.launch(Dispatchs.Main){
    // 2. 调用挂起函数,下载图片
    val bitmap = getImage("http://baidu.com/美女图片")
    // 3. 把图片设置到Ui上
    imageView.bitmap = bitmap
}

可以看到协程的代码没有回调嵌套,可读性更好

getImage函数的实现

suspend fun getImage(url:String):Bitmap {
    // 切换到IO线程
    return Dispathchs.IO { 
        // 下载图片
        HttpUtil.get(url).toBitmap() 
    }
}

2.下面讲讲协程的优势

2.1. 提升代码可读性,解决Callback hell问题

看一个弹对话框的例子

业务要求:

先弹出一个权限提示的对话框,

关闭后,再弹出一个登录的对话框

关闭后,再弹出一个提示签到的对话框

传统代码

Dialog("1.提示申请权限")
.doOnDismiss{
        Dialog("2.提示登录")
        .doOnDismiss{
                Dialog("3.提示签到")
                .doOnDismiss{
                    
                }
                .show()
        }
        .show()
}
.show()

协程版本

GlobalScope(Dispatchs.Main){
    showDialog("提示申请权限")
    showDialog("提示登录")
    showDialog("提示签到")
}

showDialog代码

suspend fun showDialog(title:String) = suspendCoroutine{cont->
     Dialog(title)
    .doOnDismiss{
        cont.resume(Unit)
    }
    .show()
}

通过回调转supsend函数的操作,让原来的回调嵌套代码变的线性

2.2. 保证函数的线程安全,并保证调用顺序

这是一个显示加载对话框的函数

普通版本

fun showLoading(context:Context){
    LoadingDialog(context).show()
}

showLoading只能在主线程被调用,在其他的线程被调用会导致崩溃,因为只有主线程才能操作UI

比较通用的解决办法是这样

fun showLoading(context:Context){
    // 增加线程检查
    if(!isMainThread())
        throw NotMainThreadException()
    }
    LoadingDialog(context).show()
}

这种fail-fast 的实践,测试排查错误很有用,但是有时候也会有漏网之鱼,造成线上崩溃。

也可以这样改

fun showLoading(context:Context){
    runOnUIThread {
         LoadingDialog(context).show()
    }
}

这样虽然也可以解决问题,让dialog,一定在主线程弹出,但是会有调用顺序的问题

showLoading(context)
println("dialog显示出来了")

期望是先弹出dialog,再打印日志,实际上是先打印日志再弹出dialog。

协程版本


suspend fun showLoading(context:Context){
    Dispatchs.Main{
        LoadingDialog(context).show()
    }
}

GlobalScope.launch{
    showLoading(context)
    println("dialog显示出来了")
}

协程版本有三项保证:

  1. 保证了dialog一定是在主线程弹出的
  2. showLoading函数是可以在任意线程被调用的
  3. 保证了先弹出dialog,再打印日志,也就是方法执行顺序

到这里,你可以会较真,我用锁也可以实现,CountDownLatch不就可以。

fun showLoading(context:Context){
    val latch = CountDownLatch(1)
    runOnUIThread {
         LoadingDialog(context).show()
         latch.countDown()
    }
    latch.await()
}

这样不就可以了,也实现了三项保证

但是,你阻塞了线程,kotlin是挂起,挂起后,调用showloading的线程还可以执行别的任务,如果是阻塞,就不可以了,这就是协程相比线程的性能优势。如果这里面没懂,没关系,本文的第3部分还会再讲解这点。

并不是其他线程框架做不到三项保证,同时保证性能,比如Rxjava就可以

再来个Rxjava版本的

fun showLoading(context:Context):Completable{
    return Completable
            .fromAction{ LoadingDialog(context).show() }
            .SubscribeOn(AndroidSchedulers.Main)
}

fun test(){
    showLoading(context)
    .subscribe{
        println("dialog显示出来了")
    }
}

这样也可以实现和协程一样的效果,具有三项保证,且不阻塞线程,但是代码可读性就比较差了

对比可以发现:

  • Rxjava实现的版本具有三项保证,并且没有阻塞线程,但是代码极度不直观。
  • CountDownLatch实现的版本也具有三项保证,但是阻塞了线程,性能不如Rxjava的版本
  • Coroutine实现的版本具有三项保证,不阻塞线程,而且代码比其他两种更加直观

2.3 挂起比阻塞高效

挂起的本质是基于事件循环机制,让线程离开当前函数,去执行别的函数,合适的时候再回到这里来执行

阻塞的本质,就是卡住线程

不是说使用协程比使用线程要高效,而是如果做相同的事情用到的线程越少,越高效。

所以不阻塞,就可以在挂起的这段时间,让线程干别的事情,这样就比阻塞使用的线程小,就更高效了。


所谓高效,指的是协程,非阻塞,提高了线程的利用率。

3. 总结一下协程的优势

  • 代码直观: 回调变为suspend 函数,有效解决了回调嵌套问题,且比Rxjava的观察者模式更易懂
  • 线程安全: suspend 函数可以在任意线程执行,内部通过withContext()切换到指定的线程
  • 高性能:通过挂起而不是阻塞,利用较少的线程,可以执行更多的任务。
  • 代码执行顺序: 通过suspend 函数保证了异步代码具有和同步代码一样的执行顺序
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容