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实现机制。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 转一篇文章 原地址:http://gank.io/post/560e15be2dca930e00da1083 前言...
    jack_hong阅读 943评论 0 2
  • 在正文开始之前的最后,放上GitHub链接和引入依赖的gradle代码: Github: https://gith...
    苏苏说zz阅读 698评论 0 2
  • 众所周知在android中当执行程序的耗时超过5秒时就会引发ANR而导致程序崩溃。由于UI的更新操作是在UI主线程...
    北铭阅读 25,467评论 11 40
  • 前言我从去年开始使用 RxJava ,到现在一年多了。今年加入了 Flipboard 后,看到 Flipboard...
    占导zqq阅读 9,202评论 6 151
  • 愿意每一天都是阳光灿烂 愿意每一幅都是山花烂漫 他画中的女儿 发梢扎根于土地 裙摆绽满青苔
    鱼半生阅读 224评论 2 3