美文网首页
协程1- 协程的优势

协程1- 协程的优势

作者: Xigong | 来源:发表于2021-01-09 18:56 被阅读0次

    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 函数保证了异步代码具有和同步代码一样的执行顺序

    相关文章

      网友评论

          本文标题:协程1- 协程的优势

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