Kotlin之协程(第一趴)

接下来我们将学习如何在Android应用中使用Kotlin协程。这是管理后台线程的推荐方法,可通过减少回调需求来简化代码。协程是一项Kotlin功能,可将长时间运行的任务(例如数据库或网络访问)的异步回调转换为顺序代码。

下面给出一个代码段,从中您可以大致了解将要进行的操作。

javascript 复制代码
// Async callbacks
networkRequest { request ->
    // Successful network request
    databaseSave(result) { row ->
        // Result saved
    }
}

系统使用协程将基于回调的代码转换为顺序代码。

scss 复制代码
// The same code with coroutines
val result = networkRequest()
// Successful network request
databaseSave(result)
// Result saved

您将从一款使用架构组件构建的现有应用入手,该应用为长时间运行的任务使用回调样式。

下载代码: 从命令行使用下列命令克隆 GitHub 代码库:

shell 复制代码
$ git clone https://github.com/googlecodelabs/kotlin-coroutines.git

首先,我们来看看起始示例应用。

  1. 如果已下载kotlin-coroutineszip文件,请将其解压。
  2. 在Android Studio中打开coroutines-codelab项目。
  3. 选择start应用模块。
  4. 点击Run按钮,然后选择模拟器或者链接必须能够运行的Android设备。此时应显示Kotlin协程屏幕:

在您点按屏幕后,此初始应用会使用线程在经过短暂延迟后增加计数。它还会从网络中提取新标题并将其显示在屏幕上。现在就试试看吧,您应该会看到计数和消息在短暂延迟后出现变换。

此应用使用架构组件将MainActivity中的界面代码与MainViewModel的应用逻辑分割开。

  1. MainActivity显示界面、注册点击监听器,并且可以显示Snackbar。它将事件传递给MainViewModel,并根据MainViewModel中的LiveData更新屏幕。
  2. MainViewModel处理onMainViewClicked中的事件,并将使用LiveDataMainActivity通信。
  3. Executor定义BACKGROUND,后者可以在后台线程上运行内容。
  4. TitleRepository从网络提取结果,并将结果保存到数据库。

向项目添加协程

要在Kotlin中使用协程,您必须在项目的build.gradle(Module: app)文件中添加coroutines-core库。

Android上的写成作为核心库和Android专用扩展函数提供:

  • kotlinx-coroutines-core用于在Kotlin中使用协程的主接口
  • kotlin-coroutines-android在协程中指出Android主线程。

此初始应用已在build.gradle中包含依赖项。创建新的应用项目时,您需要打开build.gradle(Module:app)并将写成依赖项添加到项目中。

arduino 复制代码
dependencies {
    ...
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:x.x.x"
    implementation "org.jetbranins.kotlinx-coroutines-android:x.x.x"
}

在Android上,避免阻塞主线程是非常必要的。主线程是一个处理所有界面更新的线程,也是调用所有点击处理程序和其他界面回调的线程。因此,主线程必须顺畅运行才能确保出色的用户体验。

为了避免用户在使用您的应用时感觉到任何卡顿,主线程必须每隔16毫秒或更短时间 更新一次屏幕,也就是每秒约60帧。许多常见任务所需的时间都比这个时间长,例如解析大型JSON数据集、将数据写入数据库或从网络提取数据。因此,从主线程调用此类代码可能会导致应用暂停、卡顿甚至冻结。如果您阻塞主线程太久,应用甚至可能会崩溃并显示一个应用无响应对话框。

回调模式

在不阻塞主线程的情况下执行长时间运行的任务的一种模式是回调。通过使用回调,您可以在后台线程上启动长时间运行的任务。任务完成后,系统会调用回调函数,以在主线程上告知您结果。

我们来看一个回调模式的示例:

kotlin 复制代码
// Slow request with callbacks
@UiThread
fun makeNetworkRequest() {
    // The slow network request runs on another thread
    slowFetch { result ->
        // When the result is ready, this callback will get the result
        show(result)
    }
    // makeNetworkRequest() exits after calling slowFetch without waiting for the result
}

由于此代码带有@UiThread注解,因此它必须足够快地运行以在主线程上执行,也就是说,它需要非常快地返回,以便下一次屏幕更新不会出现延迟。不过,由于slowFetch需要几秒钟甚至几分钟才能完成,因此主线程不能等待结果。show(result)回调允许slowFetch在后台线程上运行,并在准备就绪后返回结果。

使用协程移除回调

回调是一种很好的模式,但也存在缺点。过多使用回调的代码可能会变得难以读取和推演。此外,回调也不允许使用某些语言功能,例如异常。

Kotlin协程使您能够基于回调的代码转换为顺利代码。顺序编写的代码通常易于阅读,甚至可以使用异常等语言功能。

最后,两者所做的事情完全相同:等待长时间运行的任务获得结果,然后继续执行。不过,两者的代码看起来却截然不同。

关键字suspend是Kotlin将函数(即函数类型)标记为可供协程使用的方式。当协程调用标记为suspend的函数时,它不会像常规函数调用一样在函数返回之前进行阻塞,而是挂起 执行,直到结果就绪为止,然后从上次停止的位置恢复 并使用返回的结果。当它挂起并等待结果时,它会取消阻塞正在运行它的线程,以便其他函数或协程可以运行。

例如,在下面的代码中,makeNetworkRequest()slowFetch()都是suspend函数。

kotlin 复制代码
// Slow request with coroutines
@UiThread
suspend fun makeNetworkRequest() {
    // slowFetch is another suspend function so instead of 
    // blocking the main thread makeNetworkRequest will `suspend` until the result is
    // ready
    val result = slowFecth()
    // continue to execute after the result is ready
    show(result)
}
// slowFetch is main-safe using coroutines
suspend fun slowFetch(): SlowResul {...}

与回调版本一样,makeNetworkRequest必须立即从主线程返回,因为它被标记为@UiThread。这意味着,它通常无法调用slowFetch等阻塞方法。这里体现了suspend关键字的神奇之处。

bash 复制代码
重要提示:suspend关键字不指定运行代码的线程。挂起函数可以在后台线程或主线程上运行。

与基于回调的代码相比,协程代码可以利用更少的代码实现取消阻塞当前线程的相同效果。由于它具有顺序样式,因此可以轻松地链接多个长时间运行的任务,而无需创建多个回调。例如,如果代码从两个网络端点提取结果并将结果保存到数据库,此代码可以编写为协程中的函数,而无需回调。类似以下代码:

kotlin 复制代码
// Request data from network and save it to database with coroutines

// Because of the @WorkerThread, this function cannot be called on the 
// main thread without causing an error
@WorkerThread
suspend fun makeNetworkRequest() {
    // slowFetch and anotherFetch are suspend functions
    val slow = slowFetch()
    val another = anotherFetch()
    // save is the regular function and will block this thread
    database.save(slow, another)
}

// slowFetch is main-safe using coroutines
suspend fun slowFetch(): SlowResult { ... }
// anotherFetch is main-safe using coroutines
suspend fun anotherFetch(): AnotherResult { ... }

协程的其他名称

其他语言的asyncawait模式基于协程。如果您熟悉此模式,就会发现suspend关键字类似于async。但Kotlin中,调用suspend函数时,await()是隐式的。

Kotlin有一个Deferred.await()方法,用于等待一个通过async构建器启动的协程的结果。

相关推荐
一起搞IT吧2 小时前
相机Camera日志实例分析之五:相机Camx【萌拍闪光灯后置拍照】单帧流程日志详解
android·图像处理·数码相机
浩浩乎@2 小时前
【openGLES】安卓端EGL的使用
android
Kotlin上海用户组4 小时前
Koin vs. Hilt——最流行的 Android DI 框架全方位对比
android·架构·kotlin
zzq19964 小时前
Android framework 开发者模式下,如何修改动画过度模式
android
木叶丸4 小时前
Flutter 生命周期完全指南
android·flutter·ios
阿幸软件杂货间4 小时前
阿幸课堂随机点名
android·开发语言·javascript
没有了遇见5 小时前
Android 渐变色整理之功能实现<二>文字,背景,边框,进度条等
android
没有了遇见6 小时前
Android RecycleView 条目进入和滑出屏幕的渐变阴影效果
android
站在巨人肩膀上的码农6 小时前
去掉长按遥控器power键后提示关机、飞行模式的弹窗
android·安卓·rk·关机弹窗·power键·长按·飞行模式弹窗
呼啦啦--隔壁老王7 小时前
屏幕旋转流程
android