你什么时候在RxJava中使用map vs flatMap?
你什么时候在RxJava中使用map vs flatMap?
举个例子,我们想把包含JSON的文件映射到包含JSON的string –
使用地图,我们必须以某种方式处理exception。 但是如何?
Observable.from(jsonFile).map(new Func1<File, String>() { @Override public String call(File file) { try { return new Gson().toJson(new FileReader(file), Object.class); } catch (FileNotFoundException e) { // So Exception. What to do ? } return null; // Not good :( } });
使用flatMap,它更加冗长,但是我们可以将问题转发到Observable链,并且处理错误,如果我们select别的地方,甚至重试:
Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() { @Override public Observable<String> call(final File file) { return Observable.create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { try { String json = new Gson().toJson(new FileReader(file), Object.class); subscriber.onNext(json); subscriber.onCompleted(); } catch (FileNotFoundException e) { subscriber.onError(e); } } }); } });
我喜欢简单的地图,但平面地图的error handling(而不是冗长)。 我还没有看到任何有关这方面的最佳做法,我很好奇这是如何在实践中使用的。
map
将一个事件转换为另一个。 flatMap
将一个事件转换为零个或多个事件。 (这是来自IntroToRx )
当你想把你的json转换成一个对象时,使用map就足够了。
处理FileNotFoundException是另一个问题(使用map或flatmap不能解决这个问题)。
为了解决你的exception问题,只要把它与一个非检查exception:RX将调用你的onError处理程序。
Observable.from(jsonFile).map(new Func1<File, String>() { @Override public String call(File file) { try { return new Gson().toJson(new FileReader(file), Object.class); } catch (FileNotFoundException e) { // this exception is a part of rx-java throw OnErrorThrowable.addValueAsLastCause(e, file); } } });
与flatmap完全相同的版本:
Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() { @Override public Observable<String> call(File file) { try { return Observable.just(new Gson().toJson(new FileReader(file), Object.class)); } catch (FileNotFoundException e) { // this static method is a part of rx-java. It will return an exception which is associated to the value. throw OnErrorThrowable.addValueAsLastCause(e, file); // alternatively, you can return Obersable.empty(); instead of throwing exception } } });
你也可以在flatMap版本中返回一个新的Observable,它只是一个错误。
Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() { @Override public Observable<String> call(File file) { try { return Observable.just(new Gson().toJson(new FileReader(file), Object.class)); } catch (FileNotFoundException e) { return Observable.error(OnErrorThrowable.addValueAsLastCause(e, file)); } } });
FlatMap的performance非常像map,不同之处在于它本身返回一个observable,所以它非常适合映射到asynchronous操作上。 Map不必发出与源Observable相同types的项目。
在实际意义上,Map只是对链式响应进行转换(不返回Observable); 而FlatMap返回一个Observable<T>
,这就是为什么如果你打算在方法内部进行一个asynchronous调用,build议使用FlatMap,那么:
- 地图返回typesT的对象
- FlatMap返回一个
Observable<T>
。
一个清晰的例子可以在这里看到: http : //blog.couchbase.com/why-couchbase-chose-rxjava-new-java-sdk 。
Couchbase Java 2.X Client使用Rx以便利的方式提供asynchronous调用。 由于它使用Rx,它有方法映射和FlatMap,他们的文档中的解释可能有助于理解一般的概念。
要处理错误,请覆盖您的订阅者的onError。
Subscriber<String> mySubscriber = new Subscriber<String>() { @Override public void onNext(String s) { System.out.println(s); } @Override public void onCompleted() { } @Override public void onError(Throwable e) { } };
看看这个文件可能有帮助: http : //blog.danlew.net/2014/09/15/grokking-rxjava-part-1/
有关如何pipe理RX错误的好消息,请参阅: https : //gist.github.com/daschl/db9fcc9d2b932115b679
在你的情况下,我认为你需要地图,因为只有1个input和1个输出。
地图提供的function只是接受一个项目,并返回一个项目,将进一步发射(只有一次)。
flatMap – 提供的函数接受一个项目,然后返回一个“Observable”,这意味着新的“Observable”的每个项目将被进一步分开发射。
可能代码会清除你的东西。
//START DIFFERENCE BETWEEN MAP AND FLATMAP Observable.just("item1") .map( str -> { System.out.println("inside the map " + str); return str; }) .subscribe(System.out::println); Observable.just("item2") .flatMap( str -> { System.out.println("inside the flatMap " + str); return Observable.just(str + "+", str + "++" , str + "+++"); }) .subscribe(System.out::println); //END DIFFERENCE BETWEEN MAP AND FLATMAP
输出:
inside the map item1 item1 inside the flatMap item2 item2+ item2++ item2+++
我想到的方式是当你想放入map()
的函数返回一个Observable
时,你使用flatMap
。 在这种情况下,你仍然可以尝试使用map()
但是这样做是不现实的。 让我试着解释为什么。
如果在这种情况下你决定坚持使用map
,你会得到一个Observable<Observable<Something>>
。 例如,在你的情况下,如果我们使用了一个虚构的RxGson库,从它的toJson()
方法(而不是简单地返回一个String
)返回一个Observable<String>
,它看起来像这样:
Observable.from(jsonFile).map(new Func1<File, Observable<String>>() { @Override public Observable<String>> call(File file) { return new RxGson().toJson(new FileReader(file), Object.class); } }); // you get Observable<Observable<String>> here
在这一点上, subscribe()
到这样的可观察对象是相当棘手的。 在它的内部,你会得到一个Observable<String>
,你将再次需要subscribe()
来获取值。 这是不实际或不好看的。
所以为了使它有用一个想法是“扁平化”这个可观察的observable(你可能会开始看到_flat_Map的名字来自哪里)。 RxJava提供了一些方法来平坦观察者,为了简单起见,我们假设合并是我们想要的。 合并基本上需要大量的可观察物质,并在其中任何一个物体发射时发射。 (很多人会认为交换机会是一个更好的默认设置,但是如果只发出一个值,那么无论如何也没关系。)
修改我们以前的代码片段,我们会得到:
Observable.from(jsonFile).map(new Func1<File, Observable<String>>() { @Override public Observable<String>> call(File file) { return new RxGson().toJson(new FileReader(file), Object.class); } }).merge(); // you get Observable<String> here
这是更有用的,因为订阅(或映射,或过滤,或…)你只是得到的String
值。 (另外,请注意,RxJava中并不存在merge()
这样的变体,但是如果你理解了合并的概念,那么我希望你也能理解这是如何工作的。)
所以基本上是因为这样的merge()
应该只在成功返回一个observable的map()
时才有用,所以你不需要一遍又一遍地input, flatMap()
被创build为一个简写。 它像普通的map()
一样应用映射函数,但是后来不是发射返回的值,而是“平滑”(或合并)它们。
这是一般用例。 在代码中使用Rx在代码中最有用,而且你有很多方法返回observable,你想用其他的方法返回observable。
在你的用例中它恰好也是有用的,因为map()
只能将onNext()
发射的一个值转换为onNext()
发射的另一个值。 但它不能把它变成多重价值,根本没有价值或错误。 正如akarnokd在他的回答中所写的(并且介意他比我更聪明,一般来说,但至less在RxJava方面),你不应该从你的map()
抛出exception。 所以相反,你可以使用flatMap()
和
return Observable.just(value);
一切顺利的时候,但是
return Observable.error(exception);
什么时候失败了
查看他的答案完整摘录: https : //stackoverflow.com/a/30330772/1402641
我只是想用flatMap
来添加,你并不需要在函数内部使用你自己定制的Observable,你可以依赖标准的工厂方法/操作符:
Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() { @Override public Observable<String> call(final File file) { try { String json = new Gson().toJson(new FileReader(file), Object.class); return Observable.just(json); } catch (FileNotFoundException ex) { return Observable.<String>error(ex); } } });
一般来说,尽可能在RxJava中放置尽可能多的安全措施,尽可能避免从onXXX方法和callback中抛出(Runtime)exception。
下面是我使用的一个简单的缩略图规则 ,帮助我决定何时在Rx的Observable
map()
上使用flatMap()
。
一旦你决定要使用map
转换,你会写你的转换代码来返回一些对象吗?
如果你正在返回的是你的转换的最终结果是:
-
一个不可观察的对象,那么你只需要使用
map()
。map()
把这个对象包装在一个Observable中并发射出去。 -
一个
Observable
对象,那么你可以使用flatMap()
。 而flatMap()
展开Observable,拾取返回的对象,用它自己的Observable包装它并发射它。
举例来说,我们有一个方法titleCase(String inputParam),它返回input参数的带标题的string对象。 此方法的返回types可以是String
或Observable<String>
。
-
如果
titleCase(..)
的返回types仅仅是String
,那么你可以使用map(s -> titleCase(s))
-
如果
titleCase(..)
的返回types是Observable<String>
,那么你可以使用flatMap(s -> titleCase(s))
希望澄清。
在这种情况下使用地图,你不需要一个新的Observable。
你应该使用Exceptions.propagate,这是一个包装,所以你可以发送这些检查的exception到rx机制
Observable<String> obs = Observable.from(jsonFile).map(new Func1<File, String>() { @Override public String call(File file) { try { return new Gson().toJson(new FileReader(file), Object.class); } catch (FileNotFoundException e) { throw Exceptions.propagate(t); /will propagate it as error } } });
然后你应该在用户中处理这个错误
obs.subscribe(new Subscriber<String>() { @Override public void onNext(String s) { //valid result } @Override public void onCompleted() { } @Override public void onError(Throwable e) { //e might be the FileNotFoundException you got } };);
有一个很好的职位: http : //blog.danlew.net/2015/12/08/error-handling-in-rxjava/
在某些情况下,你可能会得到一系列可观察的事物,其中你的可观察事物会返回另一个可观察事物。 'flatmap'类打开第一个被埋在第一个的第二个observable,让你直接访问数据第二个observable在订阅时吐出。