You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android登录模块HttpException调试:服务器宕机未触发错误处理

问题分析与代码修正

原代码存在的核心问题

  • 错误回调中先执行sendError再处理异常逻辑:若sendError内部触发异常,后续HttpException判断会直接中断,导致服务器宕机时无法正确处理500错误。
  • ErrorBody流重复读取:ResponseBody.string()是一次性操作,直接调用后未做缓存,若后续隐式重复读取会导致解析失败,触发"is not a valid element"错误。
  • Gson解析未做异常防护:未捕获解析错误,当服务器返回非预期格式的错误信息时,会直接抛出解析异常。
  • 无网络/服务器不可达场景未处理:这类场景抛出的是IOException,原代码仅返回空错误,无友好提示。

修正后的代码

LoginViewModel.kt

fun requestLogin(loginModel: LoginModel): MutableLiveData<ResponseControl<LoginResponse?>> {
    loginRepository.requestLogin(loginModel)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe({
            requestLoginMutableLiveData.postValue(ResponseControl.Success(it))
        }, { throwable ->
            // 先处理核心异常逻辑,再执行错误上报避免中断
            when (throwable) {
                is HttpException -> handleHttpException(throwable)
                is IOException -> {
                    // 处理无网络/服务器宕机场景
                    requestLoginMutableLiveData.postValue(
                        ResponseControl.Error(
                            message = "服务器不可达,请检查网络或稍后重试",
                            data = null
                        )
                    )
                }
                else -> {
                    // 其他未知异常兜底
                    requestLoginMutableLiveData.postValue(
                        ResponseControl.Error(
                            message = "登录失败,请稍后重试",
                            data = null
                        )
                    )
                }
            }
            // 最后执行错误上报,不影响核心业务逻辑
            sendError(
                ErrorModel(
                    exceptionMessage = throwable.localizedMessage ?: "未知错误",
                    innerExceptionStackTrace = throwable.stackTraceToString()
                )
            )
        }).let {
            compositeDisposable.add(it)
        }

    return requestLoginMutableLiveData
}

// 抽离HttpException处理逻辑,提高可读性与可维护性
private fun handleHttpException(httpException: HttpException) {
    when (httpException.code()) {
        401 -> requestLoginMutableLiveData.postValue(ResponseControl.AuthorizationError())
        else -> {
            // 一次性读取ErrorBody并缓存,避免流耗尽
            val errorBodyStr = httpException.response()?.errorBody()?.string()
            errorBodyStr?.let {
                try {
                    val errorDetail = Gson().fromJson(it, ApiError::class.java)
                    requestLoginMutableLiveData.postValue(
                        ResponseControl.Error(
                            message = errorDetail.message ?: "登录失败",
                            data = null
                        )
                    )
                } catch (e: Exception) {
                    // 捕获解析异常,避免崩溃并给出通用提示
                    requestLoginMutableLiveData.postValue(
                        ResponseControl.Error(
                            message = "登录失败,请稍后重试",
                            data = null
                        )
                    )
                }
            } ?: run {
                // ErrorBody为空时的兜底处理
                requestLoginMutableLiveData.postValue(
                    ResponseControl.Error(
                        message = "登录失败,请稍后重试",
                        data = null
                    )
                )
            }
        }
    }
}

其他代码说明

  • AppApi.kt/LoginRepository.kt:原代码无需修改,Retrofit默认会将非2xx状态码包装为HttpException抛出,符合预期逻辑。
  • LoginActivity.kt:可保留原逻辑,若需要更精准的错误提示,可在ResponseControl.Error中添加错误码字段,针对性处理不同场景。

内容的提问来源于stack exchange,提问作者Ömer Seyfettin Yavuzyiğit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 04:06:24