Upload
oracle-latinoamerica
View
136
Download
0
Embed Size (px)
Citation preview
RXJAVAPROGRAMAÇÃO REATIVA FUNCIONAL COM
UBIRATAN SOARESMARÇO / 2016
MOTIVAÇÃO
Java8 trouxe uma API de Streams, que permite operações comuns sobre coleções como Map, Filter, Reduce
Java9 trará uma API de ReactiveStreams, adotadando muitos conceitos presentes em RxJava diretamente no JDK
Funcional Programming ajuda a escrever código mais robusto e conciso de maneira geral
ERIK MEIJER
“Do ponto de vista de fluxo de dados, qual a diferença entre consultar o DB e processar as posições do ponteiro do mouse ???”
VELHOS PROBLEMAS SEMPRE NA MODA
Execução orquestrada de código de forma assíncrona
Execução e sincronização de processamento concorrente
Tratamento de erros
Escalabilidade
UMA NOVA FORMA DE PENSAR
E se ao invés de buscar dados de forma assíncrona, os dados chegassem até mim de forma assíncrona ?
Reativo : algo que reage a um estímulo !!!
Fundamento matemático : teoria da categorias !!!
POR QUÊ “REACTIVE PROGRAMMING”
As demandas atuais tipicamente pedem aplicações
Responsivas
Resilientes
Orientadas a eventos
Escaláveis
HANS DOCKTER
“Programar é arte de encontrar as abstrações corretas”
RELEITURA DE CONCEITOS
Pensamento em termos de fluxo de dados : eventos discretos e fluxo desses eventos
É possível reagir a eventos e combinar os mesmos
O estado do sistema deve mudar conforme a passagem de eventos no tempo
Eventos no fluxo são imutáveis
Sistema que idealmente nunca bloqueia (I/O, cálculos, etc)
DADOS E SINCRONICIDADE
Um valor Múltiplos valores
Síncrona T getData( ) Iterable<T> getData( )
Assíncrona Future<T> getData( ) Observable<T> getData( )
RELEITURA DE CONTRATOS
Iterable<T> Observer<T>
Obter o próximo T next( ) onNext( T )
Sinalizar erro throws Exception( ) onError( Thowable )
Saber se terminou hasNext( ) onComplete( )
PULLED WAY
PUSHED WAY
CONCEITOS BÁSICOS
OBSERVABLE
OBSERVER
OPERATOR
SCHEDULER
OBSERVABLE
Representa o fluxo de dados (ou eventos, ou itens emitidos)
Por padrão, executa de forma sequencial (não concorrente)
Repassa cada item emitido para um observador (callback)
Pode ser associado ao conceito de source da Streams API do java8, porém seguindo push model quanto aos dados
CRIANDO OBSERVABLES (I)
Observable<String> source = Observable.just("GOOGLE", "APPLE", "MICROSOFT");
source.subscribe(System.out::println);
GOOGLEAPPLEMICROSOFT
PROCESS FINISHED WITH EXIT CODE 0
CRIANDO OBSERVABLES (II)
Observable.fromCallable(() -> “RxJava is Awesome”)
.subscribe(System.out::println);
List<String> names = Arrays.asList("Banana", "Apple", "Orange");
Observable.from(names).subscribe(System.out::println);
CRIANDO OBSERVABLES (III)
Observable.interval(1, TimeUnit.SECONDS)
.map(time -> "AT SECOND " + time)
.subscribe(System.out::println);
AT SECOND 0AT SECOND 1AT SECOND 2AT SECOND 3…
WARNING : estamos roubando aqui, se você executar esse código, não verá esse log !!!!
OBSERVER
Consome o fluxo de dados
Respeita o contrato no estilo pushed data
É o ponto no qual os erros flutuam : um erro que aconteça durante uma operação interrompe a sequência de emissões
Callbacks sinalizam o término da sequência
Adições ao Observer Pattern do GOF
OBSERVER
public interface Observer<T> {
}
void onCompleted();
void onError(Throwable e);
void onNext(T data);
PIPELINE DE OPERAÇÕES
Observable.from(…)
.flatMap(…)
.filter(…)
.map(…)
.observeOn(…)
.subscribeOn(…)
.subscribe(…);
upstream sequence
downstream sequence
Observable.zip( restApi.getAvaliableItems(), restApi.getRecommendedItems(clientId),
new ItemsResultsZipper())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<Item>>() { @Override public void onCompleted() { … } @Override public void onError(Throwable e) { … } @Override public void onNext(List<Item> items) { … } });
podemos fazer melhor ?
OPERADORES
Funções que permitem manipular a sequência de eventos observáveis, sejam os itens emitidos, seja a própria sequência (ou múltiplas delas)
Reactive Extensions define um enorme catálogo de operadores quanto à semântica, em categorias bem definidas
CATEGORIA OPERADORES
CRIAÇÃO just( ), from( ), range( ), interval( ), defer( ) …
COMBINAÇÃO zip( ), merge( ), combineLatest( ), concat( ) …
TRANSFORMAÇÃO map( ), flatMap( ), concatMap( ) …
FILTRAGEM filter( ), take( ), skip( ), debounce( ) …
MUITO MAIS ! cache( ), replay( ), retry( ), retryWhen( ) …
MARBLE DIAGRAMS
A B C D E
onNext( ) é chamado cinco vezes
MARBLE DIAGRAMS
A B C
1 2 3 4 5
x
I
onError( )
onCompleted( )
MAP
1 2 3 4 5
A B C D E
MAP { INT X -> CHAR Y }
FILTER
1 2 3 4 5
2 4
FILTER { INT X -> INT Y}
MERGE
1 3 5
2 4
1 2 3 4 5
MERGE
I
I
I
CONCAT
1 3 5
2 4
1 3 5 2 4
CONCAT
I
I
I
AMB
1 3 5
2 4
1 3 5
AMB
I
I
I
INTERVAL
INTERVAL {🕒}
1 2 3 4 5
🕒 🕒 🕒🕒 🕒
rxmarbles.comDisponível como app Android
SCHEDULER
Escalonador de trabalho entre threads distintas
Abstração em volta de Executors ( java.util.concurrent )
Qualquer Observable pode ser produzido em um Scheduler e observado em outro
Mecanismo fundamental para aplicações móveis
Observable.range(1, 2) .map(i -> i * 2) .observeOn(Schedulers.io()) .doOnNext(i -> System.out.println( "Emitting " + i + " on thread " + threadName())) .observeOn(Schedulers.computation()) .map(i -> i * 10) .subscribe(i -> System.out.println( "Received " + i + " on thread " + threadName()));
Emitting 2 on thread RxCachedThreadScheduler-1Received 20 on thread RxComputationThreadPool-3Emitting 4 on thread RxCachedThreadScheduler-1Received 40 on thread RxComputationThreadPool-3
sleep(3000);
Observable.just("Google", "Apple", "Microsoft", "IBM") .subscribleOn(Schedulers.computation()) .subscribe(s -> System.out.println( "Received " + s + " on thread " + threadName()));
Received Google on thread RxComputationThreadPool-1Received Apple on thread RxComputationThreadPool-1Received Microsoft on thread RxComputationThreadPool-1Received IBM on thread RxComputationThreadPool-1
sleep(3000);
NUÂNCIAS SOBRE SCHEDULERS (I)
Schedulers.io( ) encapsula um thread pool de tamanho variável
Schedulers.computation( ) encapsula um thread pool de tamanho fixo
Operam sobre deamon threads
São extensíveis para encapsular threads importantes, como por exemplo a UI thread do Android ou do JavaFX
Alguns factory methods de Observable já associam um scheduler à sequência (como interval( ) )
NUÂNCIAS SOBRE SCHEDULERS (II)
subscribeOn( ) instrui o framework sobre em qual thread os itens serão emitidos para o consumidor
observeOn( ) instrui o framework sobre quais schedulers podem operar nas etapas intermediárias do pipeline de operações
subscribeOn( ) em geral é usado uma vez no pipeline
observeOn( ) é utilizado conforme a necesside de concorrência nas etapas do pipeline
TRATAMENTO DE ERROS
Evento terminal destrói a sequência. Period.
Recuperação controlada via operadores onErrorResumeNext( ), onErrorReturn( ) e outros
Sugestão : implemente um wrapper com o retorno de onError( throwable), isso ajudará no stacktrace em casos de erro
CONCEITOS AVANÇADOS
FLATMAP
"Transform the items emitted by an Observable into Observables, then flatten the emissions from those into a single Observable
The FlatMap operator transforms an Observable by applying a function that you specify to each item emitted by the source Observable, where that function returns an Observable that itself emits items. FlatMap then merges the emissions of these resulting Observables, emitting these merged results as its own sequence.
This method is useful, for example, when you have an Observable that emits a series of items that themselves have Observable members or are in other ways transformable into Observables, so that you can create a new Observable that emits the complete collection of items emitted by the sub-Observables of these items. "
FLATTENING
MAPPING
List<String> companies = Arrays.asList("Google", "Apple", "Microsoft");
List<String> leaders = Arrays.asList("Larry", "Steve", "Bill");
Observable<List<String>> obs = Observable.just(companies, leaders);
Observable<String> flat = obs.flatMap(strings -> Observable.from(strings));
flat.subscribe(System.out::println);
GoogleAppleMicrosoftLarrySteveBill
flatMap( ) desmontou 2 sequências de items e juntou individualmente os itens de cada sequência em uma única sequência final
mapeamento
MAIS SOBRE FLATMAP
flatmap( ) é o mecanismo padrão para encadeamento de operações assíncronas
flatmap( ) não garante a ordem dos itens emitidos na sequência gerada final; se a ordem dos itens for importante, usar concatMap( )
TRANSFORMERS
Aplicáveis através do operador compose( )
Manipulam a sequência como um todo, ideais para evitar repetições de código em etapas comuns do pipeline
Casos comuns : setup de schedulers, composição de filtros, etc
restApi.callMethod01() .subscribleOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(…);
restApi.callMethod02() .subscribleOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(…);
PODEMOS FAZER MELHOR !!!
public static <T> Observable.Transformer<T, T> applySchedulers() { return observable ->
observable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); }
restApi.callMethod01() .compose(Transformers.applySchedulers() ) .subscribe(…);
restApi.callMethod02() .compose(Transformers.applySchedulers() ) .subscribe(…);
HOT AND COLD OBSERVABLES
Hot Observables começam a emitir itens no momento em que são criados
Cold Observables começam a emitir itens no momento em que são observados
Hot Observables são passíveis de perda de emissões
Cold Observables podem produzir side-effects
Alguns operadores transformam a temperatura da sequência
BACKPRESSURE
Cenário no qual a sequência observável (produtor) emite mais itens do que o observador (consumidor) consegue processar
Menos provável em sequências de itens de natureza estritamente computacional
Mais provável em sequências sendo geradas por IO
LIDANDO COM BACKPRESSURE
Dois tipos de cenário, de acordo com a natureza dos itens emitidos
Perda de emissões implica em perda de informação
Perda de emissões não implica perda de informação
BACKPRESSURE COM PERDA DE DADOS
Estratégia : diminuir a quantidade de dados que chegam até o consumidor segundo alguma heurística de perda
Diversos operadores disponíveis : sample( ), debounce( ), throttleFirst( ), etc
BACKPRESSURE SEM PERDA DE DADOS
Duas estratégias imediatas
(i) Bufferizar dados para processamento posterior, com operadores como cache( ), buffer( ), window( ) etc
(ii) Informar a upstream sequence que é preciso diminuir o ritmo na emissão de itens, usando Producer API
RXJAVA VS JAVA8 STREAMS (I)
Streams são desenhadas para processamento computacional, in-memory e com fácil paralelização
Streams trazem Collectors API para melhor semântica de agrupamentos
Streams pode adaptar sources através de Spliterators API
Streams operam estritamente em pull mode, eg, não baseado em callbacks
Não há backpressure
RXJAVA VS JAVA8 STREAMS (II)
RxJava opera essencialmente em push mode, eg, baseado em callbacks
RxJava contém a noção de tempo na API, permitindo inclusive sincronização de eventos no tempo
RxJava oferece controle fino sobre concorrência via Schedulers
RxJava é desenhada tanto para uso de processamento computacional quanto para IO
RxJava tende a ser mais burocrática que Streams para processamento computacional tipo map/filter/collect/reduce
PROBLEMAS COM RXJAVA
Debugging pode ser difícil (stacktraces gigantescos)
Verbosidade (Java 6,7)
Curva de aprendizado : muitos conceitos, em geral complexos
OBRIGADOTHAT`S ALL FOLKS !!!
@ubiratanfsoares
speakerdeck.com/ubiratansoaresubiratansoares.github.io