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

