使用.cache运算符可以观察Rxjava User-Retry吗?

时间:2015-08-20 13:36:48

标签: reactive-programming rx-java

我使用以下代码创建了一个observable。

Observable.create(new Observable.OnSubscribe<ReturnType>() {

      @Override
      public void call(Subscriber<? super ReturnType> subscriber) {
        try {
          if (!subscriber.isUnsubscribed()) {
            subscriber.onNext(performRequest());
          }
          subscriber.onCompleted();
        } catch (Exception e) {
          subscriber.onError(e);
        }
      }
    });

performRequest()将按照您的预期执行长时间运行的任务。

现在,由于我可能会在很短的时间内启动相同 Observable两次或更多次,所以我决定编写这样的变换器:

  protected Observable.Transformer<ReturnType, ReturnType> attachToRunningTaskIfAvailable() {
    return origObservable -> {
      synchronized (mapOfRunningTasks) {
        // If not in maps
        if ( ! mapOfRunningTasks.containsKey(getCacheKey()) ) {
          Timber.d("Cache miss for %s", getCacheKey());
          mapOfRunningTasks.put(
              getCacheKey(),
              origObservable
                  .doOnTerminate(() -> {
                    Timber.d("Removed from tasks %s", getCacheKey());
                    synchronized (mapOfRunningTasks) {
                      mapOfRunningTasks.remove(getCacheKey());
                    }
                  })
                  .cache()
          );
        } else {
          Timber.d("Cache Hit for %s", getCacheKey());
        }
        return mapOfRunningTasks.get(getCacheKey());
      }
    };
  }

这基本上将原始的.cache可观察量放在HashMap<String, Observable>中。

这基本上不允许使用相同getCacheKey()(示例login)的多个请求并行调用performRequest()。相反,如果第二个login请求到达而另一个请求正在进行中,则第二个请求可观察到达&#34;被丢弃&#34;并且将使用已经运行的。 =&GT;拨打onNext的所有电话都将cached,然后发送给两个实际上仅在我的后端点击一次的用户。

现在,支持此代码:

// Observable loginTask
public void doLogin(Observable<UserInfo> loginTask) {
    loginTask.subscribe(
      (userInfo) -> {},
      (throwable) -> {
        if (userWantsToRetry()) {
            doLogin(loinTask);
        } 
      }
    );
}

loginTask由前一个变换器组成。好吧,当发生错误(可能是连接)和userWantsToRetry()时,我基本上会用相同的observable重新调用该方法。不幸的是,这已被缓存,我会收到相同的错误而不会再次点击performRequest(),因为序列会被重播。

有没有办法可以同时拥有&#34;相同的请求分组&#34;变压器为我提供的行为和重试按钮?

1 个答案:

答案 0 :(得分:0)

你的问题很多,很难直接说出来。我可以提出一些建议。首先,您可以使用Observable.create简化Observable.defer(Func0<Observable<T>>)。这将在每次订阅新订阅者时运行func并捕获并引导订阅者onError的任何异常。

Observable.defer(() -> {
    return Observable.just(performRequest());
});

接下来,您可以使用observable.repeatWhen(Func1<Observable<Void>, Observable<?>>)来决定何时重试。重复运算符将在onComplete事件之后重新订阅observable。当收到onComplete事件时,此特定重载将向主题发送事件。您提供的功能将收到此主题。当您不想再次重试时,您的函数应调用类似takeWhile(predicate)和onComplete的函数。

Observable.just(1,2,3).flatMap((Integer num) -> {
    final AtomicInteger tryCount = new AtomicInteger(0);
    return Observable.just(num)
        .repeatWhen((Observable<? extends Void> notifications) -> 
            notifications.takeWhile((x) -> num == 2 && tryCount.incrementAndGet() != 3));
})
.subscribe(System.out::println); 

输出:

1
2
2
2
3

上面的示例显示,当事件不是2并且最多重试22次时,重试会大声重复。如果切换到repeatWhen,那么flatMap将包含您使用缓存的observable或realWork observable的决定。希望这有帮助!