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

RxJava引发UndeliverableException致安卓应用重装后崩溃排查

崩溃问题排查与解决

问题背景

应用基于RxJava开发,修改版本号后执行清缓存、清数据、卸载重装操作,出现异常流程:跳过闪屏页后的引导、登录流程直接进入HomeFragment,随后发生崩溃。应用正常流程应为:Splash > Onboarding > Login > Home。

崩溃日志

FATAL EXCEPTION: main
             Process: com.viva.yag, PID: 27013
             io.reactivex.exceptions.UndeliverableException: The exception could not be delivered to the consumer because it has already canceled/disposed the flow or the exception has nowhere to go to begin with. Further reading: https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling | android.content.res.Resources$NotFoundException: String resource ID #0x7f140091
                at io.reactivex.plugins.RxJavaPlugins.onError(RxJavaPlugins.java:367)
                at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:126)
                at android.os.Handler.handleCallback(Handler.java:942)
                at android.os.Handler.dispatchMessage(Handler.java:99)
                at android.os.Looper.loopOnce(Looper.java:211)
                at android.os.Looper.loop(Looper.java:300)
                at android.app.ActivityThread.main(ActivityThread.java:8503)
                at java.lang.reflect.Method.invoke(Native Method)
                at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:561)
                at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:954)
             Caused by: android.content.res.Resources$NotFoundException: String resource ID #0x7f140091
                at android.content.res.Resources.getText(Resources.java:480)
                at android.content.res.MiuiResources.getText(MiuiResources.java:106)
                at android.content.res.Resources.getString(Resources.java:573)
                at com.viva.yag.source.remote.networkViewModel.NetworkViewModel$makePromotionsApiCall$2.onSuccess(NetworkViewModel.kt:1591)
                at com.viva.yag.source.remote.networkViewModel.NetworkViewModel$makePromotionsApiCall$2.onNext(NetworkViewModel.kt:1639)
                at com.viva.yag.source.remote.networkViewModel.NetworkViewModel$makePromotionsApiCall$2.onNext(NetworkViewModel.kt:1575)
                at io.reactivex.internal.observers.DisposableLambdaObserver.onNext(DisposableLambdaObserver.java:58)
                at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.drainNormal(ObservableObserveOn.java:201)
                at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.run(ObservableObserveOn.java:255)
                at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:124)
                at android.os.Handler.handleCallback(Handler.java:942) 
                at android.os.Handler.dispatchMessage(Handler.java:99) 
                at android.os.Looper.loopOnce(Looper.java:211) 
                at android.os.Looper.loop(Looper.java:300) 
                at android.app.ActivityThread.main(ActivityThread.java:8503) 
                at java.lang.reflect.Method.invoke(Native Method) 
                at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:561) 
                at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:954)

关联代码(NetworkViewModel)

class NetworkViewModel: BaseViewModel() {
       fun makePromotionsApiCall(){
    mDataManager.requestToGetPromotions()
        .doOnSubscribe {
            mGetPromotions.value = ApiResponseObserver.loading()
        }?.subscribe(object: CustomObserver<Response<MainResponseModel>>(){
            override fun onSuccess(t: Response<MainResponseModel>) {
                if (t.isSuccessful)
                {
                    mGetPromotions.value = ApiResponseObserver.success(t.body()?.response)
                }
                else {
                    val error: MainResponseModel? = ErrorUtils.parseError(t)
                    when {
                        t.code() == 401 -> {

                            mGetPromotions.value = ApiResponseObserver.expire(
                                ErrorResponse(
                                    error?.response?.status ?: false,
                                    error?.response?.message ?: Resources.getSystem()
                                        .getString(
                                            R.string.error
                                        ),
                                    t.code()
                                )
                            )
                        }
                        t.code() == 404 -> {
                            mGetPromotions.value = ApiResponseObserver.expire(
                                ErrorResponse(
                                    error?.response?.status ?: false,
                                    error?.response?.message ?: Resources.getSystem()
                                        .getString(
                                            R.string.error
                                        ),
                                    t.code()
                                )
                            )
                        }
                        t.code() == 500 -> {
                            mGetPromotions.value = ApiResponseObserver.expire(
                                ErrorResponse(
                                    error?.response?.status ?: false,
                                    error?.response?.message ?: Resources.getSystem()
                                        .getString(
                                            R.string.error
                                        ),
                                    t.code()
                                )
                            )
                        }
                        else -> {
                            mGetPromotions.value = ApiResponseObserver.error(
                                ErrorResponse(
                                    error?.response?.status ?: false,
                                    error?.response?.message ?: Resources.getSystem()
                                        .getString(
                                            R.string.error
                                        ),
                                    t.code()
                                )
                            )
                        }
                    }
                }
            }
            override fun onNext(t: Response<MainResponseModel>) {

                onSuccess(t)
            }
            override fun onError(e: Throwable, isInternetError: Boolean, error: CustomError?) {
                mGetPromotions.value= ApiResponseObserver.error(error?.code?.let {
                    ErrorResponse(false,error.message,it)
                })
            }
            override fun onRequestComplete() {
            }
        })
}}

原因分析

  1. 资源访问错误:崩溃核心原因是Resources$NotFoundException,代码中使用Resources.getSystem().getString(R.string.error)尝试获取应用内字符串资源,但Resources.getSystem()仅能访问系统级资源,无法读取应用自身res目录下的资源,导致找不到R.string.error对应的资源ID。
  2. RxJava异常传递问题:上述资源异常抛出时,RxJava的观察者可能已被取消或销毁(比如HomeFragment未完成初始化就接收事件),导致异常无法被正常处理,进而触发UndeliverableException引发崩溃。
  3. 异常流程影响:卸载重装后跳过登录流程,可能是本地状态缓存残留或版本号修改导致登录状态判断逻辑错误,虽然不是直接崩溃原因,但会让未初始化的HomeFragment提前加载,放大了资源访问错误的影响。

解决方案

1. 修复资源访问逻辑

将Resources.getSystem()替换为应用上下文的Resources,ViewModel中应注入Application Context(避免内存泄漏):

// 修改NetworkViewModel构造,注入Application Context
class NetworkViewModel(private val appContext: Application) : BaseViewModel() {
    fun makePromotionsApiCall(){
        mDataManager.requestToGetPromotions()
            .doOnSubscribe {
                mGetPromotions.value = ApiResponseObserver.loading()
            }?.subscribe(object: CustomObserver<Response<MainResponseModel>>(){
                override fun onSuccess(t: Response<MainResponseModel>) {
                    if (t.isSuccessful) {
                        mGetPromotions.value = ApiResponseObserver.success(t.body()?.response)
                    } else {
                        val error: MainResponseModel? = ErrorUtils.parseError(t)
                        when {
                            t.code() == 401 -> {
                                mGetPromotions.value = ApiResponseObserver.expire(
                                    ErrorResponse(
                                        error?.response?.status ?: false,
                                        // 使用应用上下文获取资源
                                        error?.response?.message ?: appContext.getString(R.string.error),
                                        t.code()
                                    )
                                )
                            }
                            // 其他分支同理替换Resources.getSystem()
                            // ...
                        }
                    }
                }
                // 其他方法保持不变
                // ...
            })
    }
}

2. 确保资源存在

检查res/values/strings.xml文件,确认是否存在error字符串资源,若缺失则添加:

<string name="error">未知错误</string>

3. 全局处理RxJava未交付异常

在Application初始化时配置RxJava全局错误处理器,避免未处理的异常引发崩溃:

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        RxJavaPlugins.setErrorHandler { throwable ->
            // 仅打印日志,避免崩溃扩散
            Log.e("RxJava", "Undeliverable exception", throwable)
        }
    }
}

4. 修复流程跳过问题

检查登录状态判断逻辑,确认卸载重装后本地缓存(如SharedPreferences)是否被彻底清除,或版本号修改是否导致状态判断错误,确保未登录用户必须走引导、登录流程。

内容的提问来源于stack exchange,提问作者Sahar Batool

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 18:17:02