Autor: Moacir Ramos

Atualmente é cada vez mais comum encontrar projetos Android utilizando RxJava. Isso se reflete na quantidade de publicações abordando o assunto bem como nos anúncios de vagas para desenvolvedores.

Mas muitas vezes o que acontece é que os desenvolvedores começam a usar uma nova biblioteca apenas para seguir a tendência do mercado e não param para se perguntar se a adoção da tecnologia realmente vai trazer benefícios para o seu projeto. E adotar o RxJava é um caso ainda mais desafiador, pois comparado a outras bibliotecas, tem uma curva de aprendizado mais acentuada.

Diante desse cenário, muitos candidatos chegam às entrevistas para vagas Android sabendo como aplicar RxJava em seus projetos, mas poucos conseguem justificar porque adotaram a tecnologia. O objetivo desse artigo é relacionar a teoria do Manifesto Reativo com a implementação do RxJava para te ajudar a explorar os recursos disponíveis na biblioteca e obter sistemas que cumpram com os requisitos do Manifesto.

Esse artigo assume que você já conhece programação reativa e já teve contato com o RxJava. Caso ainda não conheça, inicie por essa introdução de 2 minutos, que apesar de curta, é muito didática a aborda aspectos fundamentais do paradigma.

O Manifesto Reativo

Alguns desenvolvedores vão argumentar que o manifesto é o tipo de coisa que você mostra para convencer o seu Gerente de Projetos ou a pessoa de negócios. Concordo em partes com isso e eu diria que conhecer o Manifesto também vai impressionar o seu entrevistador. Do ponto de vista técnico o Manifesto não ajuda muito um desenvolvedor que começou a aprender com Rx recentemente. Mas por outro lado, o Manifesto menciona recursos avançados da programação reativa que geralmente os desenvolvedores Android desconhecem. Além disso, o Manifesto vai ajudar você a validar se o seu sistema realmente está entregando o que é esperado para um sistema Reativo.

Diante da necessidade de construir sistemas cada vez mais robustos, capazes de processar quantidades cada vez maiores de dados e com exigência de tempo de resposta cada vez menores, as empresas de software passaram a buscar características arquiteturais comuns que atendessem essas mudanças de requisitos recentes. O Manifesto Reativo sintetiza as características desejáveis para esses sistemas chamados Reativos.

O Manifesto Reativo é baseado em quatro características principais:

Responsivo: um sistema responsivo atende as requisições consistentemente no menor tempo possível, contribuindo para a usabilidade, aumentando a confiança de seus clientes e estimulando futuras interações.

Resiliente: é um sistema que não fica indisponível em caso de falha. Isso vale para qualquer tipo de sistema e não apenas os de missão crítica. A resiliência pode ser obtida por meio da contenção, replicação, isolamento e delegação. O tratamento das falhas precisa ser feito dentro de cada componente isoladamente, de modo que se recupere sem afetar o sistema como um todo. E é desejável que os clientes não sejam sobrecarregados com o tratamento das falhas.

Elástico: o sistema é elástico se ele continua responsivo mesmo quando há alterações de demanda. Essa característica pode ser alcançada por meio da diminuição ou aumento dos recursos alocados. O sistema não deve ter pontos de contenção ou gargalos centrais e pode ter capacidade de dividir a demanda entre os componentes ou replicá-los conforme a necessidade.

Orientado a mensagens: os sistemas reativos usam comunicação entre componentes por meio de mensagens assíncronas. Essa característica garante baixo acoplamento, isolamento e transparência na localização da mensagem. Um sistema baseado em mensagens facilita o balanceamento de demanda entre os componentes permitindo que o sistema seja elástico responsivo. Além disso, facilita a implementação de filas de mensagens e utilização de recursos avançados relacionados como: controle de fluxo e contrapressão.

O Manifesto Reativo e o RxJava

É bom conhecer o Manifesto Reativo, mas ele especifica requisitos de muito alto nível e traz poucas dicas de como implementá-los em um sistema reativo real. Nessa seção pretendo relacionar as características desejadas dos sistemas reativos com a maneira como eles foram implementados no RxJava.

Orientado a mensagens:

Embora o RxJava não seja tão explicitamente orientado a mensagens como outras implementações de sistemas reativos, tais como os modelos de atores (por exemplo, o Akka), a plataforma RxJava oferece inúmeros recursos onde essa característica de orientação a mensagens fica mais evidente. O melhor exemplo disso são os Subjects do Rx. Em resumo, os Subjects implementam o padrão Publish/Subscribe. No RxJava, um Subject representa um Observer e um Observable ao mesmo tempo, permitindo o multicasting de eventos de uma única fonte para múltiplos Subscribers filhos. Como o componente responsável pela publicação desconhece quem serão os consumidores das mensagens, esse modelo garante baixo acoplamento entre os componentes da aplicação.

Em um de nossos projetos utilizamos os Subjects para publicar eventos relacionados ao download de conteúdo da aplicação. No nosso caso, precisamos consumir os eventos de download em vários pontos do sistema. Por exemplo, consumimos os eventos de progresso para exibí-los na notificação e também barras de progresso na tela onde o usuário solicitava o download. Como o produtor não precisa conhecer o consumidor, a implementação ficou muito simples.

Outro conceito interessante permitido pela orientação a mensagens é a contrapressão (back pressure). Até hoje a melhor explicação que encontrei sobre o tema vem do artigo: Papai, o que é uma Stream?. No artigo ele compara uma stream de dados a uma mangueira de jardim. Se você segurar a extremidade da mangueira enquanto a água flui, em algum momento a mangueira vai se encher de água e a pressão interna da mangueira vai aumentar o suficiente para que a torneira pare de enviar mais água. No caso de Streams de dados, através da contrapressão seria possível perceber quando um consumidor não está conseguindo consumir todos os dados gerados pelo produtor. Esse sinal permite que o desenvolvedor decida o que fazer diante desse cenário.

O RxJava apresenta inúmeros recursos para lidar com a contrapressão. Quando um Observable emite mais elementos do que o Observer consegue consumir, você pode usar um dos seguintes operadores na sua Stream de dados:

  • onBackpressureDrop: nesse caso, os eventos que não puderem ser consumidos serão descartados.
  • onBackpressureBuffer: este operador permite a configuração de um limite de tamanho para um buffer para armazenar todos os eventos emitidos mas não consumidos. Caso o buffer fique cheio, o Observable vai ser finalizado com um erro.
  • onBackpressureLatest: esse operador armazena os eventos mais recentemente publicados até que o consumidor seja capaz de consumí-los.

Voltando ao exemplo do download de conteúdo, nossa aplicação permitia que o usuário pudesse baixar simultaneamente inúmeras aulas de um mesmo curso. Para cada aula havia uma ProgressBar exibindo o progresso do download. Infelizmente, em aparelhos mais limitados, a Main thread não conseguia consumir todos os eventos de progresso e renderizar as barras de progresso com fluidez. Dada a limitação do consumidor da nossa Stream de dados, apelamos para o operador o onBackpressureDrop. No nosso caso, como tratava-se de eventos de progresso do download, não houve prejuízo para a experiência do usuário. Cabe ao desenvolvedor avaliar o melhor operador para o seu caso.

Responsivo

Quando se fala em aplicações reativas do lado do servidor, surgem várias possibilidades de usar inúmeros servidores em diferentes combinações, tais como clusters ou load balancers, para tornar a aplicação mais responsiva. No ambiente Mobile não existem tantas possibilidades, então o que resta é utilizar bem os recursos de hardware disponíveis. Para esse propósito o RxJava provê uma abstração chamada de Schedulers, que no universo Java/Kotlin são implementados baseados em Threads e Pools de Threads. Os principais tipos de Scheduler são:

  • Io: usado para tarefas de io, por exemplo, chamadas de rede e acesso a arquivos.
  • Computation: usado para computação pesada. Nesses schedulers o número de thread acompanha o número de núcleos do processador.
  • NewThread: cria uma nova thread para execução do trabalho. Deve ser usado com cautela, pois criar uma nova thread é custoso para o sistema operacional. Prefira outras alternativas que mantenham um pool de threads ativo para evitar esse custo.
  • From: permite especificar seu próprio pool de threads.

Falando-se em aplicações mobile responsivas, um item que precisa ser levado em consideração é o cuidado com a Main Thread. Executar tarefas pesadas e demoradas fora da Main Thread é uma tarefa comum de qualquer desenvolvedor Android e todos eles sabem a curva de aprendizado para se utilizar as AsyncTask ou Threads, e a quantidade enorme de boilerplate codenecessário para atingir seus objetivos. Então, o primeiro benefício do RxJava é trazer os Schedulers como uma abstração mais simples para execução de códigos fora da Main Thread.

Resiliente

O primeiro benefício do RxJava que garante a resiliência da aplicação é possuir abstrações prontas para tratamento de erro na API base. Isso significa um trabalho a menos para o desenvolvedor que não precisa criar sua própria abstração. Além disso, é uma excelente alternativa ao encadeamento de Exceptions do Java que geralmente deixam o código pouco legível e suscetível a bugs.

O uso das abstrações do Rx permite isolar as falhas do aplicativo por funcionalidade, garantindo um isolamento adequado entre os componentes da aplicação e permitindo que o desenvolvedor trate cada erro isoladamente. As exceções então fluem da camada de negócio para a UI de modo que o desenvolvedor tenha facilidade para informar seu usuário quando algo inesperado acontece.

A presença de uma abstração para tratamento de erro é muito clara no RxJava, pois a interface base do Observer já possui o método onErrorrecebendo uma Exception. Isso pode parecer muito comum para o desenvolvedor RxJava, mas outras bibliotecas que tentam seguir os princípios reativos, tais como o LiveData do Google, não possuem uma abstração pronta para tratamento de erros, o que pode dificultar a vida de um desenvolvedor menos experiente.

Elástico

Mais uma vez, as aplicações Mobile tem a elasticidade mais limitada se comparada a aplicações de servidor. Então, a elasticidade da aplicação depende mais de fazer um bom uso das threads do sistema e escolher o Scheduler adequado para o problema, como já foi citado.

Felizmente a demanda também é um pouco menor, o que torna esse fator mais preocupante apenas em dispositivos de baixo custo ou obsoletos.

Conclusões

Caso você tenha problemas em adotar tecnologias inovadoras como o RxJava no seu ambiente de trabalho, agora você tem um conjunto de argumentos para convencer seu time, o seu gerente ou o seu cliente. Caso já esteja utilizando, espero que este artigo te ajude a explorar cada vez mais os inúmeros recursos da plataforma RxJava para fazer aplicações responsivas, resilientes e elásticas, garantindo a satisfação de seus usuários.

Posted by:Matheus Fonseca

2 replies on “Por que estou usando RxJava na minha aplicação Android?

Deixe seu comentário