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构建器启动的协程的结果。

相关推荐
2501_9159184127 分钟前
App 苹果 上架全流程解析 iOS 应用发布步骤、App Store 上架流程
android·ios·小程序·https·uni-app·iphone·webview
2501_9160074741 分钟前
苹果上架全流程详解,iOS 应用发布步骤、App Store 上架流程、uni-app 打包上传与审核要点完整指南
android·ios·小程序·https·uni-app·iphone·webview
PuddingSama2 小时前
Android 高级绘制技巧: BlendMode
android·前端·面试
2501_915921432 小时前
iOS App 性能监控与优化实战 如何监控CPU、GPU、内存、帧率、耗电情况并提升用户体验(uni-app iOS开发调试必备指南)
android·ios·小程序·uni-app·iphone·webview·ux
Digitally3 小时前
如何将视频从安卓手机传输到电脑?
android·智能手机·电脑
CV资深专家3 小时前
Android 相机框架的跨进程通信架构
android
前行的小黑炭3 小时前
Android :如何提升代码的扩展性,方便复制到其他项目不会粘合太多逻辑,增强你的实战经验。
android·java·kotlin
2501_915921433 小时前
前端开发工具有哪些?常用前端开发工具、前端调试工具、前端构建工具与效率提升工具对比与最佳实践
android·前端·ios·小程序·uni-app·iphone·webview
花菜会噎住3 小时前
MySQL 高级特性与性能优化:深入理解函数、视图、存储过程、触发器
android·mysql·函数·索引·视图
珠峰下的沙砾5 小时前
在kotlin中如何使用像java中的static
kotlin