Kotlin协程使用

如何开启一个协程:

首先想要调用协程方法,必须先定义一个协程作用域即协程生命周期,一般在kotlin中可以以下方式开启。

  • GlobalScope.launch 开启一个协程,该协程不会阻塞线程,如果线程结束协程会停止执行,该协程的作用域是整个应用运行时期。
  • runBlocking 阻塞线程到该协程执行结束。
    runBlocking 是常规函数,而 coroutineScope 是挂起函数
    实际上GlobalScope是CoroutineScope的子类,该类是所有协程构造器的父类,用于扩展不同范围的构造器,实际上我们可以扩展不同的CoroutineScope子类实现适应Android各类控件生命周期的范围协程构造器,这个原理会在后面研究,现在先看看在作用域内的协程使用。
协程作用域内的协程操作:

协程作用域也可以认为是一个顶层的协程,以下的操作符号可以在协程作用域内使用

  • launch 协程作用域也可以认为是一个顶层的协程,这个launch表示开启一个新的协程。
  • async 异步协程,该协程开启后不会阻塞,可以用于开启多个并行协程任务,也可以显示调用其await方法实现阻塞同步获取协程返回值。
  • withContext 该方法可以用于不创建新的协程而使用传入的协程上下文和调度器修改协程所处的运行线程。
对协程的同步、挂起:

所有的协程都都会返回一个Job对象,该对象可用于对协程任务操作。

  • join 阻塞当前的协程作用域直到Job对应的协程执行完毕
  • cancle 取消对应协程的进行
  • start 启动协程 如果协程未启动的话方法返回true,否则返回false

特殊的,对于async,由于是阻塞同步获取结果,其方法会返回Job的子类Deferred,该子类的await方法可不阻塞线程而等待协程的结果返回。

声明挂起函数
  • suspend:可用于修饰一个方法,表示该方法会运行在协程作用域,该方法可以被挂起不会阻塞线程,可以在该线程中使用协程作用域内的操作符号。

各种协程操作符的使用场景:

  • suspendCoroutine 在挂起函数中使用,用于将异步回调的值转化为同步值返回,在调用resume或resumeWithException前方法会挂起,直到结果返回。

Kotlin协程封装使用

一 、网络请求封装方法
  1. 请求方法封装:针对耗时任务,如网络请求,使用suspend声明可挂起。
private suspend fun delaySomeTime(): String {
       //模拟请求延时
       delay(100)
       return "suspend return"
   }
  1. 封装请求函数回调:使用函数参数传入任务,再回调Success方法函数中,第三个参数传入协程作用域。
fun <T> req(method: suspend () -> T, Success: (T) -> Unit, scope: CoroutineScope):Job {
       return scope.launch(Dispatchers.Main) {
           val result = withContext(Dispatchers.IO) {
               method()
           }
           Success(result)
       }
   }
  1. 使用:
    例如在Activity中使用,可以传入lifecycleScope(需要导入库lifecycle-viewmodel-ktx)
var job = req(
                { delaySomeTime() },//传入耗时方法,不会阻塞线程
                { tvResult.text = it },//成功回调结果
                lifecycleScope
           )
二、第二种网络封装方法:

在ViewModel中使用liveData获取suspend方法结果,suspedn方法中可以使用suspendCoroutine将结果回调封装成同步获得,通过liveData发送到下游。
1、使用suspendCoroutine封装回调为同步挂起:

suspend fun getData(): Int {
   return suspendCoroutine { continuation ->
       getInternetData(object : CallBack<Int> {
           override fun success(data: Int) {
               continuation.resume(data)
           }

           override fun error() {
               continuation.resumeWithException(Exception("error getData"))
           }
       })
   }
}

2、通过liveData延迟发射获取挂起值

fun getData2(): LiveData<Int> {
    return liveData(Dispatchers.IO) {
        emit(getData())
    }
}

3、在ViewModel中使用liveData注册下游

三、第三种网络请求封装方法:

将网络请求封装为async同步方法返回Deferred对象,再通过await方法同步获得结果
1、封装Deferred

fun getData3(): Deferred<Int> {
    val deferred = CompletableDeferred<Int>()
    getInternetData(object : CallBack<Int>{
        override fun success(data: Int) {
            deferred.complete(data)
        }
        override fun error() {
            deferred.completeExceptionally(Exception("error"))
        }
    })
    return deferred
}

2、在协程作用域中调用获得结果

getData3().await())