如何用Kotlin Coroutines和Architecture Components进行Android

如何用Kotlin Coroutines和Architecture Components进行Android

我们对这种新的可能性感到兴奋无比,但却很难真的投入大量时间重新编写原有应用以充分发挥新型编程风格带来的种种潜力。然而,如果我们从全新项目起步,结果又会如何?我们能够向其中引入哪些突破性的思维?哪些解决方案可以带来理想的稳定性?我们是否应该广泛使用RxJava,并将响应式至上思路作为应用的结构指导?

Cycle.js库(来自André Staltz)对于响应式至上思维作出了很好的诠释: Cycle.js — Streams

Rx具备良好的可组合特性,因此拥有巨大的发展潜力,然而,其与常规的面向对象编程风格又存在着显著区别。事实上,对于毫无RxJava使用经验的开发人员而言,其确实存在着难以理解的问题。

在开始新项目之前,我们面临着更多需要回答的问题。举例来说:

  • 我们应该利用Kotlin替代Java吗?(简单来讲,答案是肯定的。)

  • 我们应该使用实验性的Kotlin Coroutines吗?(其带来了全新的编程风格。)

  • 我们是否该使用谷歌提供的实验性库:Android Architecture Components?

要为这些问题找到答案,我们需要首先创建一款小小应用,从而做出明智的决定。而这正是本文的内容所在——依托于整个流程得出有用的见解。如果大家希望了解更多细节,咱们马上进入主题。

关于这款应用

这次实验的目标在于创建一款应用,其能够下载用户所选定城市的相关天气数据,并将预报结果通过图形化图表的形式显示出来(再配上一些酷炫的动画)。要求很简单,但其中已经囊括了各类Android项目中会涉及到的大多数功能。

事实证明,coroutinesarchtiecture components确实能够良好协作,并给我们带来能够顺利解决大量问题的整洁应用架构。

Cortoutines帮助我们以自然简洁的方式表达思路。如果大家希望代码能够逐行体现您所希望执行的确切逻辑(即使您可能也需要在其间进行一些异步调用),suspendable函数也能很好地完成任务。

另外需要强调的是:无需在回调之间往来跳转。在本示例应用当中,coroutines还彻底消除了对RxJava的依赖性。配合suspendable点的各函数在阅读及理解难度方面远低于部分RxJava运算符链——这些链可以快速实现函数化转换。

话虽如此,但我个人认为并不是在每个用例中都能将RxJava替换为coroutines。我们发现,observalbes就是一类无法被逐一映射为suspendable函数的表达类型。特别是如果observable运算符链允许多个事件从其中流经,且其中每个suspendable点只能在每次调用时恢复一次,则无法实现一对一映射。

回到我们这款天气应用中来:您可以查看其运作效果——但请注意,我并不是设计师,所以相关成果可能比较简陋。

图表动画显示您能够轻松利用简单的cortoutine以手动方式加以实现——其中不涉及任何ObjectAnimators、Interpolators、Evaluators或者PropertyValuesHolders等等。

最重要源代码片段已经展示如下。不过如果您希望查看完整项目,请参阅 GitHub

代码量并不大,相信大家能够轻松完成浏览。

我将从网络层开始介绍这款应用的具体结构。接下来,我会探讨业务逻辑(在MainModel.kt文件中),其几乎不受限于Android系统平台。最后,则是UI部分(这显然仅适用于Android系统)。

为了方便起见,我在这里为总体架构图添加了文本参考编号。我会特别关注其中绿色元素——即suspendable函数与actors(一个actor实际上就是一种非常实用的coroutine builder)。

总体来讲,actor模型属于一种并行计算数学模型——我将在下一篇博文中就此展开详细探讨。

如何用Kotlin Coroutines和Architecture Components进行Android

01天气服务

这项服务负责从Open Weather Map REST API处下载特定城市的天气预报数据。

在这里,我使用了一套来自Square的简单但强大的库——Retrofit。我猜如今的Android开发人员不会没听说过它,但对于那些尚未接触过的朋友,这里再解释几句:这是一套非常流行的Andoird平台HTTP客户端。其能够面向POJO执行网络调用与响应解析。这里我们直接使用典型的Retrofit配置。我还插入了Moshi转换器将JSON响应转换为数据类。

如何用Kotlin Coroutines和Architecture Components进行Android

这里需要强调的一点是,我将由Retrofit生成的函数类型返回至另一新函数: Call

我利用 Call.enqueue(回调)以面向Open Weather Map实际执行调用。这里我并没有使用由Retrofit提供的任何调用适配工具,这是为了保证自己能够将Call对象打包在suspendable函数当中。

02实用工具

从这里,我们正式迈入了全新的coroutines世界:我们希望创建一条打包有Call对象的suspendable函数

要完成这部分内容,您需要掌握coroutines的一部分基础知识。如果您尚不了解,请参阅《Coroutines指南》的第一章内容(由Roman Elizarov撰写)。

这将是一条扩展函数:suspend funCall.await(),其负责调用Call.enqueue(…)(以实际执行网络调用),而后调用suspends,再然后是resumes(当响应返回时)。

如何用Kotlin Coroutines和Architecture Components进行Android

要将任意异步计算转化为suspendable函数,我们需要使用The Kotlin标准库当中的suspendCoroutine函数。其能够为我们提供一个Continuation对象,这属于一类通用回调。我们只需要在对新的suspendable函数进行恢复时(通常是在出现异常的情况下),调用其resume方法(或者resumeWithException方法)即可。

下一步是使用我们的新suspend funCall.await()函数,其负责将由Retrofit生成的异步函数转换为便捷的suspendable函数

03 Repository

Repository对象属于我们在应用中需要显示的数据(图表)的实际来源。

如何用Kotlin Coroutines和Architecture Components进行Android

在这里,我们会使用一些由suspend funCall.await()扩展生成的专用suspendable函数,用以实现天气服务功能。如此一来,其返回的将全部为可立即使用的Forecast预报等数据,而非Call。接下来,我们在一条公共suspendable函数中使用这些数据:suspendfun getCityCharts(city:String):List。其能够将来自API的数据转化为可随时显示的图表清单。这里我使用List上的部分定制化扩展属性以将数据实际转换至List。需要强调的是,只有suspendable函数能够调用其它suspendable函数

为了简单起见,我们这里对此appid进行硬编码。如果大家希望对应用进行测试,请点击此处生成新的appid——这是因为如果此硬编码appid被多人频繁使用,则会自动被屏蔽24小时。

在下一步中,我们将创建主应用模型(采用Android ViewModel架构组件),其利用一个actor(coroutine builder)以实现应用逻辑。

04 模型

在这款应用中,我们只使用一套简单的模型: MainModel : ViewModel,且仅供一种活动使用: MainActivity

如何用Kotlin Coroutines和Architecture Components进行Android

这个类代表着应用本身。其将由我们的活动(实际上是由Android系统的ViewModelProvider)进行实例化,但能够在配置变更(例如屏幕旋转)后继续存在,且保证新的活动实例仍拥有相同的模型实例。我们完全不必担心活动生命周期问题。相较于实现与各方法(onCreate、onDestroy等)相关的活动生命周期,这里我们只拥有一项onCleared()方法,其会在用户退出此应用时进行调用。

更确切地讲,当活动finished时,onCleared方法将得到调用。

尽管并没有与活动生命周期紧密结合,我们仍然需要采取某种方式以发布应用模型的当前状态,从而将其显示在某些特定位置(在活动中)。在这方面,LiveData 能够发挥良好作用。

LiveData类似于对RxJava BehaviorSubject的二次创造……其拥有一项observable可变值。其最重要的差异体现在订阅方式以及随后在MainActivity当中进行查看的方式上。

不过LiveData也不具备像Observable那样强大的可组合运算符。大家可以查看LiveData提供的部分简单转换信息。

不过LiveData也不具备像Observable那样强大的可组合运算符。大家可以点击此处查看LiveData提供的部分简单转换信息

另一项区别在于,LiveData仅适用于Android,但RxJava主题则更具普适性; 因此我们可以利用常规非Android JUnit对后者进行轻松测 试。

最后一项不同是,LiveData具有“生命周期意识”——我将在下一篇介绍MainActivity类的博文当中对此作出详尽探讨。

在本示例中,我们选择使用MutableLiveData:各LiveData对象允许直接向其中随意添加新值。应用state由4个LiveData对象负责体现:city、charts、loading以及message。其中最重要的自然是charts:LiveData>对象,其代表着需要显示的当前图表清单。

所有会引发应用状态变化以及响应用户操作的任务皆由ACTOR负责执行。

Actors 非常强大,我将在下一篇博文中进行具体解释。

总结

我们已经为主actor作好了一切筹备。如果大家认真查看actor代码内容,那么即使您并不了解coroutines或者actors理论,应该也能够看懂其工作原理。虽然只有寥寥数行,但其中实际包含着本款应用的全部重要业务逻辑。最值得强调的就是我们调用 suspendable函数的位置(由绿线加灰色箭头所指定的部分)。第一个位置为suspendable点,即随用户操作进行迭代的部分; 第二个位置则为网络调用。归功于coroutines,这里的内容更近似于同步阻塞代码,但实际上却并不会拥塞该线程。

请大家期待我的下一篇博文,届时我将详细讲解关于actorschannels的一切。

原文链接:https://blog.elpassion.com/create-a-clean-code-app-with-kotlin-coroutines-and-android-architecture-components-f533b04b5431

相关推荐