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
相关产品推荐
相关产品推荐

