RxJava and Kotlin #3
Separando o joio do trigo com os filtering operators do RxJava.
No capítulo anterior subimos mais um degrau rumo à consolidação do nosso conhecimento acerca do RxJava. Agora você já sabe como criar uma chain de observables, como interagir com o mundo não reativo através dos subjects e, por fim, como encerrar a stream utilizando os métodos dispose ou clear
, evitando possiveis memory leaks.
Ao final desse capítulo você se sentirá bem confortável em aplicar algumas condicionantes de forma que quem subscrever à stream só receba aquilo que realmente interessa. Para isso veremos os seguintes operadores:
- Skipping Operators: skip, skipWhile, skipUntil
- Taking Operators: take
- Ignoring Operators: elementAt, filter
- Distinct Operators: distinctUntilChanged
Skip
Esse operador é utilizado quando você quiser pular um número X de emissões. Assim, as emissões a partir da primeira até o número passado no parâmetro da função serão ignorados.
Isso é o que está demonstrado no marble graph acima: as duas primeiras emissões não foram enviadas pela stream até o subscritor.
Aqui está um exemplo:
Com o código acima você cria um observable do tipo Int - linhas 3 e 4, utiliza o operador skip para ignorar as 3 primeiras emissões e printa o resultado após a subscrição.
SkipWhile
Esse operador também é utilizado para ignorar as emissões, mas difere-se do anterior pelo fato de possibilitar-nos incluir um predicate para determinar o que deve ser ignorado, e não somente o número de emissões.
Perceba olhando o gráfico que, enquanto a emissão não for do tipo esfera verde, ou seja, a condição nos retorna como true, os elementos são ignorados, mas, a partir do momento que a condição retorna false, o skipWhile não ignorará mais.
Como no exemplo anterior, aqui você também está criando uma stream de integers e utilizando o operador skipWhile para ignorar os elementos que são ímpares, mas, assim que um número par for emitido, todos os elementos seguintes chegarão até o subscritor.
SkipUntil
O princípio para esse operador é o mesmo dos outros skips, mas esta não depende de uma condição estática para para de ignorar es emissões mas sim de uma condição dinâmica.
Olhando a documentação podemos entender que o que dá o trigger nesse operador é a emissão de um segundo observable, ou seja, o primeiro observable irá ignorar os elementos até que um segundo observable emita um onNext.
Olhando o marble graph concluímos que os eventos de 1 a 4 foram ignorados até o momento em que outro observable emitiu 0, daí em diante todos os elementos seguintes chegaram até o subscritor.
Vamos lá.
Nas linhas 2 e 3 criamos nossos subjects, onde tentei simular um subject que representaria um estado de “não perturbe” e outro representando quando o usuário ficou onLine. Na linha 6 condicionamos as emissões só serem entregues quando o onLineSubject emitir um onNext. Nas linhas 10 e11 verificamos as emissões do silentModeSubject simulando um ringtone, na linha 12 recebemos um trigger informando que o usuário está online e na linha 13 novamente uma emissão do silentModeSubject.
Analisando o que foi printado, podemos ver que o usuário não foi incomodado pelo ringtone enquanto estava no silent mode, mas que, a partir do momento que o onLineSubject emitiu um onNext o usuário passou a receber e ouvir o ringtone.
Take
Os operadores take agem de forma oposta aos operadores skipping, pois, eles não ignoram n elementos mas sim pegam n elementos.
No gráfico acima verificamos que houve 4 emissões ao todo, mas somente as duas primeiras foram separadas para serem entregues ao subscritor.
Vimos acima como utilizar o operador take. Embora houve 6 emissões, somente as 3 primeiras passaram.
Vale mencionar que há também os seguintes operadores: takeWhile e takeUntil. Para não ficar tão repetitivo não vou abordá-los aqui, mas saiba que eles agem de forma similar aos operadores skipWhile e skipUntil, e ao invés de ignorar as n emissões, eles as emitem.
ElementAt
Pode ser que em alguma situação você queira emitir um elemento que está localizado em uma determinada posição na sequência de items emitidos, e não apenas os n primeiros ou últimos elementos. Para isso você pode fazer uso desse operador que irá puxar um item localizado em um índice especificado na seqüência de itens emitidos e emiti-lo como se fosse sua própria emissão.
No exemplo acima temos uma chain de observables que emite números do tipo Int, e que só me interessa a emissão no índice 3, lembrando que o índice começa no 0. Desta forma somente o número 4, que está no índice 3, é printado.
Filter
Aprenderemos agora sobre o filter. Assim como o skipWhile o operador filter também nos permite passar uma condição booleana dentro do lambda para determinar o que passa e o que não passa. A diferente entre filter e skipWhile é que o filter elimina completamente e emissão do elementos quando a condição booleana é false, já o skipWhile, como vimos, não irá ignorar nenhum elemento mais, se a condição booleana for false.
Como vamos no marble graph, somente os números maiores que dez são emitidos até o final da stream, sendo os menores que dez filtrados.
No exemplo criamos um observable integers, criado a partir de uma lista de integers, e filtramos os números ímpares, só emitindo os pares até o final da stream. Vocé pode usar o filter para qualquer condição booleana que você precisar, como por exemplo, checar se uma lista está vazia e só emitir um evento se ela não estiver; filtrar algum estado que você não queira emitir até o final da stream e etc.
DistinctUntilChanged
Como o próprio nome nos dá uma pista, esta é um tipo de operador dedicado a filtrar emissões duplicadas. Interessante também notar, conforme exposto no gráfico acima, que esse operador somente irá previnir um item duplicado de ser emitido se este estiver na sequência do primeiro item. Por exemplo, no gráfico acima a segunda emissão do 2 foi bloqueada pelo operador, pois houve a tentativa de emitir duas vezes o número 2 sem ter havido uma mudança entre as emissões, por essa razão que o número 1 foi emitido novamente (1, 2, 1..), entre as duas emissões do número 1 houve também a emissão do número 2.
Podemos ver que o operador impediu a string “Click!” de ser emitida vezes de forma consecutiva.
Cabe destacar que o comportamento padrão do operador usa o método equals para determinar se os items são iguais, caso não seja bem isso que você precise, você pode utilizar uma variante do distinctUntilChanged que aceita um predicate para comparação de dois items.
TL;DR
Uauuu, quanta coisa. Mas você está se saindo muito bem.
Aqui vimos muitos operadoeres destinados a filtrar as emissões que são consumidas pelo subscritor, cada um adequado para certa operação, dependendo do seu use-case. Aprendemos quais são esses operadores, as diferenças entre eles e como aplicá-los no código.
No próximo post irei abordar os variados operadores destinados a transformar os items emitidos - transforming operators.