【译】第一次走进 Android 中的 Kotlin 协程

简介: 本文讲的是【译】第一次走进 Android 中的 Kotlin 协程,协程是 Kotlin 1.1 引入的最牛逼的功能。他们确实很棒,不但很强大,而且社区仍然在挖掘如何使他们得到更加充分的利用。
本文讲的是【译】第一次走进 Android 中的 Kotlin 协程,

本文提取并改编自最近更新的 Kotlin for Android Developers 一书。

协程是 Kotlin 1.1 引入的最牛逼的功能。他们确实很棒,不但很强大,而且社区仍然在挖掘如何使他们得到更加充分的利用。

简单来说,协程是一种按序写异步代码的方式。你可以一行一行地写代码,而不是到处都有乱七八糟的回调。有的还将会有暂停执行然后等待结果返回的能力。

如果你以前是 C# 程序员,async/await 是最接近的概念。但是 Kotlin 中的协程功能更强大,因为他们不是一个特定想法的实现,而是一个语言级别的功能,可以有多种实现去解决各种问题

你可以编写自己的实现,或者使用一个 Kotlin 团队和其他独立开发者已经构建好的实现。

你要明白协程在 Kotlin 1.1 中是一个实验性的功能。这意味着当前实现在将来可能会改变,尽管旧的实现仍将被支持,但你有可能想迁移到新的定义上。如我们稍后将见,你需要去选择开启这个特性,否则在使用的时候会有警告。

这也意味着你应该将本文视为一个(协程)可以做些什么的示例而不是一个经验法则。未来几个月可能会有很大变动。


理解协程如何工作

本文旨在让你了解一些基本概念,会用一个现有的库,而不是去自己去实现一个。但我认为重要的是了解一些内部原理,这样你就不会盲目使用了。

协程基于暂停函数的想法:那些函数被调用之后可以终止(程序)执行,一旦完成他们自己的任务之后又可以让他(程序)继续执行。

暂停函数用保留关键字 suspend 来标记,而且只能在其他暂停函数或协程内部被调用。

这意味着你不能随便调用一个暂停函数。需要有一个包裹函数来构建协程并提供所需的上下文。类似这样的:

fun <T> async(block: suspend () -> T)

我并不是在解释如何实现上述方法。那是一个复杂的过程,不在本文范围内,并且大多情况下已经有多种实现好的方法了。

如果你确实有兴趣实现自己的,你可以读一下 coroutines Github 中所写的规范。你仅需要知道的是:方法名字可以随意取,至少有一个暂停块做为参数。

然后你可以实现一个暂停函数并在块中调用:

suspend fun mySuspendingFun(x: Int) : Result {
 …
}

async {
 val res = mySuspendingFun(20)
 print(res)
}

协程是线程吗?不完全是。他们的工作方式相似,但是(协程)更轻量、更有效。你可以有数以百万的协程运行在少量的几个线程中,这打开了一个充满可能性的世界。

使用协程功能有三种方式:

  • 原始实现:意思是创建你自己的方式去使用协程。这非常复杂并且通常不是必要的。
  • 底层实现: Kotlin 提供了一套库,解决了一些最难的部分并提供了不同场景下的具体实现,你可以在 kotlinx.coroutines 仓库中找到这些库,比如说: one for Android 。
  • 高级实现:如果你只是想要一个可以提供一切你所需的解决方案来开始马上使用协程的话,有几个库可以使用,他们为你做了所有复杂的工作,并且(库的)数量在持续增长。我推荐 Anko,他提供了一个可以很好的工作在 Android 上的方案,有可能你已经很熟悉了。

使用 Anko 实现协程

自从 0.10 版本以来,Anko 提供了两种方法以在 Android 上使用协程。

第一种与我们在上面的例子中看到的非常相似,和其他的库所做的也类似。

首先,你需要创建一个可以调用暂停函数的异步块

async(UI) {
 …
}

UI参数是 async 块的执行上下文。

然后你可以创建在后台线程中执行的块,将结果返回给UI线程。那些块以 bg 方法定义:

async(UI) {
 val r1: Deferred<Result> = bg { fetchResult1() }
 val r2: Deferred<Result> = bg { fetchResult2() }
 updateUI(r1.await(), r2.await())
}

bg 返回一个 Deferred 对象,这个对象在 await() 方法被调用后会暂停协程,直到有结果返回。我们将在下面的例子中采用这种方案。

正如你可能知道的,由于 Kotlin 编译器能够推导出变量类型,因此可以更加简单:

async(UI) {
 val r1 = bg { fetchResult1() }
 val r2 = bg { fetchResult2() }
 updateUI(r1.await(), r2.await())
}

第二种方法是利用与特定子库中提供的监听器的集成,这取决于你打算使用哪个监听器。

例如,在 anko-sdk15-coroutines 中有一个 onClick 监听器,他的 lambda 实际上是一个协程。这样你就可以在监听器代码块上立即使用暂停函数:

textView.onClick {
 val r1 = bg { fetchResult1() }
 val r2 = bg { fetchResult2() }
 updateUI(r1.await(), r2.await())
}

如你所见,结果与之前的很相似。只是少了一些代码。

为了使用他,你需要添加一些依赖,这取决于你想要使用哪些监听器:

compile “org.jetbrains.anko:anko-sdk15-coroutines:$anko_version”
compile “org.jetbrains.anko:anko-appcompat-v7-coroutines:$anko_version”
compile “org.jetbrains.anko:anko-design-coroutines:$anko_version

在示例中使用协程

这本书所解释的例子(你可以在这里找到)中,我们创建了一个简单的天气应用。

为了使用 Anko 协程,我们首先需要添加这个新的依赖:

compile “org.jetbrains.anko:anko-coroutines:$anko_version

接下来,如果你还记得,我曾经告诉过你需要选择使用这个功能,否则就会出现警告。要做到这一点(使用协程功能),只需要简单地在根文件夹下的 gradle.properties 文件(如果不存在就创建)中添加这一行:

kotlin.coroutines=enable

现在,你已经准备好开始使用协程了。让我们首先进入详情 activity 中。他只是使用一个特定的命令调用了数据库(用来缓存每周的天气预报数据)。

这是生成的代码:

async(UI) {
    val id = intent.getLongExtra(ID, -1)
    val result = bg { RequestDayForecastCommand(id)
        .execute() }
    bindForecast(result.await())
}

太棒了!天气预报数据是在一个后台线程中请求的,这多亏了 bg 方法,这个方法返回了一个延迟结果。那个延迟结果在可以返回前会一直在 bindForecast 调用中等待。

但并不是一切都好。发生了什么?协程有一个问题:他们持有一个 DetailActivity 的引用,如果这个请求永不结束就会内存泄露

别担心,因为 Anko 有一个解决方案。你可以为你的 activity 创建一个弱引用,然后使用那个弱引用来代替:

val ref = asReference()
val id = intent.getLongExtra(ID, -1)

async(UI) {
 val result = bg { RequestDayForecastCommand(id).execute() }
 ref().bindForecast(result.await())
}

在 activity 可用时,弱引用允许访问 activity,当 activity 被杀死,协程将会取消。需要仔细确保的是所有对 activity 中的方法或属性的调用都要经过这个 ref 对象。

但是如果协程多次和 activity 交互的话会有点复杂。例如,在 MainActivity 使用这个方案将变得更加复杂。

这个 activity 将基于一个 zipCode 来调用一个端点来请求一周的天气预报数据:

private fun loadForecast() {

val ref = asReference()
 val localZipCode = zipCode

async(UI) {
 val result = bg { RequestForecastCommand(localZipCode).execute() }
 val weekForecast = result.await()
 ref().updateUI(weekForecast)
 }
}

你不能在 bg 块中使用 ref() ,因为在那个块中的代码不是一个暂停上下文,因此你需要将zipCode 保存在另一个本地变量中。

老实说,我认为泄露 activity 对象 1-2 秒没那么糟糕,不过有可能不能成为样板代码。因此如果你能确保你的后台处理不会永远不结束(比如,为你的服务器请求设置一个超时)的话,不使用 asReference() 也是安全的。

这样的话,MainActivity 将变得更加简单:

private fun loadForecast() = async(UI) {
 val result = bg { RequestForecastCommand(zipCode).execute() }
 updateUI(result.await())
}

综上,你已经可以一种非常简单的同步方式来写你的异步代码。

这些代码非常简单,但是想象一下复杂的情况:后台操作的结果被下一个后台操作使用,或者当你需要遍历列表并为每一项都执行请求的时候。

所有一切都可以写成常规的同步代码,写起来、维护起来将更加容易。


关于如何充分利用协程还有很多需要学习。如果你有更多相关的经验,请评论以让我们更加了解协程。

如果你刚刚开始学习 Kotlin ,你可以看看我的博客这本书,或者关注我的 TwitterLinkedIn 或者 Github 。





原文发布时间为:2017年7月10日

本文来自云栖社区合作伙伴掘金,了解相关信息可以关注掘金网站。
目录
相关文章
|
16天前
|
移动开发 Java Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
【4月更文挑战第3天】在移动开发领域,性能优化一直是开发者关注的焦点。随着Kotlin的兴起,其在Android开发中的地位逐渐上升,但关于其与Java在性能方面的对比,尚无明确共识。本文通过深入分析并结合实际测试数据,探讨了Kotlin与Java在Android平台上的性能表现,揭示了在不同场景下两者的差异及其对应用性能的潜在影响,为开发者在选择编程语言时提供参考依据。
|
17天前
|
数据库 Android开发 开发者
构建高效Android应用:Kotlin协程的实践指南
【4月更文挑战第2天】随着移动应用开发的不断进步,开发者们寻求更流畅、高效的用户体验。在Android平台上,Kotlin语言凭借其简洁性和功能性赢得了开发社区的广泛支持。特别是Kotlin协程,作为一种轻量级的并发处理方案,使得异步编程变得更加简单和直观。本文将深入探讨Kotlin协程的核心概念、使用场景以及如何将其应用于Android开发中,以提高应用性能和响应能力。通过实际案例分析,我们将展示协程如何简化复杂任务,优化资源管理,并为最终用户提供更加流畅的体验。
|
26天前
|
调度 数据库 Android开发
构建高效Android应用:Kotlin协程的实践与优化
在Android开发领域,Kotlin以其简洁的语法和平台友好性成为了开发的首选语言。其中,Kotlin协程作为处理异步任务的强大工具,它通过提供轻量级的线程管理机制,使得开发者能够在不阻塞主线程的情况下执行后台任务,从而提升应用性能和用户体验。本文将深入探讨Kotlin协程的核心概念,并通过实例演示如何在实际的Android应用中有效地使用协程进行网络请求、数据库操作以及UI的流畅更新。同时,我们还将讨论协程的调试技巧和常见问题的解决方法,以帮助开发者避免常见的陷阱,构建更加健壮和高效的Android应用。
35 4
|
18天前
|
Java Android开发 开发者
构建高效Android应用:Kotlin协程的实践与优化
在响应式编程范式日益盛行的今天,Kotlin协程作为一种轻量级的线程管理解决方案,为Android开发带来了性能和效率的双重提升。本文旨在探讨Kotlin协程的核心概念、实践方法及其在Android应用中的优化策略,帮助开发者构建更加流畅和高效的应用程序。通过深入分析协程的原理与应用场景,结合实际案例,本文将指导读者如何优雅地解决异步任务处理,避免阻塞UI线程,从而优化用户体验。
|
23天前
|
Java 编译器 Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
在开发高性能的Android应用时,选择合适的编程语言至关重要。近年来,Kotlin因其简洁性和功能性受到开发者的青睐,但其性能是否与传统的Java相比有所不足?本文通过对比分析Kotlin与Java在Android平台上的运行效率,揭示二者在编译速度、运行时性能及资源消耗方面的具体差异,并探讨在实际项目中如何做出最佳选择。
17 4
|
1天前
|
移动开发 Android开发 开发者
构建高效Android应用:采用Kotlin进行内存优化的策略
【4月更文挑战第18天】 在移动开发领域,性能优化一直是开发者关注的焦点。特别是对于Android应用而言,由于设备和版本的多样性,确保应用流畅运行且占用资源少是一大挑战。本文将探讨使用Kotlin语言开发Android应用时,如何通过内存优化来提升应用性能。我们将从减少不必要的对象创建、合理使用数据结构、避免内存泄漏等方面入手,提供实用的代码示例和最佳实践,帮助开发者构建更加高效的Android应用。
5 0
|
12天前
|
移动开发 API Android开发
构建高效Android应用:探究Kotlin协程的优势与实践
【4月更文挑战第7天】 在移动开发领域,性能优化和应用响应性的提升一直是开发者追求的目标。近年来,Kotlin语言因其简洁性和功能性在Android社区中受到青睐,特别是其对协程(Coroutines)的支持,为编写异步代码和处理并发任务提供了一种更加优雅的解决方案。本文将探讨Kotlin协程在Android开发中的应用,揭示其在提高应用性能和简化代码结构方面的潜在优势,并展示如何在实际项目中实现和优化协程。
|
26天前
|
移动开发 Android开发 开发者
构建高效Android应用:探究Kotlin协程的优势
在移动开发领域,尤其是针对Android平台,性能优化和流畅的用户体验始终是开发者追求的核心目标。近年来,Kotlin语言凭借其简洁性和功能性成为Android开发的新宠,特别是Kotlin协程的引入,为编写异步代码提供了一种全新的范式。本文将深入探讨Kotlin协程在Android应用开发中的应用及其带来的优势,旨在帮助开发者理解并运用协程来提高应用的性能和响应性。
|
26天前
|
存储 数据库 Android开发
构建高效Android应用:Kotlin协程的全面应用
在现代Android开发中,Kotlin协程作为一种优化异步编程和提升应用性能的强大工具,已经受到越来越多开发者的关注。本文将深入探讨Kotlin协程的核心概念、设计原理以及如何在实际Android项目中实现高效的协程应用。我们将从基础出发,逐步揭示协程如何简化线程管理,提供流畅的代码结构,并通过实际案例分析其在网络请求、数据库操作和UI响应中的应用效果。
|
29天前
|
安全 Android开发 开发者
构建高效Android应用:Kotlin与协程的完美结合
【2月更文挑战第30天】在移动开发领域,性能优化和流畅的用户体验是关键。本文深入探讨了如何通过结合Kotlin语言和协程技术来提升Android应用的性能和响应能力。我们将分析Kotlin的优势,介绍协程的基本概念,并通过实际案例展示如何在应用中实现协程以简化异步编程,从而提供更加高效的解决方案。