摘要:通过实现一次请求来解释使用协程中的实际问题是这篇文章的重点。当接收一个新事件时,启动一个新的协程来对列表进行排序,并在响应时更新。在中启动协程作为一般模式。因此,在默认情况下,在存储库中启动的任何协程都会泄露。
原文链接:Coroutines On Android (part III): Real work
原文作者:Sean McQuillan
这是一篇关于在 Android 上使用协程的系列文章之一。通过实现一次请求来解释使用协程中的实际问题是这篇文章的重点。
本系列的其他文章:[译] 在 Android 使用协程(part I) - 协程的背景知识
[译] 在 Android 使用协程(part II) - 入门指南
使用协程解决实际问题本系列的第 1 部分和第 2 部分重点介绍了如何使用协程来简化代码、在 Android 上提供主线程安全调用以及避免协程泄露。有了这个背景,协程看起来是一个既可以用于后台处理,又可以简化 Callback 的很好解决方案。
到目前为止,我们主要关注的是「什么是协程」以及「如何管理它们」。在这篇文章中,我们将看看如何使用它们来完成一些真正的任务。协程是一种通用的编程语言特性,与函数处于同一级别,因此,你可以使用它们来实现任何使用函数和对象实现的功能。然而,有两种类型的任务总是出现在实际代码中,协程是一种很好的解决方案:
一次性请求 它们总是在得到响应时认为请求完成了,所以每次调用时都会重新运行的请求
流请求 它们不会在得到第一个响应时就认为请求完成了,还会继续观察改变并将其报告给调用者
协程是这两个任务的一个很好的解决方案。在这篇文章中,我们将深入研究一次性请求,并探索如何在 Android 上用协程实现它们。
一次性请求每次调用一个一次性请求都会执行一次,并在响应时完成。此模式与常规函数调用相同——被调用,执行一些操作,然后返回。由于与函数调用的相似性,它们往往比流请求更容易理解。
每次调用一个一次性请求时都会执行一次。一旦得到响应,就停止执行。
对于一次性请求的示例,请考虑浏览器如何加载此页面。当你点击到这篇文章的链接时,浏览器向服务器发送了一个网络请求来加载页面。一旦页面被传输到你的浏览器,它就停止与后端通信——它已经获取到需要的所有数据。如果服务器修改了这篇文章,除非你刷新页面否则新的修改将不会显示在浏览器中。
因此,虽然它们缺乏流请求的实时推送功能,但一次性请求仍旧非常强大。在 Android 应用中,有很多事情可以通过一次性请求来解决,比如获取、存储或更新数据。对于排序列表之类的事情,它也是一种很好的模式。
问题:显示已排序的列表让我们通过查看如何显示排序列表来研究一次性请求。为了让示例更加具体,我们构建了一个「存货清单」的应用,供商店员工使用。它将用于根据产品最后一次进货的时间查找产品——他们希望能够对列表进行升序和降序排序。因为有很多产品,排序产品可能需要一秒钟,所以我们将使用协程来避免阻塞主线程!
在这个应用中,所有的产品都存储在一个 Room 数据库中。这是一个很好的用例,因为它不需要涉及网络请求,所以我们可以关注模式。尽管这个示例比较简单,因为它不使用网络,但是它展示了实现一次性请求所需的模式。
要使用协程实现这个请求,你将把协程引入到 ViewModel、Repository 和 Dao。让我们逐个浏览一下,看看如何将它们与协程集成在一起。
class ProductsViewModel(val productsRepository: ProductsRepository): ViewModel() {
val _sortedProducts = MutableLiveData>()
val sortedProducts: LiveData> = _sortedProducts
/**
* 当用户点击 sort 按钮时调用,由 UI 层调用
*/
fun onSortAscending() = sortPricesBy(ascending = true)
fun onSortDescending() = sortPricesBy(ascending = false)
private fun sortPricesBy(ascending: Boolean) {
viewModelScope.launch {
// 挂起和恢复使这个数据库请求确保主线程安全
// 所以我们的 ViewModel 不需要担心线程
_sortedProducts.value =
productsRepository.loadSortedProducts(ascending)
}
}
}
ProductsViewModel 负责从 UI 层接收事件,然后向存储库请求更新后的数据。它使用 LiveData 保存当前已排序的列表,以便让 UI 显示。当 sortProductsBy 接收一个新事件时,启动一个新的协程来对列表进行排序,并在响应时更新 LiveData。ViewModel 通常是这个体系结构中启动大多数协程的正确位置,因为它可以在 onCleared中取消协程。如果用户离开界面,它们通常不再需要工作。
如果你还不经常使用 LiveData,请查看 @CeruleanOtter发布的这篇很棒的文章,它介绍了如何为 UI 存储数据.
一个简单的 ViewModel 示例 (ViewModels : A Simple Example)
这是 Android 上协程的一般模式。由于 Android 框架不能调用挂起函数,因此你需要配合一个协程来响应 UI 事件。最简单的办法是事件发生时启动一个新的协程,而在 ViewModel 做这件事比较合适。
在 ViewModel 中启动协程作为一般模式。
ViewModel 使用 ProductsRepository 来实际获取数据。来看它是这样做的:
class ProductsRepository(val productsDao: ProductsDao) {
/**
* 这是一个"常规"挂起函数,这意味着调用者必须处于一个协程中。存储层不负责启动或
* 停止协程,因为它没有一个合适的生命周期来取消不必要的工作。
* 这可以是从 Dispatchers.Main 调用的,而且是主线程安全的,因为 Room 将为我们负责
* 主线程安全。
*/
suspend fun loadSortedProducts(ascending: Boolean): List {
return if (ascending) {
productsDao.loadProductsByDateStockedAscending()
} else {
productsDao.loadProductsByDateStockedDescending()
}
}
}
ProductsRepository 为产品交互提供了一个合理的接口。在这个应用程序中,由于所有内容都在本地的 Room 数据库中,所以他只是为 @Dao 提供了一个很好的接口,对于不同的排序,@Dao 有两个不同的函数。
存储层是 Android 架构体系中一个可选部分——但如果你的应用有它或类似的层,它应该更愿意暴露常规挂起函数。因为存储层没有一个天然的生命周期——它只是一个对象——它没有办法清理工作。因此,在默认情况下,在存储库中启动的任何协程都会泄露。
除了避免泄露之外,通过暴露常规挂起函数,还可以很容易地在不同的上下文中复用存储库。任何知道如何创造协程的东西都可以调用 loadSortedProducts 。例如,Workmanager 调度的后台 Job 可以直接调用它。
存储库应该暴露出主线程安全的常规挂起函数。
注意:一些后台保存操作可能会希望用户离开界面后继续执行——在没有生命周期的情况下运行这些保存是有意义的。在大多数其他情况下,viewModelScope 是一个合理的选择。
继续看 ProductsDao,它看起来是这样的:
@Dao
interface ProductsDao {
// 因为这是挂起的,Room 将使用它自己的调度器以主线程安全的方式运行这个查询
@Query("select * from ProductListing ORDER BY dateStocked ASC")
suspend fun loadProductsByDateStockedAscending(): List
// 因为这是挂起的,Room 将使用它自己的调度器以主线程安全的方式运行这个查询
@Query("select * from ProductListing ORDER BY dateStocked DESC")
suspend fun loadProductsByDateStockedDescending(): List
}
ProductsDao 是一个 Room @Dao ,它公开了两个挂起函数。由于函数被suspend 标记,Room 确保它们是主线程安全的。这意味着你可以直接从 Dispatchers.Main 来调用它们。
如果你还没有在 Room 中看到协程,请查看 @FMuntenescu的这篇很棒的文章
Room
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/7055.html
摘要:让我们探讨一下如何确保你的工作脱离主线程运行并保证执行。这确保在默认情况下,你的工作是同步运行的,并且在主线程之外运行。这是应该脱离主线程运行的工作,但是,因为它与直接相关,所以如果关闭应用程序则不需要继续。 原文地址:WorkManager Basics 原文作者:Lyla Fujiwara 译文出自:掘金翻译计划 本文永久链接:github.com/xitu/gold-m… 译者:Ri...
摘要:通过这个教程学习如何使用打包工具配合来取代或处理样式文件。使用这个命令安装插件更新。如果你没有项目的副本,你可以通过这条命令克隆在结束这个状态下的项目为添加监听插件。在代码块内,添加如下内容简单起见我省略了文件的大部分内容 通过这个教程学习如何使用JavaScript打包工具Rollup配合PostCSS来取代Grunt或Gulp处理样式文件。 上一篇文章中,我们完成了使用Rollup...
阅读 654·2023-04-25 19:43
阅读 3834·2021-11-30 14:52
阅读 3711·2021-11-30 14:52
阅读 3775·2021-11-29 11:00
阅读 3732·2021-11-29 11:00
阅读 3789·2021-11-29 11:00
阅读 3516·2021-11-29 11:00
阅读 5933·2021-11-29 11:00