调用rx.Observable.sample()时如何接收最后一个序列发射?

时间:2014-10-01 15:50:34

标签: java android reactive-programming rx-java

我正在从远程URL读取文件并使用RxJava报告下载进度。文件编写器Observable发出一系列DownloadProgress对象。因为正在发射大量物品,所以我使用Observable.sample()来管理背压。这非常有效 - UI更新以恒定速率进行,并且没有背压问题,但几乎总是跳过最后一次进度更新。

我想收到Observable序列中的最后一项,以便我可以使用最终进度更新UI。什么是确保始终发出Observable序列中最后一项的最佳方法?

Observable<Response> fileReader =
        Rx.okHttpGetRequest(url);
OkHttpResponseWriter fileWriter =
        Rx.okHttpResponseWriter(outFile);

Subscription subscription = fileReader.flatMap(fileWriter)
        .sample(1, TimeUnit.SECONDS)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Subscriber<DownloadProgress>() {
            @Override
            public void onCompleted() {}
            @Override
            public void onError(Throwable e) {}
            @Override
            public void onNext(DownloadProgress progress) {
                // I want to receive an update every one second
                // I also want to always receive the last progress update
            }
        });

3 个答案:

答案 0 :(得分:3)

或者,使用buffer代替sample,可以使用&#34;额外&#34;最后的那些。我建议这样做,这样你就不会失去任何可组合性或为未来的开发者(包括未来的你)引入头痛。

private Subscriber<List<Integer>> loggingSubscriber2 = new SimpleSubscriber<List<Integer>>() {
    @Override
    public void onNext(List<Integer> integers) {
        Log.v(TAG, String.valueOf(integers.get(integers.size() - 1)));
    }
};

private void startObservableTests() {
    Observable<Integer> fileObserver = Observable.create(
            new Observable.OnSubscribe<Integer>() {
                @Override
                public void call(Subscriber<? super Integer> subscriber) {
                    for (int i = 1; i <= 9; i++) {
                        subscriber.onNext(i);
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    subscriber.onCompleted();
                }
            }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());

    fileObserver.buffer(500, TimeUnit.MILLISECONDS).subscribe(loggingSubscriber2);
}

答案 1 :(得分:2)

这是我结束的地方:

private void startObservableTests() {

    Observable<Integer> fileObserver = Observable.create(
            new Observable.OnSubscribe<Integer>() {
                @Override
                public void call(Subscriber<? super Integer> subscriber) {
                    for (int i = 1; i <= 9; i++) {
                        subscriber.onNext(i);
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    subscriber.onCompleted();
                }
            }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());


    fileObserver.sample(500, TimeUnit.MILLISECONDS).subscribe(new SuperSubscriber(fileObserver));
}

private class SuperSubscriber extends Subscriber<Integer> {

    Observable obs;

    public SuperSubscriber(Observable<Integer> fileObserver) {
        obs = fileObserver;
    }

    @Override
    public void onCompleted() {
        obs.last().subscribe(new SimpleSubscriber<Integer>() {
            @Override
            public void onNext(Integer o) {
                Log.v(TAG, "final value was " + o);
            }
        });
    }

    @Override
    public void onError(Throwable e) {

    }

    @Override
    public void onNext(Integer o) {
        Log.v(TAG, "got a " + o);
    }
}

这是输出:

10-01 15:18:36.893 V/TAG  (26129): got a 5
10-01 15:18:38.214 V/TAG  (26129): final value was 9

答案 2 :(得分:1)

基于@Travis发布的优秀解决方案,这是我最终的最终代码。我使用bufferwindow结合使用而不是switchOnNext来进行进一步处理:

Observable<Response> fileReader =
    Rx.okHttpGetRequest(fileInfo.getUrl());
OkHttpResponseWriter fileWriter =
    Rx.okHttpResponseWriter(outFile, totalBytesRead);
Subscription subscription = Observable.switchOnNext(fileReader
        .flatMap(fileWriter)
        .window(1, TimeUnit.SECONDS))
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Subscriber<DownloadProgress>() {
            @Override
            public void onCompleted() {}
            @Override
            public void onError(Throwable e) {}
            @Override
            public void onNext(DownloadProgress progress) {
                // always emits the most recent DownloadProgress, even the last one!
            }
});

说明:

  • fileReader获取网址,创建HTTP请求并发出Response
  • fileReader.flatMap(fileWriter)获取Response并将字节流写入磁盘,发出一系列DownloadProgress个对象
  • window(1, TimeUnit.Seconds)每秒发出List<DownloadProgress>个对象,然后打包并以Observable<DownloadProgress>
  • 重新发出它们
  • Observable.switchOnNext()获取最近发出的Observable<DownloadProgress>并发出序列中的最后一个DownloadProgress对象