Este é um repositório com exemplos úteis do mundo real de uso de RxJava com Android. Geralmente estará em um estado constante de "Trabalho em Andamento" (WIP).
Também tenho dado palestras sobre Learning Rx usando muitos dos exemplos listados neste repositório.
using
)Um requisito comum é descarregar operações longas e pesadas de E/S para um thread em segundo plano (thread não UI) e alimentar os resultados de volta para o thread UI/principal, após a conclusão. Esta é uma demonstração de como operações de longa duração podem ser transferidas para um thread em segundo plano. Após a operação ser concluída, voltamos ao thread principal. Tudo usando RxJava! Pense nisso como um substituto para AsyncTasks.
A operação longa é simulada por uma chamada Thread.sleep de bloqueio (como isso é feito em um thread em segundo plano, nossa UI nunca é interrompida).
Para realmente ver este exemplo brilhar. Aperte o botão várias vezes e veja como o clique do botão (que é uma operação da UI) nunca é bloqueado porque a operação longa só é executada em segundo plano.
Esta é uma demonstração de como os eventos podem ser acumulados usando a operação "buffer".
Um botão é fornecido e acumulamos o número de cliques nesse botão ao longo de um período de tempo e depois divulgamos os resultados finais.
Se você apertar o botão uma vez, receberá uma mensagem dizendo que o botão foi pressionado uma vez. Se você apertar 5 vezes continuamente em um intervalo de 2 segundos, você obterá um único registro, dizendo que você apertou o botão 5 vezes (vs 5 registros individuais dizendo "Botão pressionado uma vez").
Observação:
Se você está procurando uma solução mais infalível que acumule toques "contínuos" em vez de apenas o número de toques em um intervalo de tempo, consulte a demonstração do EventBus, onde uma combinação dos operadores publish
e buffer
é usada. Para uma explicação mais detalhada, você também pode dar uma olhada nesta postagem do blog.
Esta é uma demonstração de como os eventos podem ser engolidos de forma que apenas o último seja respeitado. Um exemplo típico disso são as caixas de resultados de pesquisa instantânea. Ao digitar a palavra "Bruce Lee", você não deseja executar pesquisas por B, Br, Bru, Bruce, Bruce, Bruce L ... etc. terminou de digitar a palavra inteira e, em seguida, disparou uma única chamada para "Bruce Lee".
Conforme você digita na caixa de entrada, ele não dispara mensagens de log a cada mudança de caractere de entrada, mas apenas escolhe o último evento emitido (ou seja, entrada) e registra-o.
Este é o método debounce/throttleWithTimeout em RxJava.
Retrofit from Square é uma biblioteca incrível que ajuda na facilidade de networking (mesmo que você ainda não tenha mudado para o RxJava, você realmente deveria dar uma olhada). Funciona ainda melhor com RxJava e esses são exemplos que chegam à API do GitHub, retirados diretamente da palestra do desenvolvedor semideus Android Jake Wharton na Netflix. Você pode assistir a palestra neste link. Aliás, minha motivação para usar o RxJava veio de assistir a esta palestra na Netflix.
(Observação: é mais provável que você atinja a cota da API do GitHub muito rapidamente, então envie um token OAuth como parâmetro se quiser continuar executando esses exemplos com frequência).
As visualizações com atualização automática são uma coisa muito legal. Se você já lidou com Angular JS antes, eles têm um conceito bastante bacana chamado "vinculação de dados bidirecional", portanto, quando um elemento HTML é vinculado a um objeto de modelo/entidade, ele constantemente "escuta" as alterações nessa entidade e atualiza automaticamente seu estado com base no modelo. Usando a técnica neste exemplo, você poderia usar um padrão como o padrão Presentation View Model com grande facilidade.
Embora o exemplo aqui seja bastante rudimentar, a técnica usada para conseguir a ligação dupla usando um Publish Subject
é muito mais interessante.
Este é um exemplo de pesquisa usando agendadores RxJava. Isso é útil nos casos em que você deseja consultar constantemente um servidor e possivelmente obter novos dados. A chamada de rede é "simulada", portanto força um atraso antes de retornar uma string resultante.
Existem duas variantes para isso:
O segundo exemplo é basicamente uma variante do Backoff Exponencial.
Em vez de usar RetryWithDelay, usamos RepeatWithDelay aqui. Para entender a diferença entre Retry(When) e Repeat(When), sugiro a fantástica postagem de Dan sobre o assunto.
Uma abordagem alternativa para sondagem atrasada sem o uso de repeatWhen
seria usar observáveis de atraso aninhados encadeados. Consulte startExecutingWithExponentialBackoffDelay no exemplo ExponentialBackOffFragment.
Backoff exponencial é uma estratégia onde, com base no feedback de uma determinada saída, alteramos a taxa de um processo (geralmente reduzindo o número de novas tentativas ou aumentando o tempo de espera antes de tentar novamente ou reexecutar um determinado processo).
O conceito faz mais sentido com exemplos. O RxJava torna (relativamente) simples a implementação de tal estratégia. Meus agradecimentos a Mike por sugerir a ideia.
Digamos que você tenha uma falha na rede. Uma estratégia sensata seria NÃO repetir a chamada de rede a cada 1 segundo. Em vez disso, seria inteligente (não... elegante!) tentar novamente com atrasos crescentes. Então você tenta no segundo 1 executar a chamada de rede, sem dados? tente depois de 10 segundos... negativo? tente depois de 20 segundos, sem cookie? tente depois de 1 minuto. Se isso ainda estiver falhando, você terá que desistir da rede!
Simulamos esse comportamento usando RxJava com o operador retryWhen
.
Cortesia do snippet de código RetryWithDelay
:
Veja também o exemplo de Polling, onde usamos um mecanismo de espera exponencial muito semelhante.
Outra variante da estratégia de backoff exponencial é executar uma operação por um determinado número de vezes, mas com intervalos atrasados. Então você executa uma determinada operação daqui a 1 segundo, depois a executa novamente daqui a 10 segundos e depois executa a operação daqui a 20 segundos. Depois de um total geral de 3 vezes, você para de executar.
Simular esse comportamento é, na verdade, muito mais simples do que o mecanismo de nova tentativa anterior. Você pode usar uma variante do operador delay
para conseguir isso.
.combineLatest
)Obrigado a Dan Lew por me dar essa ideia no podcast fragmentado - episódio 4 (por volta das 4h30).
.combineLatest
permite monitorar o estado de vários observáveis ao mesmo tempo, de forma compacta, em um único local. O exemplo demonstrado mostra como você pode usar .combineLatest
para validar um formulário básico. Existem 3 entradas principais para que este formulário seja considerado "válido" (um e-mail, uma senha e um número). O formulário se tornará válido (o texto abaixo fica azul :P) assim que todas as entradas forem válidas. Caso contrário, um erro será mostrado nas entradas inválidas.
Temos 3 observáveis independentes que rastreiam as alterações de texto/entrada para cada um dos campos do formulário ( WidgetObservable
do RxAndroid é útil para monitorar as alterações de texto). Depois que uma mudança de evento é percebida em todas as 3 entradas, o resultado é "combinado" e a validade do formulário é avaliada.
Observe que a função Func3
que verifica a validade entra em ação somente depois que TODAS as 3 entradas recebem um evento de alteração de texto.
O valor dessa técnica se torna mais aparente quando você tem mais campos de entrada em um formulário. Lidar com isso de outra forma com um monte de booleanos torna o código confuso e meio difícil de seguir. Mas usando .combineLatest
toda essa lógica está concentrada em um belo bloco compacto de código (eu ainda uso booleanos, mas isso foi para tornar o exemplo mais legível).
Temos dois Observáveis de origem: um cache de disco (rápido) e uma chamada de rede (fresca). Normalmente, o disco Observable é muito mais rápido que o Observable de rede. Mas, para demonstrar o funcionamento, também usamos um cache de disco falso "mais lento" apenas para ver como os operadores se comportam.
Isso é demonstrado usando 4 técnicas:
.concat
.concatEager
.merge
.publish
+ merge + takeUntilA 4ª técnica é provavelmente a que você deseja usar eventualmente, mas é interessante passar pela progressão das técnicas para entender o porquê.
concat
é ótimo. Ele recupera informações do primeiro Observable (cache de disco no nosso caso) e depois do Observable de rede subsequente. Como o cache do disco é presumivelmente mais rápido, tudo parece bem e o cache do disco é carregado rapidamente e, quando a chamada de rede termina, trocamos os resultados "frescos".
O problema com concat
é que o observável subsequente nem começa até que o primeiro Observável seja concluído. Isso pode ser um problema. Queremos que todos os observáveis comecem simultaneamente, mas produzam os resultados da maneira que esperamos. Felizmente, o RxJava introduziu concatEager
, que faz exatamente isso. Ele inicia ambos os observáveis, mas armazena o resultado do último até que o anterior Observável termine. Esta é uma opção completamente viável.
Às vezes, porém, você só deseja começar a mostrar os resultados imediatamente. Supondo que o primeiro observável (por alguma razão estranha) demore muito para percorrer todos os seus itens, mesmo que os primeiros itens do segundo observável tenham chegado ao fio, ele será forçosamente colocado na fila. Você não quer necessariamente "esperar" por qualquer Observável. Nessas situações, poderíamos usar o operador merge
. Ele intercala os itens à medida que são emitidos. Isso funciona muito bem e começa a exibir os resultados assim que são mostrados.
Semelhante ao operador concat
, se o seu primeiro Observable for sempre mais rápido que o segundo Observable, você não terá problemas. No entanto, o problema com merge
é: se por algum motivo estranho um item for emitido pelo cache ou observável mais lento após o observável mais recente/mais recente, ele substituirá o conteúdo mais recente. Clique no botão "MERGE (SLOWER DISK)" no exemplo para ver esse problema em ação. As contribuições de @JakeWharton e @swankjesse vão para 0! No mundo real, isso pode ser ruim, pois significaria que os dados novos seriam substituídos por dados obsoletos do disco.
Para resolver este problema você pode usar merge em combinação com o super bacana operador publish
que recebe um "seletor". Escrevi sobre esse uso em uma postagem do blog, mas devo agradecer ao Jedi JW por lembrar dessa técnica. publish
o observável da rede e fornecemos a ele um seletor que começa a emitir a partir do cache do disco, até o ponto em que o observável da rede começa a emitir. Assim que o observável da rede começa a emitir, ele ignora todos os resultados do observável do disco. Isso é perfeito e resolve qualquer problema que possamos ter.
Anteriormente, eu estava usando o operador merge
, mas superando o problema de substituição de resultados monitorando o "resultAge". Veja o antigo exemplo PseudoCacheMergeFragment
se estiver curioso para ver esta implementação antiga.
Este é um exemplo super simples e direto que mostra como usar os operadores timer
, interval
e delay
do RxJava para lidar com vários casos em que você deseja executar uma tarefa em intervalos específicos. Basicamente, diga NÃO aos Android TimerTask
s.
Casos demonstrados aqui:
Há postagens de blog que explicam muito melhor os detalhes desta demonstração:
Uma pergunta comum feita ao usar o RxJava no Android é: "como faço para retomar o trabalho de um observável se ocorrer uma alteração na configuração (rotação de atividade, alteração de localidade do idioma, etc.)?".
Este exemplo mostra uma estratégia, a saber. usando fragmentos retidos. Comecei a usar fragmentos retidos como "fragmentos de trabalho" depois de ler este post fantástico de Alex Lockwood há algum tempo.
Aperte o botão Iniciar e gire a tela conforme desejar; você verá o observável continuar de onde parou.
Existem certas peculiaridades sobre a “quente” da fonte observável usada neste exemplo. Confira minha postagem no blog onde explico os detalhes.
Desde então, reescrevi este exemplo usando uma abordagem alternativa. Embora a abordagem ConnectedObservable
tenha funcionado, ela entra nas terras do "multicasting", que pode ser complicado (segurança de thread, .refcount etc.). Os assuntos, por outro lado, são muito mais simples. Você pode vê-lo reescrito usando um Subject
aqui.
Escrevi outra postagem no blog sobre como pensar sobre assuntos, onde entro em alguns detalhes.
Volley é outra biblioteca de rede apresentada pelo Google no IO '13. Um gentil cidadão do github contribuiu com este exemplo para que saibamos como integrar o Volley com o RxJava.
Aproveito o uso simples de um Assunto aqui. Honestamente, se você ainda não tem seus itens baixados por meio de um Observable
(como por meio de Retrofit ou uma solicitação de rede), não há um bom motivo para usar Rx e complicar as coisas.
Este exemplo basicamente envia o número da página para um Assunto, e o assunto cuida da adição dos itens. Observe o uso de concatMap
e o retorno de um Observable<List>
de _itemsFromNetworkCall
.
Para diversão, também incluí um exemplo de PaginationAutoFragment
, que "pagina automaticamente" sem precisar apertar um botão. Deve ser simples de seguir se você entendeu como funciona o exemplo anterior.
Aqui estão algumas outras implementações sofisticadas (embora eu tenha gostado de lê-las, não comecei a usá-las em meu aplicativo do mundo real, porque pessoalmente não acho que seja necessário):
O diagrama ascii abaixo expressa a intenção do nosso próximo exemplo com elegância. f1,f2,f3,f4,f5 são essencialmente chamadas de rede que, quando feitas, retornam um resultado necessário para um cálculo futuro.
(flatmap)
f1 ___________________ f3 _______
(flatmap) | (zip)
f2 ___________________ f4 _______| ___________ final output
|
____________ f5 _______|
O código deste exemplo já foi escrito por um certo Sr.skehlet nas interwebs. Vá até a essência do código. Está escrito em Java puro (6), então é bastante compreensível se você entendeu os exemplos anteriores. Vou lançá-lo aqui novamente quando o tempo permitir ou quando outros exemplos convincentes acabarem.
Este é um exemplo simples que demonstra o uso do operador .timeout
. O Botão 1 concluirá a tarefa antes da restrição de tempo limite, enquanto o Botão 2 forçará um erro de tempo limite.
Observe como podemos fornecer um Observable personalizado que indica como reagir sob uma exceção de tempo limite.
using
) A operadora using
é relativamente menos conhecida e notoriamente difícil para o Google. É uma bela API que ajuda a configurar um recurso (caro), usá-lo e depois descartá-lo de maneira limpa.
O bom desse operador é que ele fornece um mecanismo para usar recursos potencialmente caros com um escopo restrito. usando -> configurar, usar e descartar. Pense em conexões de banco de dados (como instâncias de Realm), conexões de soquete, bloqueios de thread, etc.
Multicasting em Rx é como uma arte obscura. Poucas pessoas sabem como fazer isso sem preocupação. Este exemplo condiciona dois assinantes (na forma de botões) e permite adicionar/remover assinantes em diferentes momentos e ver como os diferentes operadores se comportam nessas circunstâncias.
A fonte observale é um temporizador ( interval
) observável e a razão pela qual ele foi escolhido foi escolher intencionalmente um observável sem terminação, para que você possa testar/confirmar se seu experimento multicast irá vazar.
Também dei uma palestra detalhada sobre Multicasting na 360|Andev. Se você tiver disposição e tempo, sugiro assistir primeiro a essa palestra (especificamente o segmento de permutação de operadores Multicast) e depois brincar com o exemplo aqui.
Todos os exemplos aqui foram migrados para usar o RxJava 2.X.
Usamos a biblioteca Interop de David Karnok em alguns casos, pois certas bibliotecas como RxBindings, RxRelays, RxJava-Math etc. ainda não foram portadas para 2.x.
Tento garantir que os exemplos não sejam excessivamente inventados, mas reflitam um caso de uso do mundo real. Se você tiver exemplos úteis semelhantes que demonstrem o uso de RxJava, sinta-se à vontade para enviar uma solicitação pull.
Estou pensando no RxJava também, então se você acha que há uma maneira melhor de fazer um dos exemplos mencionados acima, abra um problema explicando como. Melhor ainda, envie uma solicitação pull.
Threading Rx é um negócio complicado. Para ajudar, este projeto usa ferramentas YourKit para análise.
YourKit oferece suporte a projetos de código aberto com ferramentas inovadoras e inteligentes para monitorar e criar perfis de aplicativos Java. YourKit é o criador do YourKit Java Profiler.
Licenciado sob a Licença Apache, Versão 2.0 (a "Licença"). Você pode obter uma cópia da Licença em
http://www.apache.org/licenses/LICENSE-2.0
A menos que exigido pela lei aplicável ou acordado por escrito, o software distribuído sob a Licença é distribuído "COMO ESTÁ", SEM GARANTIAS OU CONDIÇÕES DE QUALQUER TIPO, expressas ou implícitas. Consulte a Licença para saber o idioma específico que rege as permissões e limitações da Licença.
Você concorda que todas as contribuições para este repositório, na forma de correções, solicitações pull, novos exemplos, etc., seguem a licença mencionada acima.