我正在从远程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
}
});
答案 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发布的优秀解决方案,这是我最终的最终代码。我使用buffer
与window
结合使用而不是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
对象