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

Android基于RxJava2实现可重复重定向跳转外部应用的方案咨询

RxJava2 实现重定向跳转外部App逻辑修复

现有实现的核心问题

  • 分支逻辑写反:重定向非空时才需要尝试拉起App,原有代码空重定向反而调用了拉起逻辑
  • 变量作用域错误:第二层flatMap无法获取上一层返回的redirect参数
  • 无重试循环逻辑:仅能执行最多2次重定向请求,无法满足拉起失败后继续获取下一个重定向重试的要求
  • 全局变量存最后重定向地址线程不安全:多并发调用时会出现值覆盖错乱

修正后实现代码

首先移除全局的lastRedirectUrl变量,改为在Rx流内部传递状态,避免线程安全问题:

// 中间状态载体,用于流内传递当前请求地址、最后有效重定向地址
private static class RedirectState {
    String currentUrl;
    String lastValidRedirect;

    public RedirectState(String currentUrl, String lastValidRedirect) {
        this.currentUrl = currentUrl;
        this.lastValidRedirect = lastValidRedirect;
    }
}

public Observable<Result> getStartAppResult(String initUrl, AppOpeningRule shopOpeningRule) {
    return Observable.defer(() -> Observable.just(new RedirectState(initUrl, "")))
            // 循环获取重定向+尝试拉起App
            .concatMap(state -> getSingleHttpRedirectUrl(state.currentUrl, shopOpeningRule)
                    .flatMap(redirect -> {
                        // 重定向为空,终止流程返回携带最后有效重定向的Result
                        if (Strings.isNullOrEmpty(redirect)) {
                            return Observable.just(new Result("", state.lastValidRedirect));
                        }
                        // 更新最后有效重定向地址
                        String newLastRedirect = redirect;
                        // 尝试拉起App
                        return isAppStarted(redirect, shopOpeningRule.getPackageName())
                                .flatMap(appStarted -> {
                                    if (appStarted) {
                                        // 拉起成功,返回标识已打开的Result
                                        return Observable.just(new Result(true, newLastRedirect));
                                    } else {
                                        // 拉起失败,用当前重定向作为下一次请求的url,进入下一轮循环
                                        RedirectState nextState = new RedirectState(redirect, newLastRedirect);
                                        Result continueResult = new Result();
                                        continueResult.setNeedContinue(true);
                                        continueResult.setNextState(nextState);
                                        return Observable.just(continueResult);
                                    }
                                });
                    })
            )
            // 最多重试10次,避免死循环
            .repeatWhen(completed -> completed
                    .zipWith(Observable.range(1, 10), (obj, count) -> count)
                    .flatMap(count -> count >= 10 ? 
                            Observable.error(new IllegalStateException("重定向次数超过上限")) : 
                            Observable.just(true))
            )
            // 遇到不需要继续的Result就终止流程
            .takeUntil(result -> !result.isNeedContinue())
            .filter(result -> !result.isNeedContinue())
            // 异常兜底,返回最后一次有效重定向
            .onErrorResumeNext(throwable -> Observable.just(new Result("", "")));
}

// 修正getSingleHttpRedirectUrl,不再修改全局变量
private Observable<String> getSingleHttpRedirectUrl(String url, final ShopOpeningRule appOpeningRule) {
    return httpService.goToWebSiteLink(url)
            .compose(rxTransformers.applyUnauthorizedHandlerCustomError())
            .onErrorResumeNext(throwable -> {
                if (throwable instanceof RetrofitException) {
                    RetrofitException error = (RetrofitException) throwable;
                    if (error.getKind() == Kind.HTTP) {
                        if (error.getHttpExceptionCode() >= REDIRECT_START && error.getHttpExceptionCode() <= REDIRECT_END) {
                            String redirectUrl = error.getResponse().headers().get(KEY_HEADER_REDIRECT_LOCATION);
                            if (!Strings.isNullOrEmpty(redirectUrl)) {
                                if (appOpeningRule.hasRedirectLinkPatterns()) {
                                    if (LinkParser.urlContainsPattern(redirectUrl, appOpeningRule.getRedirectLinkPatterns())) {
                                        String modifiedLink = LinkParser.modifyRedirectLinkIfNeeded(redirectUrl, appOpeningRule);
                                        return Observable.just(modifiedLink);
                                    } else {
                                        return Observable.just(redirectUrl);
                                    }
                                } else {
                                    return Observable.just(redirectUrl);
                                }
                            }
                        }
                    }
                }
                return Observable.just("");
            });
}

// 拉起App方法补充版本适配,FLAG_ACTIVITY_REQUIRE_NON_BROWSER仅API30及以上支持
public Observable<Boolean> isAppStarted(String url, String appPackageName) {
    return Observable.create(emitter -> {
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
        int flags = Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            flags |= Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER;
        }
        intent.setFlags(flags);
        intent.setPackage(appPackageName);

        boolean hasActivityNotFoundException = false;
        try {
            context.startActivity(intent);
        } catch (ActivityNotFoundException exception) {
            hasActivityNotFoundException = true;
        } finally {
            emitter.onNext(!hasActivityNotFoundException);
            emitter.onComplete();
        }
    });
}

补充说明

  • Result类需要自行扩展needContinue、nextState字段用于循环控制,也可以根据自己的业务需求调整字段结构
  • 最大10次的重定向上限可根据业务需求调整,避免出现无限循环的异常情况
  • 移除了全局变量的使用,所有状态都在Rx流内部传递,支持多场景并发调用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 13:15:04