-
Notifications
You must be signed in to change notification settings - Fork 663
3. 基于LiveData的请求封装
鸡你太美 edited this page Nov 4, 2025
·
2 revisions
RequestDsl 是框架中用于发起网络请求的 DSL封装,它允许你以声明式的方式发起请求,并自动处理加载状态、异常和线程切换。
在 ViewModel 中,使用 request 函数来发起请求:
class UserViewModel : BaseViewModel() {
fun login(username: String, password: String) = request {
onRequest {
// 遵循 Kotlin lambda 表达式规则:最后一行作为返回值
UserRepository.login(username, password).await()
}
loadingType = LoadingType.LOADING_DIALOG
loadingMessage = "正在登录中..."
}
}
- 自动切换线程:onRequest 块在 Dispatchers.IO 执行
- 自动异常捕获:统一处理所有异常,避免重复 try-catch
- 自动 Loading 状态:根据配置自动显示/隐藏 Loading
- Kotlin Lambda 返回值:遵循 Kotlin lambda 表达式规则,最后一行作为返回值
-
onRequest: 一个挂起函数块,在子线程执行,可以在这里执行实际的网络请求或任何耗时操作。它的返回值将作为成功结果传递给 UI。 -
loadingType: 加载类型,有三种选择:-
LoadingType.LOADING_DIALOG: 显示对话框形式的加载提示。 -
LoadingType.LOADING_XML: 显示页面内的加载状态布局(比如请求时布局会设置加载中,请求成功自动展示页面,失败自动展示失败布局)。 -
LoadingType.LOADING_NULL: 不显示任何加载提示。等同于静默请求
-
| 参数 | 类型 | 说明 | 默认值 |
|---|---|---|---|
| onRequest | suspend CoroutineScope.() -> T | 请求逻辑块,在子线程执行 | 必须实现 |
| loadingType | LoadingType | 加载类型 | LOADING_NULL |
| loadingMessage | String | 加载提示文字 | "正在加载..." |
JetpackMvvm框架不再集成网络框架进来,需要开发者自己选择集成,你只要将请求代码写在onRequest函数中即可,这里我使用我常用的rxhttp网络框架作为示例,具体使用示例可在示例demo中查看
fun login(username: String, password: String) = request {
onRequest {
// 执行在 IO 线程,可以调用挂起函数
UserRepository.login(username, password).await()
}
loadingType = LoadingType.LOADING_DIALOG
loadingMessage = "正在登录中..."
}
fun registerAndLogin(username: String, password: String) = request {
onRequest {
// 先注册,后登录 - 协程内顺序执行
userRepository.register(username, password).await()
userRepository.login(username, password).await()
}
loadingType = LoadingType.LOADING_DIALOG
loadingMessage = "注册并登录中..."
}
fun getHomeData() = request {
onRequest {
//下面3个接口并发请求 是同时请求的,等待全部请求完成
//获取文章数据
val listDeferred = HomeRepository.getList().safeAsync(this)
//获取Banner数据
val bannerDeferred = HomeRepository.getBanner().safeAsync(this)
//获取置顶文章数据
val topDeferred = HomeRepository.getTopArticle().safeAsync(this)
// 请求得到结果
val listData = listDeferred.await()
val banner = bannerDeferred.await()
val top = topDeferred.await()
...
listData
}
loadingType = LoadingType.LOADING_XML
}
fun processUserData() = request {
onRequest {
val rawData = HomeRepository.getList().await()
// 数据处理
val processedData = rawData
.filter { it.isValid }
.map { it.toUiModel() }
.sortedBy { it.createTime }
// 最后一行 processedData 将作为成功结果
processedData
}
loadingType = LoadingType.LOADING_XML
}
onRequest 运行在子线程中,最后一行作为返回值,请不要在里面直接操作UI :
onRequest {
// ✅ 正确:网络请求、数据库操作等
repository.getData().await()
// ✅ 正确:网络请求、数据库操作后明确data为返回结果
val data = repository.getData().await()
data
// ❌ 错误:直接更新 UI
// mBind.textView.text = data.title
// ❌ 错误:直接调用 LiveData.value
// _uiState.value = data
}
- 基础观察模式
// 完整处理
viewModel.getData().obs(this) {
onSuccess { data ->
// 处理成功数据
updateUI(data)
}
onError { status ->
// 自定义错误处理
handleError(status)
}
}
- 简化观察模式
// 只处理成功,使用框架默认错误处理
viewModel.getData().obs(this) {
onSuccess { data ->
// 专注业务逻辑
showData(data)
}
// 不实现 onError,自动使用框架默认处理既:loadindType!=LoadingType.LOADING_NULL时吐司提示
}
this,Fragment 中调用时强烈推荐使用 viewLifecycleOwner
为什么 Fragment 要用 viewLifecycleOwner?
| 生命周期所有者 | 适用场景 | 风险说明 |
|---|---|---|
| this (Fragment) | 不推荐 | 观察者会持续到 Fragment 完全销毁,可能导致:• 视图销毁后仍更新 UI• 内存泄漏风险• 不必要的资源消耗 |
| viewLifecycleOwner | 推荐 | 观察者与视图生命周期绑定:• onCreateView → 开始观察• onDestroyView → 自动移除• 避免视图销毁后的 UI 更新 |