Kotlin協程-從一到多

来源:https://www.cnblogs.com/honguilee/archive/2023/06/16/17478630.html
-Advertisement-
Play Games

> 上一篇文章,我介紹了Kotlin協程的創建,使用,協作等內容。本篇將引入更多的使用場景,繼續帶你走進協程世界。 ### 使用協程處理非同步數據流 常用編程語言都會內置對同一類型不同對象的數據集表示,我們通常稱之為容器類。不同的容器類適用於不同的使用場景。Kotlin的`Flow`就是在非同步計算的需 ...


上一篇文章,我介紹了Kotlin協程的創建,使用,協作等內容。本篇將引入更多的使用場景,繼續帶你走進協程世界。

使用協程處理非同步數據流

常用編程語言都會內置對同一類型不同對象的數據集表示,我們通常稱之為容器類。不同的容器類適用於不同的使用場景。Kotlin的Flow就是在非同步計算的需求下引入的,用於表示非同步的數據流。

Flow

“問渠哪得清如許,為有源頭活水來”,非同步數據流的基本就是以某種方式獲得非同步數據。Kotlin提供了多種種方式,比較常用的就是Kotlin協程包的asFlow擴展和flow構造器。前者是對普通數據集的Flow化封裝,沒有更多可言,我們著重來看後者。
flow構造器的主要目標就是產生一個非同步數據流,它是一個泛型函數,參數是一個掛起函數,並且是FlowCollector是擴展函數。這個介面只有一個emit方法,就是為創建的Flow提供非同步計算的數據的,因為它是掛起函數,所以我們能在裡面使用其他掛起函數計算非同步值,然後通過emit方法將值發送出去,如此反覆就能為下游操作提供源源不斷的數據流了。
事情還沒完,上面的步驟我們只是規定了創建數據的方式,並沒有真正執行,也就是建好了道路,但是還沒有車上路。那麼,怎樣才能讓車在路上跑呢,查看Flow的介面會發現,它提供了collect方法來處理數據。collect接收一個掛起函數作為處理邏輯,但是同時,collect方法本身也是掛起函數,所以,這個方法只能在掛起函數中運行。有了這些知識,我們就可以寫出最簡單的非同步數據流了。

 1uspend fun compute():Int{
             delay(123)
             return 1024
 }
 
 viewModelScope.launch {
     val flow=flow<Int> {
         emit(9527)
         emit(compute())
        delay(256)
        emit(256)
    }
    flow.collect {
        println(it)
    }
}

flow構造器裡面隨意做各種操作,只要在必要的時候傳遞結果就行了,但是需要註意的是,emit方法只能運行在同一個協程里。乍一看,這樣分開寫和寫在一起並沒有本質上的差別,但Flow還能做到更多。

該給Flow換個工作環境了

上一節,我們那個簡單的示例,假如把構造器裡面的數據獲取方法換成網路請求,應用就歇菜了。因為它們都是運行在主線程裡面的。那麼這個時候,看過上一篇文章的小伙伴馬上就會反應過來,用withContext方法在構造器裡面切換線程就行了哇。思路是很對,因為Flow的預設配置就是構造器和collect方法工作在同一線程,既然現在主線程不讓運行,那就把構造器的線程切換一下就行了唄。然後事實並不是這樣,這樣寫出來的代碼根本無法運行。因為官方提供了唯一的flowOn方法來切換構造器的執行線程。使用也很簡單,就是對創建好的Flow對象配置一次flowOn方法就行了。

val flow=["1.jpg","2.jpg"].asFlow()
flow.map { decode(it) }
        .flowOn(Dispatchers.IO)
viewModelScope.launch {
    flow.collect{
        adapter.add(it)
    }

有些中間處理邏輯

熟悉RxJava的小伙伴可能有疑問了,這些操作RxJava也能完成,甚至還有更多的操作符來支持中間狀態的處理,那麼非同步數據流能做到這些嗎。毫無疑問,它可以。普通的數據集有map,filter等操作方法,對於非同步數據流來說,這些方法同樣適用。而且這些方法參數都是掛起函數,都可以執行非同步操作。而且它還有個更靈活的transform方法,這個方法可以定製自己的操作符,實現更靈活的數據操作。

當然,上面那些操作符都只能實現單一非同步流的操作,對於多數據流的支持,它也同樣不在話下。zip可以將兩個兩個數據源兩兩合併起來,合成的數據流長度為兩個數據流中最短的那個數據流的長度。combine則與zip不同,它會將兩個數據流最近的發送數據作為輸入,也就是說,假如一塊一慢的兩個數據源,慢的數據源的元素可能會被多次取到,從而最終的數據流比最短的那個都長。

val flow = flowOf(1, 2).delayEach(10)
val flow2 = flowOf("a", "b", "c").delayEach(15)
flow.combine(flow2) { i, s -> i.toString() + s }.collect {
     println(it) // Will print "1a 2a 2b 2c"
}

結束狀態跟蹤

上一節提到,由於數據源和處理邏輯不在同一個地方,所以很難確定最終的數據流大小,進而不知道數據流什麼時候處理結束。而且中間操作也可能會改變數據流的大小,由此就更加難以確定數據處理結束的時機了。但是我們有的時候卻需要在數據處理完成後做一些操作,該怎麼辦呢?這個時候當然是該onCompletion方法上場了。這個方法有一個可為空的Throwable類型參數,很顯然,這可以同時指示兩種處理結果,成功或者失敗,失敗就會將異常對象傳遞進來。

多個協程共同工作

很多時候,避免不了讓多個協程共同工作。對於返回單個值的協程,上一篇我們也提到過了,可以傳遞async構造器的返回對象Deferred,但是局限性就是這個對象只能傳遞一個值。針對多值傳遞的情況,Kotlin提供了Channel的解決方法。Channel類似於阻塞隊列,數據通過send方法發送出去,在另外的地方使用receive方法接收。通過這種方法,我們可以極大提供協程的工作效率。利用它就可以輕鬆實現生產者和消費者模型。

 val chanel=Channel<Int>()
 viewModelScope.launch(Dispatchers.IO) {
     for (i in 1..5){
         delay(1000)
         chanel.send(i)
     }
 }
 viewModelScope.launch { 
     for (i in chanel){
        println("Handle ${i}")
    }
}

當然,這隻是最簡單的用法,還可以加入更多的生產者,或者不再需要數據時取消,甚至還有專門的product構造器,直接獲得返回多個值的協程對象。

總結

Kotlin協程有很多有用的API,這些API覆蓋了大部分非同步使用的場景。所以在使用協程的時候,我們首先需要明確使用場景,再根據使用場景確定使用哪一套API,這可以使我們避免陷入API恐懼症。為此,我根據這兩篇文章的內容,整理出了一份情景表格,實際開發中可以參照使用。
Kotlin協程構造器

API 使用場景
launch 執行耗時操作,不需要返回值
async 需要獲取耗時操作的單個返回值
produce 需要獲取耗時操作的多個返回值

Kotlin協程協同工具

API 使用場景
Flow 操作非同步數據流
Channel 協程間通信

青山不改,綠水長流,咱們下期見!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文分享自天翼雲開發者社區《淺談HPC中的Lustre》,作者:n****m 1. 什麼是 lustre? Lustre 體繫結構是一個為集群設計的存儲體繫結構。 其核心組件是運行在 Linux 操作系統上、 支持標準的 POSIX* UNIX 文件系統介面、 並遵循 GPL2.0 許可的 Lust ...
  • ![file](https://img2023.cnblogs.com/other/3195851/202306/3195851-20230616175541667-1729991882.jpg) Catalog(目錄)提供了關於資料庫、表格和訪問數據所需的信息的元數據,以及統一的 API 來管理元 ...
  • ![file](https://img2023.cnblogs.com/other/2685289/202306/2685289-20230616173226789-2009691483.jpg) > 截至今天,Apache DolphinScheduler 項目在 GitHub 上的 Star 數 ...
  • > mysql單表數據量太大 導致查詢緩慢問題如何解決 您好,這是Bing。根據網路搜索結果,mysql單表數據量太大導致查詢緩慢的問題有以下幾種可能的解決方案: - 使用索引。索引是一種數據結構,可以幫助資料庫高效地獲取數據。索引可以根據WHERE和JOIN中出現的列創建,以提高查詢效率。索引的種 ...
  • 摘要:華為全球智慧金融峰會2023上,華為雲資料庫服務產品部總經理蘇光牛帶來了《華為雲分散式資料庫GaussDB,做金融數字化的堅實數據底座》的主題分享。 6月7日,華為全球智慧金融峰會2023在上海順利舉行,華為雲資料庫服務產品部總經理蘇光牛帶來了《華為雲分散式資料庫GaussDB,做金融數字化的 ...
  • 查看資料庫 show databases; 創建資料庫 create database db1; 創建資料庫db1 刪除資料庫 drop database db1; 刪除資料庫db1 切換進入資料庫 use user 進入user資料庫 查看資料庫中的表 show tables; 創建表 創建表的方 ...
  • Hello,社區的小伙伴們,又到了每月版本發佈時間。🎉🎉🎉 本次社區版更新帶來了新功能 **「發佈變更」**,以及**內置脫敏規則、授權粒度細化、連接池管理、變更鏈接密鑰**等,信息量不少,一起來看! ### 發佈變更 大量數據變更的便捷操作 社區版 v2.0.0,我們增加了「數據變更」模塊, ...
  • > 上一篇文章從理論上對Kotlin協程進行了部分說明,本文將在上一篇的基礎上,從實戰出發,繼續協程之旅。 ### 從源頭說起 在Kotlin中,要想使用協程,首先需要使用協程創建器創建,但還有個前提——協程作用域(`CoroutineScope`)。在早期的Kotlin實現中,協程創建器是一等函數 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...