Quando você usa map
vs flatMap
no RxJava ?
Digamos, por exemplo, que queremos mapear arquivos que contêm JSON em Strings que contêm o JSON--
Usando map
, temos que lidar com isso de Exception
alguma forma. Mas como?:
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 :(
}
});
Usando flatMap
, é muito mais detalhado, mas podemos encaminhar o problema ao longo da cadeia Observables
e lidar com o erro se escolhermos outro lugar e até tentar novamente:
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);
}
}
});
}
});
Eu gosto da simplicidade do map
, mas do tratamento de erros flatmap
(não da verbosidade). Não vi nenhuma prática recomendada por aí e estou curioso para saber como isso está sendo usado na prática.
subscriber.onError()
etc. Todos os exemplos que eu vi rotearam erros dessa maneira. Isso não importa?OnErrorThrowable
sãoprivate
e você precisa usarOnErrorThrowable.from(e)
.O FlatMap se comporta de maneira muito semelhante ao mapa, a diferença é que a função que ele aplica retorna um observável, portanto é perfeitamente adequado para mapear operações assíncronas.
No sentido prático, a função Map aplica apenas faz uma transformação sobre a resposta encadeada (não retornando um Observável); enquanto a função que o FlatMap aplica retorna
Observable<T>
, é por isso que o FlatMap é recomendado se você planeja fazer uma chamada assíncrona dentro do método.Resumo:
Um exemplo claro pode ser visto aqui: http://blog.couchbase.com/why-couchbase-chose-rxjava-new-java-sdk .
O Couchbase Java 2.X Client usa o Rx para fornecer chamadas assíncronas de maneira conveniente. Como ele usa o Rx, ele possui o mapa de métodos e o FlatMap, a explicação na documentação pode ser útil para entender o conceito geral.
Para lidar com erros, substitua onError no seu assinante.
Pode ajudar a olhar para este documento: http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/
Uma boa fonte sobre como gerenciar erros com o RX pode ser encontrada em: https://gist.github.com/daschl/db9fcc9d2b932115b679
fonte
No seu caso, você precisa de um mapa, pois há apenas 1 entrada e 1 saída.
A função fornecida pelo mapa simplesmente aceita um item e retorna um item que será emitido mais (apenas uma vez) para baixo.
A função flatMap - fornecida aceita um item e retorna um "Observável", o que significa que cada item do novo "Observável" será emitido separadamente mais adiante.
Pode ser que o código esclareça as coisas para você:
Resultado:
fonte
A maneira como penso sobre isso é que você usa
flatMap
quando a função que você deseja colocar dentro demap()
retorna umObservable
. Nesse caso, você ainda pode tentar usar,map()
mas seria impraticável. Deixe-me tentar explicar o porquê.Se, nesse caso, você decidisse
map
continuar, obteria umObservable<Observable<Something>>
. Por exemplo, no seu caso, se usássemos uma biblioteca RxGson imaginária, que retornasse um métodoObservable<String>
fromtoJson()
(do que simplesmente retorne aString
), ficaria assim:Neste ponto, seria bastante complicado para
subscribe()
um observável. Dentro dele, você obteria um valorObservable<String>
ao qual seria necessáriosubscribe()
obter novamente o valor. O que não é prático ou agradável de se olhar.Portanto, para tornar útil uma idéia, é "achatar" esse observável de observáveis (você pode começar a ver de onde vem o nome _flat_Map). O RxJava fornece algumas maneiras de nivelar os observáveis e, por uma questão de simplicidade, vamos assumir que a mesclagem é o que queremos. A mesclagem basicamente pega um monte de observáveis e emite sempre que algum deles é emitido. (Muitas pessoas argumentam que a troca seria um padrão melhor. Mas se você estiver emitindo apenas um valor, isso não importa).
Assim, alterando nosso snippet anterior, obteríamos:
Isso é muito mais útil, porque, ao se inscrever nisso (ou mapear, filtrar ou ...), você apenas obtém o
String
valor. (Além disso, lembre-se, essa variante demerge()
não existe no RxJava, mas se você entender a idéia de mesclagem, espero que você também entenda como isso funcionaria.)Então, basicamente porque isso
merge()
provavelmente só deve ser útil quando for possívelmap()
retornar um observável e, assim, você não precisará digitar isso repetidamente,flatMap()
foi criado como uma abreviação. Ele aplica a função de mapeamento da mesma maneira quemap()
faria normalmente , mas mais tarde, em vez de emitir os valores retornados, também os "achata" (ou mescla).Esse é o caso de uso geral. É mais útil em uma base de código que usa Rx em todo o lugar e você tem muitos métodos retornando observáveis, que você deseja encadear com outros métodos retornando observáveis.
Em seu caso de uso ele passa a ser útil também, porque
map()
só pode transformar um valor emitido emonNext()
em outro valor emitido emonNext()
. Mas não pode transformá-lo em vários valores, nenhum valor ou um erro. E como o akarnokd escreveu em sua resposta (e lembre-se de que ele é muito mais esperto que eu, provavelmente em geral, mas pelo menos no que diz respeito ao RxJava), você não deve lançar exceções no seumap()
. Então, em vez disso, você pode usarflatMap()
equando tudo vai bem, mas
quando algo falha.
Veja sua resposta para obter um trecho completo: https://stackoverflow.com/a/30330772/1402641
fonte
A questão é: quando você usa map vs flatMap no RxJava? . E acho que uma demonstração simples é mais específica.
Quando você deseja converter o item emitido para outro tipo, no seu caso, converter o arquivo em String, map e flatMap podem funcionar. Mas eu prefiro o operador de mapa porque é mais claro.
No entanto, em algum lugar,
flatMap
pode fazer um trabalho mágico, masmap
não pode. Por exemplo, quero obter as informações de um usuário, mas primeiro preciso obter o ID dele quando o usuário efetuar login. Obviamente, preciso de duas solicitações e elas estão em ordem.Vamos começar.
Aqui estão dois métodos, um para login retornado
Response
e outro para buscar informações do usuário.Como você vê, na função flatMap se aplica, primeiro eu obtenho o ID do usuário e
Response
depois busco informações do usuário. Quando duas solicitações são concluídas, podemos executar nosso trabalho, como atualizar a interface do usuário ou salvar dados no banco de dados.No entanto, se você usar,
map
não poderá escrever um código tão bom. Em uma palavra,flatMap
pode nos ajudar a serializar solicitações.fonte
Aqui é um simples polegar regra que eu uso me ajudar a decidir como quando usar
flatMap()
maismap()
no Rx deObservable
.Quando você decide que vai empregar uma
map
transformação, escreveria seu código de transformação para retornar algum objeto, certo?Se o que você está retornando como resultado final de sua transformação é:
um objeto não observável, você usaria apenas
map()
. Emap()
envolve esse objeto em um Observável e o emite.um
Observable
objeto, então você usariaflatMap()
. EflatMap()
desembrulha o Observable, pega o objeto retornado, envolve-o com seu próprio Observable e emite-o.Digamos, por exemplo, que temos um método titleCase (String inputParam) que retorna o objeto Titled Cased String do parâmetro de entrada. O tipo de retorno desse método pode ser
String
ouObservable<String>
.Se o tipo de retorno
titleCase(..)
fosse meroString
, você usariamap(s -> titleCase(s))
Se o tipo de retorno
titleCase(..)
fosseObservable<String>
, você usariaflatMap(s -> titleCase(s))
Espero que isso esclareça.
fonte
Eu só queria acrescentar que
flatMap
, você realmente não precisa usar seu próprio Observable personalizado dentro da função e pode confiar em métodos / operadores de fábrica padrão:Geralmente, você deve evitar lançar exceções (tempo de execução) dos métodos onXXX e retornos de chamada, se possível, mesmo que tenhamos colocado tantas salvaguardas quanto pudemos no RxJava.
fonte
Nesse cenário, use map, você não precisa de um novo Observable para ele.
você deve usar Exceptions.propagate, que é um invólucro para poder enviar essas exceções verificadas ao mecanismo rx
Você deve então lidar com esse erro no assinante
Existe uma excelente publicação para isso: http://blog.danlew.net/2015/12/08/error-handling-in-rxjava/
fonte
Em alguns casos, você pode acabar tendo uma cadeia de observáveis, em que seu observável retornaria outro observável. 'flatmap' meio que desembrulha o segundo observável que está oculto no primeiro e permite acessar diretamente os dados que o segundo observável está cuspindo durante a assinatura.
fonte
O Flatmap mapeia observáveis para observáveis. Mapeie itens para itens.
O Flatmap é mais flexível, mas o Mapa é mais leve e direto, portanto depende do seu caso de uso.
Se você estiver fazendo QUALQUER COISA assíncrona (incluindo a troca de threads), deverá usar o Flatmap, pois o Map não verificará se o consumidor está descartado (parte do peso leve)
fonte