TL; DR
如何将Task.whenAll(List<Task>)
转换为RxJava
?
我现有的代码使用Bolts构建异步任务列表,并等待所有这些任务完成后再执行其他步骤。从本质上讲,它会构建一个List<Task>
并返回一个Task
,根据example on the Bolts site,当列表中的所有任务完成时,标记为已完成。{ / p>
我希望将Bolts
替换为RxJava
,而我假设这种方法可以构建一个异步任务列表(大小未提前知道)并将它们全部包装起来单个Observable
是可能的,但我不知道如何。
我已尝试查看merge
,zip
,concat
等...但无法继续使用List<Observable>
我{如果我理解正确的文档,那么他们似乎只需要同时处理两个Observables
。
我试图学习RxJava
并且仍然是新手,所以请原谅我,如果这是一个明显的问题或在某个地方的文档中解释过;我试过搜索。任何帮助将不胜感激。
答案 0 :(得分:68)
如果您拥有动态任务合成,则可以使用flatMap
。像这样:
public Observable<Boolean> whenAll(List<Observable<Boolean>> tasks) {
return Observable.from(tasks)
//execute in parallel
.flatMap(task -> task.observeOn(Schedulers.computation()))
//wait, until all task are executed
//be aware, all your observable should emit onComplemete event
//otherwise you will wait forever
.toList()
//could implement more intelligent logic. eg. check that everything is successful
.map(results -> true);
}
Another good example of parallel execution
注意:我真的不知道您对错误处理的要求。例如,如果只有一个任务失败,该怎么办。我认为你应该验证这种情况。
答案 1 :(得分:54)
听起来你正在寻找Zip operator。
有几种不同的使用方法,让我们看一个例子。假设我们有一些不同类型的简单可观察量:
Observable<Integer> obs1 = Observable.just(1);
Observable<String> obs2 = Observable.just("Blah");
Observable<Boolean> obs3 = Observable.just(true);
等待它们的最简单方法是这样的:
Observable.zip(obs1, obs2, obs3, (Integer i, String s, Boolean b) -> i + " " + s + " " + b)
.subscribe(str -> System.out.println(str));
请注意,在zip函数中,参数具有与要压缩的可观察对象类型相对应的具体类型。
也可以直接压缩可观察列表:
List<Observable<?>> obsList = Arrays.asList(obs1, obs2, obs3);
Observable.zip(obsList, (i) -> i[0] + " " + i[1] + " " + i[2])
.subscribe(str -> System.out.println(str));
...或将列表包装到Observable<Observable<?>>
:
Observable<Observable<?>> obsObs = Observable.from(obsList);
Observable.zip(obsObs, (i) -> i[0] + " " + i[1] + " " + i[2])
.subscribe(str -> System.out.println(str));
但是,在这两种情况下,zip函数只能接受单个Object[]
参数,因为列表中的observable类型事先不知道,也不知道它们的编号。这意味着zip函数必须检查参数的数量并相应地转换它们。
无论如何,上述所有示例最终都会打印1 Blah true
编辑:使用Zip时,请确保所有正在压缩的Observables
发出相同数量的项目。在上面的例子中,所有三个可观察者都发出了一个项目。如果我们将它们改为这样的话:
Observable<Integer> obs1 = Observable.from(new Integer[]{1,2,3}); //Emits three items
Observable<String> obs2 = Observable.from(new String[]{"Blah","Hello"}); //Emits two items
Observable<Boolean> obs3 = Observable.from(new Boolean[]{true,true}); //Emits two items
然后1, Blah, True
和2, Hello, True
将成为传递给zip函数的唯一项目。由于其他可观测量已完成,因此永远不会压缩项目3
。
答案 2 :(得分:8)
在提出的建议中,zip()实际上彼此结合了可观察到的结果,可能是(也可能不是)想要的结果,但未在问题中提出。在这个问题中,所需要的只是每个操作的执行,要么是一对一或并行执行(未指定,但是链接的Bolts示例是关于并行执行的)。另外,当任何可观察对象完成时,zip()将立即完成,因此违反了要求。
对于Observables的并行执行,flatMap()presented in the other answer很好,但是merge()会更直接。请注意,合并将在任何一个Observable发生错误时退出,如果您宁愿将退出推迟到所有Observable完成之前,则应该查看mergeDelayError()。
我认为应该Observable.concat() static method一一使用。其javadoc状态如下:
concat(java.lang.Iterable>序列) 将可迭代的可观测变量平整化为一个可观测变量,一个又一个,而不进行交织
如果您不想并行执行,这听起来像您想要的。
此外,如果您仅对完成任务感兴趣,而不对返回值感兴趣,则应该考虑使用Completable而不是Observable。
TLDR:对于任务和oncompletion事件完成时的一对一执行,我认为Completable.concat()最适合。对于并行执行,Completable.merge()或Completable.mergeDelayError()听起来像解决方案。前者将立即停止对任何可完成对象的任何错误,而后者将即使它们中的一个有错误也将全部执行,然后才报告错误。
答案 3 :(得分:3)
用Kolin
Observable.zip(obs1, obs2, BiFunction { t1 : Boolean, t2:Boolean ->
})
设置函数参数的类型很重要,否则会出现编译错误
最后一个参数类型随参数的数量而变化: BiFunction为2 功能3为3 功能4为4 ...
答案 4 :(得分:1)
您可能已经查看了使用2个Observables的zip
运算符。
还有静态方法Observable.zip
。它有一个对你有用的形式:
zip(java.lang.Iterable<? extends Observable<?>> ws, FuncN<? extends R> zipFunction)
您可以查看javadoc for more.
答案 5 :(得分:1)
我正在使用JavaRx Observables和RxKotlin在Kotlin中编写一些计算升沉代码。我想观察要完成的可观察列表,同时向我提供有关进度和最新结果的更新。最后它返回最佳计算结果。额外的要求是并行运行Observables以使用我所有的cpu核心。我最终得到了这个解决方案:
@Volatile var results: MutableList<CalculationResult> = mutableListOf()
fun doALotOfCalculations(listOfCalculations: List<Calculation>): Observable<Pair<String, CalculationResult>> {
return Observable.create { subscriber ->
Observable.concatEager(listOfCalculations.map { calculation: Calculation ->
doCalculation(calculation).subscribeOn(Schedulers.computation()) // function doCalculation returns an Observable with only one result
}).subscribeBy(
onNext = {
results.add(it)
subscriber.onNext(Pair("A calculation is ready", it))
},
onComplete = {
subscriber.onNext(Pair("Finished: ${results.size}", findBestCalculation(results))
subscriber.onComplete()
},
onError = {
subscriber.onError(it)
}
)
}
}
答案 6 :(得分:0)
我有类似的问题,我需要从rest调用中获取搜索项目,同时还需要整合来自NearestSearchProvider.AUTHORITY的保存建议,并将它们组合到一个统一的列表中。我试图使用@MyDogTom解决方案,很遗憾,RxJava中没有Observable.from。经过一番研究,我找到了适合我的解决方案。
fun getSearchedResultsSuggestions(context : Context, query : String) : Single<ArrayList<ArrayList<SearchItem>>>
{
val fetchedItems = ArrayList<Observable<ArrayList<SearchItem>>>(0)
fetchedItems.add(fetchSearchSuggestions(context,query).toObservable())
fetchedItems.add(getSearchResults(query).toObservable())
return Observable.fromArray(fetchedItems)
.flatMapIterable { data->data }
.flatMap {task -> task.observeOn(Schedulers.io())}
.toList()
.map { ArrayList(it) }
}
我从可观察对象数组中创建了一个可观察对象,其中包含根据查询来自互联网的建议和结果列表。之后,您只需使用flatMapIterable查看这些任务,然后使用flatmap运行它们,然后将结果放置在数组中,然后可以将其提取到回收视图中。
答案 7 :(得分:0)
如果您使用Project Reactor,则可以使用Mono.when
。
Mono.when(publisher1, publisher2)
.map(i-> {
System.out.println("everything is done!");
return i;
}).block()