RxJava。顺序执行

时间:2017-11-13 12:59:48

标签: android rx-java rx-java2

在我的Android应用程序中,我有一个处理用户交互的演示者,包含一种请求管理器,如果需要,还可以通过请求管理器将用户输入发送给请求管理器。

请求管理器本身包含服务器API并使用此RxJava处理服务器请求。

我有一个代码,每次用户输入消息并向服务器显示响应时,它会向服务器发送请求:

private Observable<List<Answer>> sendRequest(String request) {
    MyRequest request = new MyRequest();
    request.setInput(request);
    return Observable.fromCallable(() -> serverApi.process(request))
            .doOnNext(myResponse -> {
                // store some data
            })
            .map(MyResponse::getAnswers)
            .subscribeOn(Schedulers.newThread())
            .observeOn(AndroidSchedulers.mainThread());
}

但是现在我需要排队。用户可以在服务器响应之前发送新消息。应按顺序处理队列中的每条消息。即在我们得到对第一条消息的响应后,将发送第二条消息,依此类推。

如果发生错误,则不应处理其他请求。

我还需要在RecyclerView中显示答案。

我不知道如何更改上面的代码来实现上述处理

我看到了一些问题。一方面,这个队列可以随时由用户更新,另一方面,任何时候服务器发送响应消息应该从队列中删除。

也许有一个rxjava运算符或我错过的特殊方式。

我在这里看到了类似的答案,然而,&#34;队列&#34;有不变的。 Making N sequential api calls using RxJava and Retrofit

我非常感谢任何解决方案或链接

4 个答案:

答案 0 :(得分:5)

我没有找到任何优雅的native-RxJava解决方案。所以我会定制一个Subscriber来完成你的工作。

对于你的3分:

  1. 对于顺序执行,我们创建一个单线程调度程序

    Scheduler sequential = Schedulers.from(Executors.newFixedThreadPool(1));

  2. 为了在发生错误时停止所有请求,我们应该同时订阅所有请求,而不是每次都创建Flowable。所以我们定义了以下函数(这里我请求Integer和响应String):

    void sendRequest(Integer request)

    Flowable<String> reciveResponse()

    并定义一个字段以建立请求和响应流的关联:

    FlowableProcessor<Integer> requestQueue = UnicastProcessor.create();

  3. 为了重新运行未发送的请求,我们定义了重新运行功能:

    void rerun()

  4. 然后我们可以使用它:

    reciveResponse().subscribe(/**your subscriber**/)
    

    现在让我们实施它们。

    发送请求时,我们只需将其推送到requestQueue

    即可
    public void sendRequest(Integer request) {
      requestQueue.onNext(request);
    }
    

    首先,要按顺序执行请求,我们应该将工作安排到sequential

    requestQueue
      .observeOn(sequential)
      .map(i -> mockLongTimeRequest(i)) // mock for your serverApi.process
      .observeOn(AndroidSchedulers.mainThread());
    

    其次,在发生错误时停止请求。这是默认行为。如果我们什么都不做,则错误将导致订阅中断,并且不会发出任何更多项目。

    第三,重新运行未发送的请求。首先是因为本机运算符将取消流,如MapSubscriber do(RxJava-2.1.0-FlowableMap#63):

    try {
        v = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper function returned a null value.");
    } catch (Throwable ex) {
        fail(ex);// fail will call cancel
        return;
    }
    

    我们应该包装错误。在这里,我使用我的Try类来包装可能的异常,你可以使用任何其他可以包装异常而不是抛出它的实现:

        .map(i -> Try.to(() -> mockLongTimeRequest(i)))
    

    然后是自定义OnErrorStopSubscriber implements Subscriber<Try<T>>, Subscription

    它正常请求和发出项目。当发生错误时(实际上是发出的Try失败)它停在那里并且不会请求或发出甚至下游请求它。调用rerun方法后,它将返回正在运行的statu并正常发出。这节课大约有80行。您可以在my github上看到代码。

    现在我们可以测试我们的代码:

    public static void main(String[] args) throws InterruptedException {
      Q47264933 q = new Q47264933();
      IntStream.range(1, 10).forEach(i -> q.sendRequest(i));// emit 1 to 10
      q.reciveResponse().subscribe(e -> System.out.println("\tdo for: " + e));
      Thread.sleep(10000);
      q.rerun(); // re-run after 10s
      Thread.sleep(10000);// wait for it complete because the worker thread is deamon
    }
    
    private String mockLongTimeRequest(int i) {
      Thread.sleep((long) (1000 * Math.random()));
      if (i == 5) {
        throw new RuntimeException(); // error occur when request 5
      }
      return Integer.toString(i);
    }
    

    并输出:

    1 start at:129
    1 done  at:948
    2 start at:950
        do for: 1
    2 done  at:1383
    3 start at:1383
        do for: 2
    3 done  at:1778
    4 start at:1778
        do for: 3
    4 done  at:2397
    5 start at:2397
        do for: 4
    error happen: java.lang.RuntimeException
    6 start at:10129
    6 done  at:10253
    7 start at:10253
        do for: 6
    7 done  at:10415
    8 start at:10415
        do for: 7
    8 done  at:10874
    9 start at:10874
        do for: 8
    9 done  at:11544
        do for: 9
    

    你可以看到它按顺序运行。并在发生错误时停止。在调用rerun方法后,它继续处理左未发送请求。

    For complete code, see my github.

答案 1 :(得分:1)

对于这种行为,我正在使用Flowable背压实施。 创建api请求流的父流外部流,flatMap使用maxConcurrency = 1的api请求并实现某种缓冲策略,因此Flowable不会抛出异常。

Flowable.create(emitter -> {/* user input stream*/}, BackpressureStrategy.BUFFER)
                .onBackpressureBuffer(127, // buffer size
                        () -> {/* overflow action*/},
                        BackpressureOverflowStrategy.DROP_LATEST) // action when buffer exceeds 127
                .flatMap(request -> sendRequest(request), 1) // very important parameter
                .subscribe(results -> {
                    // work with results
                }, error -> {
                    // work with errors
                });

它将缓冲用户输入达到给定的阈值,然后将其删除(如果你不这样做会抛出异常,但用户不太可能超过这样的缓冲区),它会逐个执行1像一个队列。如果在libary本身中存在某种行为的运算符,请不要尝试自己实现此行为。

哦,我忘了提及,您的sendRequest()方法必须返回Flowable,或者您可以将其转换为Flowable。

希望这有帮助!

答案 2 :(得分:1)

我建议创建异步可观察方法,这里有一个示例:

public Observable<Integer> sendRequest(int x){
    return Observable.defer(() -> {
        System.out.println("Sending Request : you get Here X ");
        return storeYourData(x);
    });
}

public Observable<Integer> storeYourData(int x){
    return Observable.defer(() -> {
        System.out.println("X Stored : "+x);
        return readAnswers(x);
    }).doOnError(this::handlingStoreErrors);
}

public Observable<Integer> readAnswers(int h){
    return Observable.just(h);
}

public void handlingStoreErrors(Throwable throwable){
        //Handle Your Exception.
}

第一个observable将在发送请求时发送请求将继续第二个并且你可以链接,你可以自定义每个方法来处理错误或成功,这个样本就像队列一样。

此处执行结果:

for (int i = 0; i < 1000; i++) {
        rx.sendRequest(i).subscribe(integer -> System.out.println(integer));

}
Sending Request : you get Here X 
X Stored : 0
0
Sending Request : you get Here X 
X Stored : 1
1
Sending Request : you get Here X 
X Stored : 2
2
Sending Request : you get Here X 
X Stored : 3
3
.
.
.
Sending Request : you get Here X 
X Stored : 996
996
Sending Request : you get Here X 
X Stored : 997
997
Sending Request : you get Here X 
X Stored : 998
998
Sending Request : you get Here X 
X Stored : 999
999

答案 3 :(得分:0)

我的解决方案如下(我之前在Swift中做了类似的事情):

  1. 对于请求和响应,您将需要一个包装器界面(让我们称之为&#34;事件&#34;)。
  2. 你需要一个状态对象(让它成为类&#34;状态&#34;),它将包含请求队列和最新的服务器响应,以及一个接受&#34; Event&#的方法34;作为参数并返回&#39;此&#39;。
  3. 您的主处理链将看起来像 Observable state = Observable.merge(serverResponsesMappedToEventObservable,requestsMappedToEventObservable).scan(new State(),(state,event) - &gt; {state.apply(event)})< / em>的
  4. .merge()方法的两个参数可能都是主题。
  5. 队列处理将在&#34; State&#34;的唯一方法中进行。 object(在任何事件上从队列中挑选和发送请求,在请求事件中添加到队列,在响应事件上更新最新响应)。