1+ package me.hgj.jetpackmvvm.core.data
2+
3+ import androidx.lifecycle.viewModelScope
4+ import kotlinx.coroutines.CancellationException
5+ import kotlinx.coroutines.CoroutineScope
6+ import kotlinx.coroutines.Dispatchers
7+ import kotlinx.coroutines.flow.Flow
8+ import kotlinx.coroutines.flow.MutableSharedFlow
9+ import kotlinx.coroutines.flow.SharedFlow
10+ import kotlinx.coroutines.flow.asSharedFlow
11+ import kotlinx.coroutines.flow.catch
12+ import kotlinx.coroutines.flow.onCompletion
13+ import kotlinx.coroutines.launch
14+ import kotlinx.coroutines.supervisorScope
15+ import me.hgj.jetpackmvvm.R
16+ import me.hgj.jetpackmvvm.base.vm.BaseViewModel
17+ import me.hgj.jetpackmvvm.core.net.LoadStatusEntity
18+ import me.hgj.jetpackmvvm.core.net.LoadingEntity
19+ import me.hgj.jetpackmvvm.core.net.LoadingType
20+ import me.hgj.jetpackmvvm.ext.util.code
21+ import me.hgj.jetpackmvvm.ext.util.getStringExt
22+ import me.hgj.jetpackmvvm.ext.util.logE
23+ import me.hgj.jetpackmvvm.ext.util.msg
24+
25+ /* *
26+ * 基于 Flow 的网络或本地请求 DSL
27+ * 支持在 onRequest 中自由组合 Flow 操作符,支持多次数据发射
28+ *
29+ * 使用示例:
30+ * ```
31+ * ViewModel中:
32+ * fun observeUserData() = requestFlow {
33+ * onRequest {
34+ * // 可以自由组合多个 Flow
35+ * userRepository.getUserFlow()
36+ * .combine(settingsRepository.getSettingsFlow()) { user, settings ->
37+ * UserWithSettings(user, settings)
38+ * }
39+ * .map { it.toUiModel() }
40+ * }
41+ * loadingType = LoadingType.LOADING_DIALOG
42+ * loadingMessage = "正在加载用户数据..."
43+ * }
44+ *
45+ * UI层中:
46+ * mViewModel.observeUserData().obs(this) {
47+ * onSuccess { userData ->
48+ * // 支持多次触发,每次数据更新都会调用
49+ * updateUI(userData)
50+ * }
51+ * onError { status ->
52+ * showError(status.msg)
53+ * }
54+ * }
55+ * ```
56+ */
57+ fun <T > BaseViewModel.requestFlow (
58+ requestParameterDslClass : FlowRequestParameterDsl <T >.() -> Unit
59+ ): SharedFlow <ApiResult <T >> {
60+ val dsl = FlowRequestParameterDsl <T >().apply (requestParameterDslClass)
61+ return executeFlowRequestWithResult(dsl)
62+ }
63+
64+ /* *
65+ * 执行 Flow 版本的请求,支持多次数据发射
66+ */
67+ private fun <T > BaseViewModel.executeFlowRequestWithResult (
68+ requestParameterDsl : FlowRequestParameterDsl <T >
69+ ): SharedFlow <ApiResult <T >> {
70+ val resultFlow = MutableSharedFlow <ApiResult <T >>(
71+ replay = requestParameterDsl.replay,
72+ extraBufferCapacity = requestParameterDsl.extraBufferCapacity
73+ )
74+ viewModelScope.launch(Dispatchers .IO ) {
75+ supervisorScope {
76+ try {
77+ // 显示 loading(只在开始时显示一次)
78+ if (requestParameterDsl.loadingType != LoadingType .LOADING_NULL ) {
79+ loadingChange.loading.postValue = LoadingEntity (
80+ loadingType = requestParameterDsl.loadingType,
81+ loadingMessage = requestParameterDsl.loadingMessage,
82+ isShow = true ,
83+ coroutineScope = this
84+ )
85+ }
86+ // 执行 Flow 并收集结果
87+ requestParameterDsl.onRequest.invoke(this )
88+ .onCompletion { cause ->
89+ // 请求完成时隐藏 loading
90+ if (requestParameterDsl.loadingType != LoadingType .LOADING_NULL ) {
91+ loadingChange.loading.postValue = LoadingEntity (
92+ loadingType = requestParameterDsl.loadingType,
93+ loadingMessage = requestParameterDsl.loadingMessage,
94+ isShow = false
95+ )
96+ }
97+ // 成功完成
98+ if (cause == null && requestParameterDsl.loadingType == LoadingType .LOADING_XML ) {
99+ loadingChange.showSuccess.postValue = true
100+ }
101+ }
102+ .catch { e ->
103+ // Flow 内部的异常处理
104+ if (e is CancellationException ) return @catch
105+ e.printStackTrace()
106+ " 抱歉!出错了----> : ${e.message} " .logE()
107+ val loadStatus = LoadStatusEntity (
108+ code = e.code,
109+ msg = e.msg,
110+ throwable = e,
111+ loadingType = requestParameterDsl.loadingType
112+ )
113+ if (loadStatus.loadingType == LoadingType .LOADING_XML ) {
114+ loadingChange.showError.postValue = loadStatus
115+ }
116+ resultFlow.emit(ApiResult .Error (loadStatus))
117+ }
118+ .collect { data ->
119+ // 每次发射数据都发送成功结果
120+ resultFlow.emit(ApiResult .Success (data))
121+ }
122+
123+ } catch (e: CancellationException ) {
124+ // 请求被取消
125+ return @supervisorScope
126+ } catch (e: Exception ) {
127+ // 外层异常处理
128+ e.printStackTrace()
129+ " 抱歉!出错了----> : ${e.message} " .logE()
130+ if (requestParameterDsl.loadingType != LoadingType .LOADING_NULL ) {
131+ loadingChange.loading.postValue = LoadingEntity (
132+ loadingType = requestParameterDsl.loadingType,
133+ loadingMessage = requestParameterDsl.loadingMessage,
134+ isShow = false
135+ )
136+ }
137+ val loadStatus = LoadStatusEntity (
138+ code = e.code,
139+ msg = e.msg,
140+ throwable = e,
141+ loadingType = requestParameterDsl.loadingType
142+ )
143+ if (loadStatus.loadingType == LoadingType .LOADING_XML ) {
144+ loadingChange.showError.postValue = loadStatus
145+ }
146+ resultFlow.emit(ApiResult .Error (loadStatus))
147+ }
148+ }
149+ }
150+ return resultFlow.asSharedFlow()
151+ }
152+
153+ /* *
154+ * 增强版 Flow 请求参数封装类
155+ */
156+ class FlowRequestParameterDsl <T > {
157+ /* *
158+ * 协程请求方法体,返回一个 Flow<T>,支持多次数据发射
159+ * 可以自由使用 Flow 的各种操作符组合
160+ */
161+ private var _onRequest : (suspend CoroutineScope .() -> Flow <T >)? = null
162+ var onRequest: suspend CoroutineScope .() -> Flow <T >
163+ get() = _onRequest ? : { throw IllegalStateException (" onRequest 必须实现哦" ) }
164+ set(value) {
165+ _onRequest = value
166+ }
167+
168+ /* *
169+ * 执行请求封装
170+ */
171+ fun onRequest (block : suspend CoroutineScope .() -> Flow <T >) {
172+ _onRequest = block
173+ }
174+
175+ /* * 加载提示内容 */
176+ var loadingMessage: String = getStringExt(R .string.helper_loading_tip)
177+
178+ /* * 请求时loading类型 默认请求时不显示loading,
179+ * 注意:如果有多次发送值的应用场景建议传LoadingType.LOADING_NULL ,比如轮询请求,实时数据流等
180+ * */
181+ @LoadingType
182+ var loadingType = LoadingType .LOADING_NULL
183+
184+ /* * SharedFlow 的重放数量
185+ * ```
186+ *1.这个参数指定了将最近发射过的多少个值重放给新的订阅者。例如,如果设置为1,那么每个新的订阅者都会立即收到最近发射的一个值。如果设置为0,那么新的订阅者不会收到任何之前发射的值,只会收到订阅后新发射的值。
187+ *
188+ *2.应用场景:当你希望新订阅者能够立即获得最新状态时,可以设置replay>0。比如,在状态管理中,你希望UI组件在配置变化后重建时能够立即获得最新的状态,就可以设置replay=1。
189+ * ```
190+ * */
191+ var replay: Int = 0
192+
193+ /* * SharedFlow 的额外缓冲容量
194+ * ```
195+ * 1.这个参数用于配置除了replay之外还可以缓冲多少个数。SharedFlow 的总缓冲容量 = replay + extraBufferCapacity。当有发射值而订阅者尚未消费时,这些值会被缓冲起来,直到达到缓冲容量上限。默认情况下,SharedFlow 的发射操作会挂起,直到有缓冲空间可用(除非使用tryEmit)。
196+ *
197+ * 2.应用场景:当你希望处理背压(backpressure)时,可以通过调整缓冲容量来控制。较大的缓冲容量可以允许发射者在不挂起的情况下发射更多值,但可能会消耗更多内存。注意,即使设置了额外的缓冲容量,新的订阅者仍然只能收到replay个最近的值。
198+ * ```
199+ * */
200+ var extraBufferCapacity: Int = 0
201+ }
0 commit comments