Sandwich库是否有RxJava Single等效特性?如何优化串行API与ViewModel处理
串行API调用优化与优雅处理方案
问题梳理
你当前的串行API调用存在嵌套回调(回调地狱)、重复错误处理代码、ViewModel中用Any类型接收结果导致代码混乱的问题,需求包括:
- 简化串行API实现,消除嵌套
- 统一错误/异常处理,无需重复编写回调
- ViewModel中优雅处理不同类型的响应结果
解决方案
1. 简化串行调用,消除嵌套回调
利用Kotlin协程的顺序执行特性,结合Sandwich库的API,将嵌套的suspendOnSuccess改为线性调用,同时封装错误处理逻辑减少重复代码。
封装错误处理扩展函数
先封装通用扩展函数,统一处理API响应的错误和异常:
import com.skydoves.sandwich.ApiResponse import com.skydoves.sandwich.getOrThrow import com.skydoves.sandwich.onError import com.skydoves.sandwich.onException private suspend fun <T> ApiResponse<T>.handleResponse(): T? { return this.onError { throw ApiErrorException(this.message, this.code) }.onException { throw this.exception }.getOrNull() } // 自定义异常类,区分API错误与普通异常 class ApiErrorException(message: String?, val code: Int?) : Exception(message)
重构串行调用代码
用线性调用替代嵌套,每个API步骤依次执行,失败则抛出异常由统一的catch块处理:
suspend fun reCheckout(orderData: OrderData) = flow<ReCheckoutResult> { // 1. 登录注册 val loginResponse = loginAndRegister(orderData.phoneNumber).handleResponse() ?: return@flow val jwt = "jwt ${loginResponse.data.auth.token}" Client.saveAuth(loginResponse.data) // 2. 取消订单 cancelOrder( orderData.orderId, OrderCancelRequestBody("store", "re-checkout") ).handleResponse() ?: return@flow // 3. 添加购物车商品 addMultiCartItem(jwt, orderData.cartItems).handleResponse() ?: return@flow // 4. 结账 val checkoutResponse = checkOut(orderData.checkoutBody!!).handleResponse() ?: return@flow emit(ReCheckoutResult.Success(checkoutResponse.data)) }.catch { e -> // 统一处理所有异常 val result = when (e) { is ApiErrorException -> ReCheckoutResult.Error(e.message ?: "请求失败", e.code) else -> ReCheckoutResult.Exception(e) } emit(result) }
2. 统一错误/异常处理
通过上述扩展函数和flow的catch操作符,实现类似RxJava Single的全局错误/异常处理:
- 所有API调用的错误和异常都通过
handleResponse统一抛出 - 最终由flow的
catch块捕获并转换为统一结果类型,无需在每个API调用后重复编写suspendOnError/suspendOnException
3. ViewModel中优雅处理不同响应类型
使用**密封类(Sealed Class)**定义统一结果类型,替代Any类型,让ViewModel通过类型安全的when表达式处理结果,避免强制类型转换:
定义密封类结果类型
sealed class ReCheckoutResult { // 结账成功结果,携带对应数据类型 data class Success(val checkoutData: CheckoutData) : ReCheckoutResult() // API错误结果,携带错误信息和状态码 data class Error(val errorMessage: String, val errorCode: Int? = null) : ReCheckoutResult() // 异常结果,携带Throwable data class Exception(val exception: Throwable) : ReCheckoutResult() }
ViewModel中处理结果
在ViewModel中收集flow时,通过when表达式匹配不同结果类型,代码清晰且类型安全:
import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.launch class CheckoutViewModel(private val repository: CheckoutRepository) : ViewModel() { fun processReCheckout(orderData: OrderData) { viewModelScope.launch { repository.reCheckout(orderData).collect { result -> when (result) { is ReCheckoutResult.Success -> { updateUiWithCheckoutData(result.checkoutData) } is ReCheckoutResult.Error -> { showErrorToast(result.errorMessage) } is ReCheckoutResult.Exception -> { handleNetworkException(result.exception) } } } } } // 示例处理函数 private fun updateUiWithCheckoutData(data: CheckoutData) { /*...*/ } private fun showErrorToast(message: String) { /*...*/ } private fun handleNetworkException(exception: Throwable) { /*...*/ } }
内容的提问来源于stack exchange,提问作者c-an
相关产品推荐
相关产品推荐

