[译] Coroutines on Android(三)实战

本系列文章主要翻译自 medium-AndroidDevelopers. 使用协程解决实际问题 前两章重点研究了协程如何简化代码,在 Android 中提供主线程安全,以及如何避免协程泄露。在此基础上,协程是一个在 Android 中进行后台处理以及以及简化回调的优秀方案。 到目前为止,我们主要关注的是什么是协程以及如何管理它们。在这篇文章中,我们将看看如何使用它们来完成实战任务。协程是一个通用的语言特性,与函数在同一个级别——你可以用它实现任何可以用函数或对象来实现的东西。当然,实际工作中有两种情况是非常适合使用协程解决的: 一次性的请求。一旦得到结果那个这次请求就结束了。 流式请求。它持续地观察数据改变并通知调用者——即使得到结果也会持续执行。 这两种任务都非常适合协程。这篇文章中,我们将深入研究一次性请求,并探索如何在 Android 中用协程实现它们。 一次性请求 一次性请求当被调用时执行,当得到结果时返回并结束。就像普通的函数那样——调用它,执行一些工作,然后返回。由于与函数调用的相似性,它比流式请求更容易理解。 一次性请求当被调用时执行,得到结果时结束。 A one shot request is performed each time it’s called. It stops executing as soon as a result is ready. 举个例子,想想浏览器是如何加载网页的。当你点击这篇文章的链接,浏览器会向服务器发送一个请求来加载页面。一旦页面成功传输到浏览器,便会停止与服务器的通讯——浏览器已经得到了所有它需要的数据。如果服务器修改了文章,除非你刷新网页,否则浏览器不会及时修改。 尽管这缺少流式请求的实时推送特性,但一次性请求还是非常强大。一个应用的许多功能都可以通过一次性请求来实现,比如获取、保存、更新数据。对于排序列表之类的事情,它也是一种很好的模式。 需求:显示一个排序的列表 让我们通过探索如何显示排序列表来研究一次性请求。为了更加具体,让我们构建一个库存管理应用,供商店的员工使用。它可以根据最后一次进货的时间查找商品——他们希望能够对列表进行升序和降序排序。但是商品太多了,排序需要几乎1秒才能完成——所以我们使用协程来避免阻塞主线程! 在这个应用中,所有的商品都存储在一个数据库中,使用了 Room 框架。这是一个很好的用例,因为它不需要涉及网络请求,我们可以专注于一次性请求模式本身,麻雀虽小,五脏俱全。 要实现此功能,你需要将协程引入 ViewModel, Repository 以及 Dao。让我们逐个看看怎么与协程进行结合。 class ProductsViewModel(val productsRepository: ProductsRepository): ViewModel() { private val _sortedProducts = MutableLiveData<List<ProductListing>>() val sortedProducts: LiveData<List<ProductListing>> = _sortedProducts /** * 当用户点击排序按钮时调用这些函数。 */ fun onSortAscending() = sortPricesBy(ascending = true) fun onSortDescending() = sortPricesBy(ascending = false) private fun sortPricesBy(ascending: Boolean) { viewModelScope....

July 2, 2019 · Chenhe

[译] Coroutines on Android(二)起步

本系列文章主要翻译自 medium-AndroidDevelopers. 本篇将开始整合协程与 Android,探索如何启动并跟踪协程,以便适配 UI 生命周期。 为何跟踪协程 在第一篇中,我们探索了协程能解决的问题。总结一下,协程是解决这两个问题的优秀方案: 在主线程运行长时间任务导致阻塞。 从主线程上安全地调用一切 suspend 函数。也即主线程安全(Main-safety) 为了解决这些问题,协程通过给常规函数添加 suspend 与 resume 操作,使得协程被挂起时在单独线程执行,主线程可以继续执行其他工作。 但是,协程本身不能跟踪正在执行的工作。协程本身很轻量,你完全可以同时启动大量的协程,甚至成百上千个。 如果要手动编写代码跟踪这几千个协程的进度将非常困难。也许能够追踪他们,确保他们完成或者取消,但这非常枯燥且容易出错。稍有不注意,就会失去对协程的跟踪,也就是协程泄露。 协程泄露就像内存泄露,但是更严重。如果协程泄露,除了占用内存,还会占用 CPU、硬盘甚至启动网络请求。 泄露的协程会浪费内存、CPU、硬盘、网络,而这些都是不需要的。 A leaked coroutine can waste memory, CPU, disk, or even launch a network request that’s not needed. 为了帮助减少协程泄露,Kotlin 介绍了 结构化并发。结构化并发是协程特性与最佳实践的结合,帮助你保持对所有协程的追踪。 在 Android,我们可以利用结构化并发完成三件事: 当不再需要时取消任务。 对运行中的任务保持追踪。 捕获协程中的异常。 让我们深入研究每一个,看看结构化并发如何帮助我们确保始终跟踪所有协程。 使用 scopes 取消任务 在 Kotlin,协程必须在一个叫做 CoroutineScope 内执行。CoroutineScope 保持了对所有协程的跟踪,尽管被挂起也是这样。不像之前我们讨论的 Dispatchers,Scope 实际上不执行协程,只是确保你不会跟丢或忘记它们。 为了保证所有协程都被跟踪,Kotlin 不允许在没有 CoroutineScope 的情况下启动协程。Scope 能够启动具备我们上一节讨论的挂起与恢复能力的协程。...

July 2, 2019 · Chenhe

[译] Coroutines on Android(一)背景知识

本系列文章主要翻译自 medium-AndroidDevelopers. 本系列文章主要关注 Kotlin Coroutine(协程) 是如何工作的,以及如何解决实际的 Android 问题。但是相对来说不会过于深入底层,更偏向于应用。 协程解决了什么? Kotlin 协程提供了一个全新的,更加简单地方式来实现异步。协程在 Kotlin 1.3 中正式发布,API 已经稳定,可以用于生产环境。事实上,协程的概念始终存在,最早是 Simula 语言在1967年探索了协程的使用。 在过去的几年,协程正逐步流行开来,众多的语言,例如 js, c#, python, ruby, go 都有所支持。Kotlin 协程基于这些基础,可以用于构建大型应用程序。 在 Android 上,协程可以优雅地解决下面两个问题: 在主线程运行长时间任务导致阻塞。 从主线程上安全地调用一切 suspend 函数。也即主线程安全(Main-safety) 主线程安全指的是:调用者不必关心这个函数应当在那个线程(调度器)调用,总能“同步”取得正确的返回值且不会阻塞。 下面从这两个角度入手,深入看看协程任何高效整洁地解决这些问题。 长时间运行的任务 调用 API 等行为会涉及到网络请求。读取数据库或加载配置文件涉及到文件读写。这类工作就是所谓的长时间任务,因为相对来说他们花费的时间很长,应用必须停下来等待他们。 在 Android 每个应用都有一个主线程来处理 UI 与交互。如果在这个线程执行了太繁重的工作,应用就会变卡,导致非常糟糕的用户体验。任何长时间运行的任务都不应该阻塞主线程,这样应用才能流畅运行,避免掉帧或未响应的出现。 为了在子线程执行网络请求,最常见的模式是使用回调(Callback)。回调提供了一个引用,它可以在在未来的某个时间通知代码继续运行,使用回调,代码可能是这样的: class ViewModel: ViewModel() { fun fetchDocs() { get("developer.android.com") { result -> show(result) } } } 尽管 get 函数在主线程被调用,但他会在另一个线程执行网络请求。然后一旦得到相应,回调函数就会在主线程执行。这是一个很棒的模式来出来长时间运行的任务。Retorfit 就是一个非常著名的网络请求库帮助你完成这些工作。 使用协程来解决 协程可以大幅简化上面的代码,现在我们用协程来重写上面的功能:...

July 1, 2019 · Chenhe