kotlin协程系列 二 :异步任务的实现方式

开发过程中,有时候需要从网络上下载数据,并刷新界面。

fun init(){
  val userInfo = fetchUserInfo() // 网络请求
  refreshUI(userInfo) // 刷新UI
}

fetchUserInfo是比较耗时的操作,会一直阻塞当前线程直到数据返回。

在android项目中,为了避免阻塞UI线程造成anr,都是新开线程去执行耗时的操作,获取到执行结果后返回UI线程继续执行余下的操作。

修改后的代码:
发起网络请求时注册callBack方法,当完成网络请求后,在UI线程回掉callBack方法。

fun init(){
  fetchUserInfo(callBack = { userInfo ->
      refreshUI(userInfo) // 刷新UI
  }) // 网络请求
}

这样可以避免anr的产生。但是一些复杂的情况下容易产生“callBack hell”,比如网络请求返回后,先存入本地数据库(耗时操作),在进行UI刷新操作。

fun init(){
  fetchUserInfo(callBack = { userInfo ->
      saveUserInfo(userInfo){  userInfo ->
          refreshUI(userInfo) // 刷新UI
      } 
  }) // 网络请求
}

callback层层嵌套,代码将会横向扩展,十分臃肿。

为了解决代码横向扩展带来的问题,可以借助RxJava,使横向扩展转变为纵向扩展。

fun init() {
    saveUserInfo(userInfo)
                 .flatMap{ userInfo ->
                    saveUserInfo(userInfo)
                 }.subscribe{ userInfo ->
                    refreshUI(userInfo) 
                 }
}

但是Rxjava学习成本高昂,其次代码变得不直观明朗,一眼望去全是flatMap等操作符。而且RxJava是单参数传递,如果我想同时传递两个参数,必须新建一个包装类。

以上两种方式都不是完美的解决方法。我们的终极目标是以写同步代码的方式书写异步代码。

就像这样

fun init(){
  val userInfo = fetchUserInfo() // 网络请求
  refreshUI(userInfo) // 刷新UI
}

当然上边只是理想情况,我们需要让编译器知道哪些代码是异步的,是需要后台执行的,需要和同步代码区分开来。

kotlin的最终实现方案:

fun init(){
  launch {
    val userInfo = async {
        fetchUserInfo() // 网络请求
    }
    refreshUI(userInfo.await()) // 刷新UI 
  }
}

launch创造了一个协程执行的上下文,async代码块包裹的是需要异步执行的任务,当执行userInfo.await()时,代码会暂停执行(但是不阻塞UI线程),直到网络请求返回后再执行refreshUI的操作。

下一篇会详细介绍kotlin coroutines实现机制。

你可能感兴趣的:(kotlin协程系列 二 :异步任务的实现方式)