美文网首页Kotlinkotlin
Kotlin 协程coroutine

Kotlin 协程coroutine

作者: androidfan | 来源:发表于2020-01-04 17:32 被阅读0次

    koltin 协程的定义

    官方描述:协程通过将复杂性放入库来简化异步编程。程序的逻辑可以在协程中顺序地表达,而底层库会为我们解决其异步性。该库可以将用户代码的相关部分包装为回调、订阅相关事件、在不同线程(甚至不同机器)上调度执行,而代码则保持如同顺序执行一样简单。
    简单理解:一般程序会有一个主进程,主进程中可能含有多个线程。而协程,是线程中的,也就是说一个线程中可能包含多个协程,协程与协程之间是可以嵌套的。
    精确奥义:在Kotlin里协程就是由官方提供的一套线程API

           //java里Excutor
            ExecutorService executorService = Executors.newCachedThreadPool();
            executorService.execute(new Runnable() {
                @Override
                public void run() {
    
                }
            });
         //java里线程
            new Thread(new Runnable() {
                @Override
                public void run() {
    
                }
            }).start();
         //Koltin里协程
            launch{
    
              }
    

    koltin 协程的作用是什么

    那既然java里有Thread 和Executor ,Android 里又增加了Handler和AsyncTask,并且现在还有了RxJava,那么协程的优势在呢?其实协程和以上所说的没有本质区别,但是借助了Kotlin的语言优势,更方便的实现对线程的操作,仅此而已。
    协程的作用简单来说,有以下两个方面

    • 协程通过替代回调(callback)来简化异步代码。
    • 通过提升 CPU 利用率,减少线程切换进而提升程序运行效率。

    案例1

      //非阻塞式挂起,用看似同步的方式写出异步的代码
            var userName = getUserInfo() //网路请求
            userTv?.text = userName.toString()
    

    上面的代码是不是看起来很清爽,在java里,实现网络请求,通常是用回调的,而我们对于回调也是早就习以为常,通常也是在回调方法里进行页面数据更新,当然这个也是根据不同的网络请求框架的。但是用了协成,就可以这样,上下顺序排列,简直完美。协程改变了并发任务的操作难度,更容易coder操作

    案例2

    再比如现在还有一个操作,根据业务需要,我们需要进行两个网络请求,在这两个网络请求都返回数据后,我们对数据进行加工组装,展示出来。
    对于我们来说,这两个网络请求是没有关联的,可以异步发起,但是很可能受到网络框架影响和知识影响,我们给写成了同步的,即:先进行网络请求1,在网络请求1成功的回到里拿到数据,在进行网络请求2,在网络请求2成功能的回调里进行数据汇总加工,由原来的并行变成串行,这种就导致页面加载速度降低了一倍但是,有了协程,就可以这样:

    
            //协程
         launch(Dispatchers.Main){
            var userInfo=async{getUserInfo()}   //网络请求
            var userToken=async{getUserToken()} //网络请求
            var userData=unDateUI(userInfo,userToken) //显示
    
    }
    

    协程就像非常轻量级的线程。线程是由系统调度的,线程切换或线程阻塞的开销都比较大。而协程依赖于线程,但是协程挂起时不需要阻塞线程,几乎是无代价的,协程是由开发者控制的。所以协程也像用户态的线程,非常轻量级,一个线程中可以创建任意个协程。协程很重要的一点就是当它挂起的时候,它不会阻塞其他线程。

    协程的特性

    • 可控制:协程能做到可被控制的发起子任务(协程的启动和停止都由代码控制,不像 java)
    • 轻量级:协程非常小,占用资源比线程还少
    • 语法糖:使多任务或多线程切换不再使用回调语法

    koltin 协程要怎么用

    当我们知道了协程的定义后,就可以了解到协成的使用场景,在需要执行耗时操作,可能引起阻塞时,(Android系统为了保证界面的流畅和及时响应用户的输入事件,主线程需要保持每16ms一次的刷新(调用 onDraw()函数),所以不能在主线程中做耗时的操作(比如 读写数据库,读写文件,做网络请求,解析较大的 Json 文件,处理较大的 list 数据)。 )就可以由原来的java子线程换成Kotlin协程,这样程序执行更加顺畅,值得注意的是,协成并不一定是在子线程的才可以用的,在主线程中也是可以实现的。

    原则 只要是耗时或者需要等待的操作,都可以使用协程

    需要使用到的库,要提起前导入

    //Kotlin协程依赖
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1"
    //切换到后台执行IO操作
       launch(Dispatchers.IO) {
                saveToDisk(data)
            }
    //切換到主线程
       launch(Dispatchers.Main) {
                undateUi(data)
            }
    **注意,launch 函数里一连串的代码片段统称为协程**
    

    如果只用launch函数,那么协程并不能做太多的事情,仅仅是切换或者指定线程,所以还要配合withContext()
    withContext()函数可以指定线程来执行代码,并且执行完毕后再切换回来,继续执行原先线程里的后续代码。

        //withContext()
           launch(Dispatchers.Main){
             var image=withContext(Dispatchers.IO){
                   saveImageToDisk(data)
               }
         userImage.setImageBitmap(image)
    }
    
    对于上面代码,可以解释成,在主线程里,我们启用了协程,并且指定在IO线程里操作,当操作完成后,会自动切换回主线程,进行页面的刷新操作
    

    由于由了自动切换的操作,所以在处理并发任务的时候,我们就可以优雅很多,甚至可以把WithContext单独抽取出来,放到函数里,包裹着实际操作的耗时代码,例如;

          launch(Dispatchers.Main){
             var image= saveImageToDisk(data)
             userImage.setImageBitmap(image)
    }
          suspend fun saveImageToDisk(data:String){
         withContext(Dispatchers.IO){
              save()
      }
    }
    
    

    但是如果直接这样写,是会报错的,因为withContext方法需要在协程里面被调用,所以saveImageToDisk函数前面要加上suspend 关键字,提醒调用者,我是一个耗时操作,我要在协程里被调用。
    suspend是kotlin协程中一个非常重要的关键字,当执行有被suspend关键字修饰的函数式,线程会被挂起,并且这个挂起是非阻塞式的,不会影响后面的继续执行。
    那么这个挂起的是什么?线程还是函数?
    都不是,挂起的是协程,也就是launch包裹的代码片段,也就是说,这个线程跟这个协程从此脱离了,不是线程暂停了。
    那么如果协程被挂起了,后续操作是怎么进行的呢?
    如果这个协程所在的线程是子线程,那么协程被挂起后,子线程继续执行自己的任务,要么继续,要么等待回收,如果这个协程所在的线程是主线程,那么协程被挂起后,主线程则是继续刷新页面。
    以上是对于线程来说的,那么对于协程来说呢?
    对于协程来说,当执行到suspend挂起函数时,协程的代码内容由withContext 指定的线程继续执行,执行完毕后在切换到原来线程,继续执行。
    可能有点绕,这点需要好好消化下。
    另外补充一下
    如果需要指定协程运行的线程,就需要指定Dispatchers ,常用的有三种:

    • Dispatchers.Main:Android中的主线程,可以直接操作UI
    • Dispatchers.IO:针对磁盘和网络IO进行了优化,适合IO密集型的任务,比如:读写文件,操作数据库以及网络请求
    • Dispatchers.Default:适合CPU密集型的任务,比如解析JSON文件,排序一个较大的list
      注意BaseActivity要继承 CoroutineScope by MainScope(),launch才能被识别

    所谓的挂起,其实本质上还是切换了一个线程
    到这里,我们就可以来总结下
    协程,本质上就是一个线程切换框架
    挂起,本质上是就是切换线程,
    协程的非阻塞式,和java里线程的非阻塞是一样,只不过更方便而已。

    好了,今天就学习到这里,协程还有很多知识需要掌握,大家good good study and day day up!

    相关文章

      网友评论

        本文标题:Kotlin 协程coroutine

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