学习扔物线进阶视频课程笔记。kotlin协程
切线程
为什么要切线程?不想卡住当前线程,需要在别的线程去并行执行任务;或者任务必须在UI线程做时,切换到UI线程执行,示例代码:
CoroutineScope(Dispatchers.IO).launch {
//do something
}
CoroutineScope(EmptyCoroutineContext).launch(Dispatchers.IO) {
//do something
}
调度器Dispatcher
Dispatchers默认提供4种调度器:
Default:处理计算密集型任务,线程池里面的线程数量等于CPU核心数,当线程数大于核心数后会影响CPU性能
IO:处理IO密集型任务(网络IO、磁盘IO,不占用CPU性能),线程池里面的线程数量远大于CPU核心数(固定64),IO工作进行期间CPU闲置
Default和IO的唯一区别就是线程池大小,按照规则把密集型任务放在Default线程池,把IO密集型任务放在IO线程池,可以有效提高处理性能
Main:会把任务通过handler传到UI线程处理
Unconfined:不进行限制,用它启动的代码直接执行,不切线程,挂起函数执行后也不会切回来
也可以自定义调度器线程池,使用newFixedThreadPoolContext(10,"name")
挂起函数
在协程里面执行挂起函数,函数会切到挂起函数指定的线程执行,而且执行完成后线程会自动再切回来,执行后面的代码。协程使用挂起函数可以消除回调,简化代码。挂起函数用关键字suspend修饰,挂起函数只能运行在协程中(因为挂起函数挂起的是协程),或者别的挂起函数中。
示例代码:
原理:挂起函数可以让开发人员在编码时用线性的方式编码,在编译过程中,kotlin协程将挂起函数前后代码拆分,抽象出一套状态机的机制,来实现线程的来回切换,在挂起函数的调用前后,都会进行一次状态的切换,状态机机制类似于嵌套,为代码如下:
Android中协程的常规使用
在实际的Android开发中,通常把协程启动在UI线程,在协程内部用挂起函数切换线程执行耗时任务,任务执行完后再切回UI线程。
通常使用jetpack提供的协程scope,lifecycleScope或者viewmodelScope,使用这两个scope,协程的生命周期和组件的生命周期绑定,在activity或者fragment结束时,会自动取消scope包含的所有协程;此外,这两个scope是用的Dispatcher是Main.immediate,当需要切换到主线程时,会先判断当前线程是否是主线程,如果不是主线程,才会调用handler抛任务,比直接使用Main效率高(Main不做判断,直接调用handler抛任务)。
launch、async、withContext和runBlocking
launch启动协程且并行执行,返回Job对象,Job对象的cancel取消协程,join方法表示等待协程执行结束,是挂起函数
async启动协程且并行执行,返回Deferred对象,Deferred是Job的子类,新增了一个挂起函数await,表示等待协程执行完成并获取执行后的结果。join和await比较,如果只是流程上的依赖,使用join,如果依赖结果,使用await,常用于多个并行任务执行后等待他们的执行结果。
withContext启动协程且串行执行,是一个挂起函数,执行结束后才会执行后面的代码
runBlocking会阻塞当前线程,并在当前线程运行,直到运行完成,他的所用是把挂起函数式的代码转换成阻塞式的代码,这样可以让线程写法的代码调用挂起函数。通常在协程代码和回调式函数的老代码兼容时使用,或者是一些测试代码中。
协程和回调式API
runBlocking可以把挂起函数式的代码转换成阻塞加回调的代码
使用suspendCoroutine或者suspendCancellableCoroutine可以将回调式函数转成挂起函数
continuation.resume()或者resumeWithExeception返回结果
retrofit源码中支持kotlin的实现,也是这样实现的