iKirby's Blog/

Kotlin Coroutines (协程) 简单使用

之前在闲暇时间自己学了一些 Android 相关的东西,写了一个简单的 IT之家 App ,用来代替日渐臃肿的官方 App (说实话 IT之家 官方 App 算是非常良心了,但是咱有点强迫症)。现在想起来最早写的代码真的是惨不忍睹,网络操作直接 new Thread() ,然后再 runOnUiThread() ,最后自己都看不下去了,换成了 RxJava (由于使用 Jsoup 直接获取电脑版页面并解析,就没有使用 Retrofit)。虽然看上去好了不少,但是 RxJava 里面很多东西我自己也没有搞懂,而且有时候会出现莫名其妙的异常(我把 App 分享给几个朋友之后,经常有人说遇到奇怪问题,但是我自己又测不出来问题,就很烦)。

今年暑假时候学了学 Kotlin ,把整个 App 全部使用 Kotlin 重写了。最近又了解了一下 Kotlin Coroutines ,感觉是个非常棒的东西,而且只用于网络操作的处理的话,比 RxJava 也更加轻量。于是这两天闲下来了,就把使用到 RxJava 的部分换成了 Coroutines ,不仅写起来更简单,代码看起来也更整洁更容易阅读。


Kotlin Coroutines 基于暂停的机制,相应方法被调用时会暂停程序执行,并在执行完成后继续程序的执行。这样就可以以有序的方式写异步代码,不需要搞乱七八糟的回调。它基本使用很简单,就拿我现在的用法来说吧。

需要注意的是,Kotlin Coroutines 还处于实验性阶段,不建议用于生产环境。更新:已经推出了 1.0.0 版本,需要配合 Kotlin 1.3 使用。

添加依赖

对于 Android 项目,在 build.gradle 中添加(最新版本可以在官方 GitHub 仓库看到)

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:0.26.1'

其中提供了在 Android 上对 UI 线程进行操作所需的 Dispatchers.Main Context 。

修改 API 接口

将方法的返回值修改为 Deferred<T> ,例如(我这个是获取登录后的 cookie ,所以返回值类型为 String)

interface UserApi {
    fun login(username: String, password: String): Deferred<String?>
}

然后修改对应的实现

override fun login(username: String, password: String): Deferred<String?> = GlobalScope.async {
    [email protected] try {
        // 调用封装好的登录方法
    } catch (e: IOException) {
        // 如果出现网络错误或登录失败,返回空值
        null
    }
}

这样用户登录所使用的方法就修改完成了。

修改 Activity 或 Dialog 中的代码

首先添加全局变量

private lateinit var job: Job

然后修改执行登录操作的代码

job = GlobalScope.launch(Dispatchers.Main) {
    // 调用这个方法会暂停它下面代码的执行,但不影响 UI 线程,登录方法执行完成后会才会继续执行下面的
    val cookie = UserApiImpl.login(username, password).await()
    if (cookie != null) {
        // 登录成功,返回了对应的 cookie ,进行进一步操作
    } else {
        // 返回的 cookie 为空,登录失败
    }
}

最后和生命周期绑定,以便用户退出对应 Activity 或 Dialog 时取消正在执行的操作(我这里是 DialogFragment)

override fun onDestroyView() {
    // 如果 job 已经被初始化了,说明对应操作执行了
    if (::job.isInitialized) {
        job.cancel()
    }
    super.onDestroyView()
}

这样就全部完成了。

如果要方便的管理多个 Job ,可以创建一个空的 Job 作为 parent

private val parentJob = Job()

然后使用

GlobalScope.launch(Dispatchers.Main + parentJob) {}

在 onDestroy() 方法中直接 cancel 这个 parent 就可以了

override fun onDestroy() {
    parentJob.cancel()
    super.onDestroy()
}

博客好久没更新了,说实话我也不知道写什么,于是就有了第一篇和技术相关的文章,虽然比较水吧... 还有... 顺便祝大家中秋快乐。

留下一条评论

共有 4 条评论

  1. CzBiX:

    我还在用老版本的 coroutines 库,新版本有些地方不兼容。

    2018-10-18 15:42 回复
  2. skylark:

    惊现dalao

    2018-10-25 08:05 回复
    1. Wate:

      刷个留言

      2018-10-30 00:53 回复