RxJava中CompositeDisposable引发两类异常:是否为同一问题?
这俩问题确实是同一根因导致的!
先给你拆解清楚背后的逻辑:
核心问题根源:线程处理失误 + 请求取消时的OkHttp回调失衡
你写的代码里,Observable.create() 直接在内部执行OkHttp网络请求,但没加 subscribeOn(Schedulers.io()) 把网络操作扔去后台线程。这就引发了连锁反应:
- 如果你的订阅操作(
subscribe())是在主线程触发的,那OkHttp的请求就直接在主线程跑了——这本身就违反Android的网络规则。当你调用CompositeDisposable.clear()时,如果网络请求还在主线程吭哧跑,自然就会炸出NetworkOnMainThreadException。 - 另一方面,
CompositeDisposable.clear()会触发所有Disposable的dispose(),这会取消正在跑的OkHttp请求。但OkHttp内部有个EventListener机制,需要enter()和exit()成对调用——比如请求刚触发callStart()(调用了enter()),结果中途被取消,对应的callEnd()或callFailed()没机会调用exit(),直接就抛出java.lang.IllegalStateException: Unbalanced enter/exit了。
两步解决所有问题
第一步:把网络操作强制扔去后台线程
给Observable加上subscribeOn(Schedulers.io()),确保OkHttp请求绝不会跑到主线程:
Observable<Stuff> observable = Observable.create(new ObservableOnSubscribe<Stuff>() { @Override public void subscribe(@NonNull ObservableEmitter<Stuff> e) throws Exception { // 执行OkHttp相关操作 if (!e.isDisposed()) { // 先检查Emitter没被销毁,避免白忙活 e.onNext(stuff); e.onComplete(); } } }) .subscribeOn(Schedulers.io()) // 这行是关键!把网络操作丢去IO线程 .observeOn(AndroidSchedulers.mainThread()); // 如果要更新UI,再切回主线程
第二步:让Emitter和OkHttp请求的生命周期绑定
在subscribe()方法里,把Emitter的销毁事件和OkHttp请求的取消操作关联起来,同时确保异常和回调都只在Emitter存活时执行:
@Override public void subscribe(@NonNull ObservableEmitter<Stuff> e) throws Exception { OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder().url("你的接口地址").build(); Call call = client.newCall(request); // 绑定:当Emitter被dispose时,自动取消OkHttp请求 e.setDisposable(Disposables.fromRunnable(() -> { call.cancel(); })); try { Response response = call.execute(); if (!e.isDisposed()) { // 把response解析成Stuff对象 Stuff stuff = parseResponse(response); e.onNext(stuff); e.onComplete(); } } catch (IOException ex) { if (!e.isDisposed()) { e.onError(ex); } } }
总结一下
这俩异常其实是同一个问题引发的连锁反应:先是没给网络操作指定后台线程,导致主线程跑网络;然后请求被取消时又没正确清理OkHttp的回调状态。按上面两步改完,就能一次性搞定这两个问题啦。
内容的提问来源于stack exchange,提问作者casolorz
相关产品推荐
相关产品推荐

