调用CompositeDisposable.clear导致OkHttp抛出java.lang.IllegalStateException异常
解决RxJava+OkHttp请求时出现的Okio Unbalanced enter/exit异常
这个问题我之前排查过好几次,本质是异步请求的生命周期管理和Okio的超时机制冲突导致的,咱们一步步说清楚原因和解决办法:
问题根源
当你在onStop()里调用CompositeDisposable.clear()时,RxJava会立刻中断正在运行的Observable(也就是你的OkHttp请求)。而OkHttp内部依赖Okio的AsyncTimeout来处理请求超时,粗暴中断请求会打乱它的enter()和exit()方法的调用流程——这两个方法必须成对调用,一旦次数不匹配,就会抛出你看到的IllegalStateException: Unbalanced enter/exit。
可行的解决方案
1. 调整Disposable的清理时机
优先在onDestroy()而不是onStop()里清理CompositeDisposable:
onStop()会在Activity/Fragment退到后台时触发,这时候请求还在执行,强行中断很容易触发Okio的这个问题;onDestroy()是组件真正销毁时才调用的,这时候取消请求更合理,也能避免生命周期和异步请求的冲突。
代码示例:
private CompositeDisposable compositeDisposable = new CompositeDisposable(); @Override protected void onDestroy() { super.onDestroy(); if (compositeDisposable != null && !compositeDisposable.isDisposed()) { compositeDisposable.clear(); } }
2. 优雅取消OkHttp请求,避免粗暴中断
不要依赖CompositeDisposable的clear来直接中断请求,而是把OkHttp的Call和RxJava的订阅绑定,在取消订阅时主动调用Call.cancel():
OkHttpClient okHttpClient = new OkHttpClient(); Request request = new Request.Builder() .url("https://your-api-url.com") .build(); Call httpCall = okHttpClient.newCall(request); Observable<String> requestObservable = Observable.fromCallable(() -> { Response response = httpCall.execute(); if (!response.isSuccessful()) { throw new IOException("Request failed: " + response.code()); } return response.body().string(); }); requestObservable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnDispose(httpCall::cancel) // 取消订阅时优雅取消OkHttp请求 .subscribe( responseBody -> { // 处理成功响应 }, throwable -> { // 处理错误 } ) .addTo(compositeDisposable);
3. 升级OkHttp版本
这个异常在OkHttp的一些旧版本(比如3.x的早期版本)里是已知的bug,官方在后续版本中修复了超时管理的逻辑。建议升级到OkHttp 4.x的稳定版本,大概率能直接解决这个问题。
额外注意点
- 永远不要在主线程执行OkHttp的同步请求,一定要通过
subscribeOn(Schedulers.io())把请求放到IO线程,这不仅是避免ANR的基础,也能减少一些异步流程的异常; - 如果确实需要在
onStop()里取消请求,可以试试用compositeDisposable.dispose()代替clear()——dispose()会销毁整个CompositeDisposable容器,中断逻辑更彻底,但要注意后续不能再往这个容器里添加Disposable了。
内容的提问来源于stack exchange,提问作者casolorz
相关产品推荐
相关产品推荐

