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

多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");
                }
            });
}

关键细节说明

  1. 精准异常过滤:在zipWith中只对IOException进行重试,其他异常直接抛出,符合你的需求
  2. 安全的重试计数:用Observable.range生成重试次数,避免了成员变量计数的线程安全问题
  3. 可选延迟重试:添加delay(1, TimeUnit.SECONDS)可以避免短时间内频繁请求服务器,你可以根据业务需求调整或移除
  4. 语法错误修复:修正了原代码中变量名写错、线程调度位置错误的问题
  5. 逻辑复用性:把重试逻辑封装成ObservableTransformer,可以在其他Observable链上直接复用

如果你的需求是每个API调用各自独立重试(而非整个zip流程重试),只需要把retryOnIOException分别应用到三个API的Observable上即可,示例:

getLogin(Constants.EMAILID, Constants.PASSWORD)
        .compose(retryOnIOException(3))

内容的提问来源于stack exchange,提问作者Ronil Raut

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:05:56