多API调用场景下,如何用retryWhen()实现IOException的3次重试?
解决RxJava Zip多个API调用时的IOException重试问题
首先,咱们来分析你现有代码的几个核心问题:
- 未区分异常类型:当前代码不管是什么异常都会触发重试,但你只需要针对
IOException进行重试 - 重试计数的线程安全隐患:你在
Function内部定义的retryCount会因为RxJava的线程调度机制,出现计数不准确的情况 - retryWhen的位置错误:你的重试逻辑没有正确附加到zip后的Observable链上,导致重试逻辑完全不生效
- zip操作存在语法错误:原代码里
getProductDetails后.subscribeOn(Schedulers.io())的位置不对,且setUserInfoModuleList存在变量名写错的问题
正确的实现方式
我们需要实现一个仅针对IOException、最多重试3次的可靠重试逻辑,并且正确整合到zip操作的Observable链中:
// 封装通用的IOException重试逻辑,方便复用 private ObservableTransformer<Object, Object> retryOnIOException(int maxRetries) { return upstream -> upstream.retryWhen(errors -> errors // 用zipWith结合range来安全计数,避免线程安全问题 .zipWith(Observable.range(1, maxRetries + 1), (throwable, retryCount) -> { // 仅在IOException且未达重试上限时允许重试 if (throwable instanceof IOException && retryCount <= maxRetries) { return retryCount; } // 其他异常或重试上限到了,直接抛出 throw Exceptions.propagate(throwable); }) .doOnNext(retryCount -> Log.e(TAG, "Exception retrying, count: " + retryCount)) // 可选:添加延迟重试,避免短时间内频繁请求服务器 .delay(1, TimeUnit.SECONDS)); } public void onClickLogin(View view) { Observable.zip( getLogin(Constants.EMAILID, Constants.PASSWORD), getUserInfo(Constants.EMAILID, Constants.PASSWORD), getProductDetails(Constants.EMAILID, Constants.PASSWORD), (loginModuleList, userInfoModules, productModule) -> { AllZipData allZipData = new AllZipData(); allZipData.setLoginModuleList(loginModuleList); allZipData.setUserInfoModuleList(userInfoModules); // 修正原代码的变量名错误 allZipData.setProductModule(productModule); return allZipData; }) .subscribeOn(Schedulers.io()) // 统一设置IO线程执行 .observeOn(AndroidSchedulers.mainThread()) // 主线程处理结果 .compose(retryOnIOException(3)) // 应用重试逻辑 .subscribe(new Observer<AllZipData>() { @Override public void onSubscribe(Disposable d) { compositeDisposable.add(d); } @Override public void onNext(AllZipData allZipData) { MyDatabase myDatabase = MyDatabase.getInstance(context); // 简化循环写法,更简洁安全 for (UserInfoModule module : allZipData.getUserInfoModuleList()) { UserInfoTable userInfoTable = new UserInfoTable(); userInfoTable.setValue1(module.getValue1()); myDatabase.userDao().insertUserInfo(userInfoTable); } } @Override public void onError(Throwable e) { Log.e(TAG, "onError: all zip data " + e.toString()); } @Override public void onComplete() { Log.e(TAG, "onComplete: all data zipped"); } }); }
关键细节说明
- 精准异常过滤:在
zipWith中只对IOException进行重试,其他异常直接抛出,符合你的需求 - 安全的重试计数:用
Observable.range生成重试次数,避免了成员变量计数的线程安全问题 - 可选延迟重试:添加
delay(1, TimeUnit.SECONDS)可以避免短时间内频繁请求服务器,你可以根据业务需求调整或移除 - 语法错误修复:修正了原代码中变量名写错、线程调度位置错误的问题
- 逻辑复用性:把重试逻辑封装成
ObservableTransformer,可以在其他Observable链上直接复用
如果你的需求是每个API调用各自独立重试(而非整个zip流程重试),只需要把retryOnIOException分别应用到三个API的Observable上即可,示例:
getLogin(Constants.EMAILID, Constants.PASSWORD) .compose(retryOnIOException(3))
内容的提问来源于stack exchange,提问作者Ronil Raut
相关产品推荐
相关产品推荐

