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
相关产品推荐
相关产品推荐

