ViewModel是EventFlow-State映射

ViewModel負責組裝界面狀態State。引發State變換的原因有很多,比如用戶點擊某個按鈕,一次網絡請求受到應答,一次本地數據庫查詢返回結果等等。因此ViewModel是根據各種事件生成State的對象,換句話說,是一個從多個事件流到狀態的映射。

ViewModel: (InputFlow, NetworkFlow, DatabaseFlow, ...) -> State

用圖來表示就是

                         ┌─────────┐
用戶交互 ───? 事件流 ───┤         ││ViewModel├───? UI 狀態 ───? 渲染UI
數據變更 ───? 數據流 ───┤         │└─────────┘

1.?常見事件流處理模式

我們考慮處理兩個事件流的模式。多事件流的處理方法是類似的。

1.1.?模式1:取各自最新值

combine(flowA, flowB) { a, b ->// 當某個流更新時,獲取兩個流各自的最新值。Result(a, b)
}

適用場景:表單聯動驗證(如郵箱+密碼驗證)、實時數據看板。

1.2.?模式2:合并流

merge(flowA, flowB) // 任意流更新時觸發,取兩個流中的最新值。

適用場景:同類型事件合并(如多個按鈕點擊事件)

1.3.?模式3:流水線

flowA.flatMapLatest { aValue -> flowB.map { bValue -> Processed(aValue, bValue) }
}

適用場景:搜索建議(關鍵詞變化觸發新查詢)、參數化數據加載

1.4.?模式4:序號匹配

flowA.zip(flowB) { a, b -> // 嚴格按序號匹配,雙方各發1次才觸發。Pair(a, b) 
}

適用場景:分頁加載、操作-響應確認機制。

1.5.?模式5:優先流

combine(flowA, flowB) { a, b ->when {a.priority > b.priority -> Result(a)b.isCritical -> Result(b)else -> Result.merge(a, b)}
}

特點:實現業務規則主導的數據優先級。 適用場景:多源數據沖突處理、關鍵操作優先。

1.6.?模式6:將異常轉換為事件

val flowAState = flowA.map { Success(it) }.catch { emit(Error(it)) }.stateIn(viewModelScope, SharingStarted.Lazily, Loading)

特點:單個流的失敗不影響整體功能。 適用場景:模塊化數據展示、獨立可失敗操作。

2.?常見問題和方案

2.1.?問題1:輸入流過多

輸入流過多會導致代碼可讀性下降。封裝中間事件和中間流可以解決這個問題。

// 創建中間組合流
val userPreferences = combine(themeFlow, fontSizeFlow, layoutFlow) { UserPrefs(it[0], it[1], it[2]) 
}val finalState = combine(userPrefs, contentFlow) { ... }

2.2.?問題2:狀態頻繁更新導致UI抖動

在流中增加防抖可以避免UI抖動。

searchQueryFlow.debounce(300)          // 防抖.distinctUntilChanged().flatMapLatest { query -> repository.search(query).map { it.toState() } }

2.3.?問題3:訂閱泄漏

使用stateIn和flatMapLatest可以避免訂閱泄露。

代碼1??錯誤例子:

init {viewModelScope.launch {   // 直接啟動協程dataFlow.collect { ... }}
}

代碼2??正確例子

val state = dataFlow.map { ... }.stateIn(                 // 使用stateIn擴展scope = viewModelScope,started = SharingStarted.Eagerly,initialValue = ...)

2.4.?問題4:錯誤恢復

使用retryWhen可以從錯誤中進行恢復。

flowA.retryWhen { cause, attempt -> if (attempt < 3) delay(200 * attempt) else throw cause}.catch { emit(fallbackData) }

2.5.?問題5:動態切換流

val activeFlow = MutableStateFlow<Flow<Data>>(flowA)val result = activeFlow.flatMapLatest { it }

支持在運行時切換數據源(如測試模式/生產模式切換)。

2.6.?問題6:緩存共享流

復用單數據源,減少重復訂閱。

// 創建共享流
val sharedFlow = repository.dataSource.shareIn(viewModelScope, SharingStarted.WhileSubscribed())// 分流處理
val stateA = sharedFlow.map { extractA(it) }
val stateB = sharedFlow.map { extractB(it) }

2.7.?問題7:將業務狀態作為界面狀態

不要直接使用業務狀態作為界面狀態(業務狀態極為簡單的除外)。

// 錯誤方式:混合業務狀態和UI控制狀態
data class BadState(val data: List<Item>,val loading: Boolean,val toastMessage: String?, // UI控制狀態val dialogVisible: Boolean // UI控制狀態
)// 正確方式:分層狀態
// 業務狀態
data class BusinessState(val data: List<Item>, val error: Throwable?)// UI狀態(由業務狀態轉換)
val uiState = businessState.map { when {it.data.isEmpty() -> EmptyStateit.error != null -> ErrorState(it.error)else -> SuccessState(it.data)}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/87172.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/87172.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/87172.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

javaweb Day2

PreparedStatement作用: 預編譯SQL語句并執行: 預防SQL注入問題 SQL注入:SQL注入是通過操作輸入來修改事先定義好的SQL語句&#xff0c;用以達到執行代碼對服務器進行攻擊的方法。

Java項目:基于SSM框架實現的中學教學管理系統【ssm+B/S架構+源碼+數據庫+畢業論文+開題報告】

摘 要 現代經濟快節奏發展以及不斷完善升級的信息化技術&#xff0c;讓傳統數據信息的管理升級為軟件存儲&#xff0c;歸納&#xff0c;集中處理數據信息的管理方式。本景海中學教學管理系統就是在這樣的大環境下誕生&#xff0c;其可以幫助管理者在短時間內處理完畢龐大的數據…

JVM調優實戰 Day 15:云原生環境下的JVM配置

【JVM調優實戰 Day 15】云原生環境下的JVM配置 文章標簽 jvm調優, 云原生, Java性能優化, JVM參數配置, 容器化部署, Kubernetes, Docker, JVM在云原生中的應用 文章簡述 隨著云原生技術的普及&#xff0c;Java 應用越來越多地運行在容器&#xff08;如 Docker&#xff09;和…

數據結構day7——文件IO

一、標準 IO 的起源與概念 標準 IO&#xff08;Standard Input/Output&#xff09;是由 Dennis Ritchie 在 1975 年設計的一套 IO 庫&#xff0c;后來成為 C 語言的標準組成部分&#xff0c;并被 ANSI C 所采納。它是對底層文件 IO 的封裝&#xff0c;提供了更便捷、可移植的文…

6.Docker部署ES+kibana

部署ES&#xff08;Elasticsearch&#xff09;kibana 1.ES暴露的端口很多 2.ES十分消耗內存 3.ES的數據一般需要掛載出去&#xff0c;放在安全目錄&#xff08;掛載) elastic 前往官方手冊 1.下載運行elasticsearch的 docker run -d --name elasticsearch --net somenet…

使用mysqldump對mysql數據庫進行備份

目錄 1軟件說明 2語法格式 3備份流程 3.1只備份指定數據庫中表和數據 3.1.1準備目錄 3.1.2備份db1數據庫里面的所有表信息 3.1.3還原備份 3.2備份數據庫結構 3.2.1備份db1數據庫的結構和數據 3.2.2還原數據庫 3.3備份所有數據庫 3.3.1備份數據庫 3.3.2還原數據庫 1…

vue3路由跳轉打開新頁面

Vue3 路由跳轉打開新頁面的方法 在 Vue3 中&#xff0c;有幾種方法可以實現路由跳轉時打開新頁面&#xff1a; 1. 使用 router.resolve 方法 import { useRouter } from vue-routerconst router useRouter()const openNewPage (path) > {const resolved router.resolv…

SeaTunnel 社區 2 項目中選“開源之夏 2025”,探索高階數據集成能力!

Apache SeaTunnel 社區在“開源之夏 2025”中再傳捷報&#xff0c;共有兩個項目成功入選&#xff0c;聚焦于 Flink CDC schema 支持與元數據管理的生態擴展方向&#xff0c;體現出 SeaTunnel 在實時數據集成和平臺化能力構建上的深入布局。 中選項目與學生如下&#xff1a; 《…

Neo4j無法建立到 localhost:7474 服務器的連接出現404錯誤

一、確認中文路徑問題&#xff08;核心原因&#xff09; 安裝路徑包含中文&#xff0c;可能導致 Windows 命令行或 Neo4j 解析路徑時出錯。 解決方法&#xff1a; 重新安裝 Neo4j 到英文路徑&#xff08;推薦&#xff09;&#xff1a; 將 Neo4j 解壓或安裝到不含中文的目錄&a…

鋰離子電池均衡拓撲綜述

鋰離子電池均衡拓撲綜述 一、引言 鋰離子電池因其高能量密度、長循環壽命等優點&#xff0c;在電動汽車、儲能系統等領域得到了廣泛應用。然而&#xff0c;電池組在使用過程中&#xff0c;由于電池個體差異、充放電管理等因素&#xff0c;會出現荷電狀態&#xff08;SOC&…

[面試] 手寫題-淺拷貝,深拷貝

淺拷貝 // 淺拷貝 function shallow(obj) {const newObj {}for (const key in obj) {// 保證 key 不是原型的屬性if (obj.hasOwnProperty(key)) {newObj[key] obj[key]}}return newObj }深拷貝 遞歸 O(n^2) // 深拷貝 function deepClone(obj {}) {// 如果傳入的是 null&am…

BehaviorTree.ROS2安裝記錄

坑比庫&#xff0c; 首先 git clone https://github.com/BehaviorTree/BehaviorTree.ROS2.git 依賴 git clone https://github.com/PickNikRobotics/cpp_polyfills.git git clone https://github.com/PickNikRobotics/RSL.git git clone https://github.com/PickNikRobotics/…

Vue基礎(19)_Vue內置指令

我們學過的vue內置指令&#xff1a; v-bind&#xff1a;單向綁定解析表達式&#xff0c;可簡寫為&#xff1a;:xxx v-model&#xff1a;雙向數據綁定 v-for&#xff1a;遍歷數組/對象/字符串 v-on&#xff1a;綁定事件監聽&#xff0c;可簡寫為 v-if&#xff1a;條件渲染(動態控…

排列組合初步

什么是排列組合 排列組合是計數問題&#xff0c;順序不同且值相同算兩種方案是排列&#xff0c;順序不同且值相同算一種方案是組合。 暴力枚舉方案能算出方案數&#xff0c;太耗時&#xff0c;運用加法原理和乘法原理可降低時間復雜度。先將原問題拆解成子問題&#xff0c;根…

SQL調優方案對比與最佳實踐

問題背景介紹 在大型互聯網或企業級應用中&#xff0c;數據庫往往成為系統性能的瓶頸。隨著數據量和并發量的增長&#xff0c;單一的 SQL 查詢可能出現響應遲緩、鎖等待、全表掃描等性能問題。為保證系統的穩定性和用戶體驗&#xff0c;需要對 SQL 查詢做深入的調優。常見的調…

Terraform Helm:微服務基礎設施即代碼

&#x1f680; Terraform & Helm&#xff1a;微服務基礎設施即代碼 &#x1f4da; 目錄 &#x1f680; Terraform & Helm&#xff1a;微服務基礎設施即代碼1. 引言 &#x1f680;2. 環境與依賴 &#x1f9f0;3. 架構示意 &#x1f3d7;?4. Terraform 定義云資源 &…

清理 Docker 緩存占用

Docker 緩存主要包括未使用的鏡像、容器、卷和網絡等資源。清理緩存可以提高磁盤空間&#xff0c;線上升級次數比較多的話&#xff0c;服務器中Docker緩存會非常嚴重&#xff0c;做下清理瘦身會有意想不到的效果 清理未使用的鏡像 運行以下命令刪除未被任何容器引用的鏡像&…

深入解析NumPy的核心函數np.array()

深入解析NumPy的核心函數np.array NumPy與np.array()簡介NumPy的重要性np.array()的作用 np.array()函數的詳細參數object參數dtype參數copy參數order參數subok參數ndmin參數like參數 np.array()函數的使用示例創建基本的一維和二維數組創建具有特定數據類型的數組創建多維數組…

定時器的設計

定時器 定時器原理如何理解定時器定時器數據結構選取定時器觸發方式 定時器的實現 定時器原理 如何理解定時器 定時器在日常通常被描述為組織大量延時任務的模塊&#xff0c;其實從字面意思去理解的話&#xff0c;他就是去處理延時任務的&#xff0c;那么什么是延時任務呢&am…

大模型-分布式論文一瞥

1分離式架構 1.1 DistServe DistServe: Disaggregating Prefill and Decoding for Goodput-optimized Large Language Model Serving DistServe: Disaggregating Prefill and Decoding for Goodput-optimized Large Language Model Serving 講的是一個將prefill和decoding分…