Skip to content

3. 基于LiveData的请求封装

鸡你太美 edited this page Nov 4, 2025 · 2 revisions

1️⃣ RequestDsl - 网络请求与耗时操作 DSL

RequestDsl 是框架中用于发起网络请求的 DSL封装,它允许你以声明式的方式发起请求,并自动处理加载状态、异常和线程切换。

1.1 基本用法

在 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 表达式规则,最后一行作为返回值

1.2 参数说明

  • onRequest: 一个挂起函数块,在子线程执行,可以在这里执行实际的网络请求或任何耗时操作。它的返回值将作为成功结果传递给 UI。
  • loadingType: 加载类型,有三种选择:
    • LoadingType.LOADING_DIALOG: 显示对话框形式的加载提示。
    • LoadingType.LOADING_XML: 显示页面内的加载状态布局(比如请求时布局会设置加载中,请求成功自动展示页面,失败自动展示失败布局)。
    • LoadingType.LOADING_NULL: 不显示任何加载提示。等同于静默请求
参数 类型 说明 默认值
onRequest suspend CoroutineScope.() -> T 请求逻辑块,在子线程执行 必须实现
loadingType LoadingType 加载类型 LOADING_NULL
loadingMessage String 加载提示文字 "正在加载..."

1.3使用示例

1.3.1 基础请求

JetpackMvvm框架不再集成网络框架进来,需要开发者自己选择集成,你只要将请求代码写在onRequest函数中即可,这里我使用我常用的rxhttp网络框架作为示例,具体使用示例可在示例demo中查看

fun login(username: String, password: String) = request {
    onRequest {
        // 执行在 IO 线程,可以调用挂起函数
        UserRepository.login(username, password).await()
    }
    loadingType = LoadingType.LOADING_DIALOG
    loadingMessage = "正在登录中..."
}

1.3.2串行操作

fun registerAndLogin(username: String, password: String) = request {
    onRequest {
        // 先注册,后登录 - 协程内顺序执行
        userRepository.register(username, password).await()
        userRepository.login(username, password).await()
    }
    loadingType = LoadingType.LOADING_DIALOG
    loadingMessage = "注册并登录中..."
}

1.3.3 并行操作


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
}

1.3.4 复杂数据处理

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
}

1.4 ⚠️重要提醒

onRequest 运行在子线程中,最后一行作为返回值,请不要在里面直接操作UI :

onRequest {
    // ✅ 正确:网络请求、数据库操作等
    repository.getData().await()
    // ✅ 正确:网络请求、数据库操作后明确data为返回结果
    val data = repository.getData().await()
    data
    // ❌ 错误:直接更新 UI
    // mBind.textView.text = data.title
    
    // ❌ 错误:直接调用 LiveData.value
    // _uiState.value = data
}

2️⃣ UI层观察结果

2.1 obs观察示例

  • 基础观察模式
// 完整处理
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时吐司提示
}

2.2 注意

⚠️ 生命周期LifecycleOwner选择很重要:Activity中 调用obs时可以传this,Fragment 中调用时强烈推荐使用 viewLifecycleOwner 为什么 Fragment 要用 viewLifecycleOwner?

生命周期所有者 适用场景 风险说明
this (Fragment) 不推荐 观察者会持续到 Fragment 完全销毁,可能导致:• 视图销毁后仍更新 UI• 内存泄漏风险• 不必要的资源消耗
viewLifecycleOwner 推荐 观察者与视图生命周期绑定:• onCreateView → 开始观察• onDestroyView → 自动移除• 避免视图销毁后的 UI 更新

Clone this wiki locally