摘要:通過實現一次請求來解釋使用協程中的實際問題是這篇文章的重點。當接收一個新事件時,啟動一個新的協程來對列表進行排序,并在響應時更新。在中啟動協程作為一般模式。因此,在默認情況下,在存儲庫中啟動的任何協程都會泄露。
原文鏈接: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
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/7055.html
摘要:讓我們探討一下如何確保你的工作脫離主線程運行并保證執行。這確保在默認情況下,你的工作是同步運行的,并且在主線程之外運行。這是應該脫離主線程運行的工作,但是,因為它與直接相關,所以如果關閉應用程序則不需要繼續。 原文地址:WorkManager Basics 原文作者:Lyla Fujiwara 譯文出自:掘金翻譯計劃 本文永久鏈接:github.com/xitu/gold-m… 譯者:Ri...
摘要:通過這個教程學習如何使用打包工具配合來取代或處理樣式文件。使用這個命令安裝插件更新。如果你沒有項目的副本,你可以通過這條命令克隆在結束這個狀態下的項目為添加監聽插件。在代碼塊內,添加如下內容簡單起見我省略了文件的大部分內容 通過這個教程學習如何使用JavaScript打包工具Rollup配合PostCSS來取代Grunt或Gulp處理樣式文件。 上一篇文章中,我們完成了使用Rollup...
閱讀 713·2023-04-25 19:43
閱讀 3907·2021-11-30 14:52
閱讀 3784·2021-11-30 14:52
閱讀 3852·2021-11-29 11:00
閱讀 3783·2021-11-29 11:00
閱讀 3869·2021-11-29 11:00
閱讀 3557·2021-11-29 11:00
閱讀 6105·2021-11-29 11:00