Modularização Android Parte 4.1 — Refatorando

MVVM, Koin, Rx, Room, Databinding e um pouco mais… *Coroutines

Iago Mendes Fucolo
Android Dev BR
5 min readApr 15, 2020

--

Um ano depois…

Muita coisa mudou nesse tempo e, por isso, resolvi escrever dar continuidade a esta série, mas com ênfase em refatorar o projeto para que ele esteja de acordo com conceitos mais atuais e também diferentes. Isso não significa que o que já foi feito não tem mais aplicação ou não deve mais ser usado. Pelo contrário: ainda são conceitos muito válidos. Porém, trago nestes novos capítulos abordagens diferentes, para que, no fim, você tome a decisão sobre o que usar e como usar.

P.S: A arquitetura continuará sendo a mesma.

Melhorando nossos LiveDatas:

A primeira coisa que vamos adicionar é uma classe chamada Event, dentro do package utils. A razão de utilizar a classe Event é porque quando entramos e saímos em um fragment/activity por meio de um evento usando LiveDatas ocorre o seguinte cenário: quando voltamos para a tela anterior o evento é executado novamente e acabamos voltando para onde estávamos, entrando num ciclo infinito de ir e vir entre duas telas, e isso pode acontecer em outros casos também. Depois de quebrar a cabeça com isso, encontrei um artigo que sugere a criação da classe Event, que evita que o evento execute toda vez que a activty/fragment seja recriada, pois o evento só vai ser executado se ele realmente existir. Vamos combinar o uso de Event com os LiveDatas, como veremos mais adiante.

Para mais detalhes sobre a classe Event, recomenda-se a leitura do artigo abaixo

Refatorando a feature main:

Essa feature não possui nada de especial, mas não é por isso que não merece nossa atenção. No primeiro momento, podemos observar que na MainViewModel utilizamos booleans como tipos de nossos LiveDatas. Para esse caso simples não foi difícil lembrar o que o código faz, mas imaginemos uma situação onde temos que controlar inúmeras ações somente com a combinação de booleans: com certeza daria muita dor de cabeça para decifrar o que estaria acontecendo.

Mas, para evitar isso, vamos utilizar algumas táticas, sendo a primeira delas criar uma classe do tipo Sealed para controlar os estados que queremos para cada ação:

A classe MainAction tem um conjunto de tipos de Actions e, com isso, sabemos exatamente as ações que queremos executar, pois está bem claro em cada object dentro da classe. O propósito de usar Sealed class para controlar os tipos de ações é justamente evitar controles dos quais não temos conhecimento e que não têm seus tipos explícitos. Mas você pode estar pensando: poderíamos usar um enum para isso, certo? Certo, mas, ao contrário do enum, que tem sua tipagem restrita, na sealed class podemos ter varias instâncias diferentes de um mesmo estado.

artigos relacionados:

Em seguida, vamos combinar a nossa MainAction com a classe Event com o tipo de dado que o LiveData vai operar no ViewModel:

Aqui, temos dois elementos: um Mutable LiveData, chamado _mainActionLiveData, que será utilizado para setar nossas ações e só deve ser acessado e alterado por MainViewModel; e para que possamos observar os valores que forem setados nele, há um LiveData, que chamamos de mainActionLiveData, que recebe _mainActionLiveData como valor, e com isso podemos observar qualquer mudança que ocorrer em _mainActionLiveData somente fazendo o subscribe de mainActionLiveData na nossa view.

Então, teremos o seguinte resultado final de MainViewModel:

PS: A classe MainAction está no mesmo arquivo que MainViewModel, porém você pode livremente retirá-la daqui e colocá-la em outro package.

Como podemos observar, na classe MainViewModel modificamos os métodos onShowAndroidRequire e onOutAppLiveData para setar em _mainActionLiveData a MainAction correspondente à ação que queremos. Assim, como já mencionamos, a view que estiver observando mudanças em mainActionLiveData será acionada.

Conseguem ver a diferença de usar tipos com sealed class ao invés de booleans? Fica muito mais claro o que estamos querendo fazer.

MainActivity:

E, para finalizar, vamos alterar a MainAcitivity, para que funcione com as novas mudanças da MainViewModel, já que a activity depende do ViewModel para executar ações.

A única coisa que modificamos aqui foi o método setupViewModel:

A primeira coisa que verificamos dentro do Observer é se nosso evento existe de fato, e, para isso, ele não pode ser nulo. Então, por meio de getContentIfNotHandled de Event (event é do tipo Event<MainAction>), nós teremos certeza de que o evento não será null. Assim, somente será executado o que está dentro de let quando um evento não nulo for observado no LiveDatas. E também getContentIfNotHandled nos retorna o valor com o tipo que estamos observando dentro de Event, nesse caso MainAction. Isso evita que o evento seja executado novamente quando a activity/fragment for recriada ou quando, por alguma razão, tivermos um evento nulo.

Obs: isso acontece mais em fragments, mas é sempre bom evitar possíveis problemas.

Uma coisa interessante de se observar aqui é a utilização de Sealead class para a MainAction, pois deixamos o código mais legível, fazendo, assim, com que cada branch do when de mainAction execute uma ação especifica.

Para fechar esse método, utilizamos uma extension chamada exhaustive, que você vai encontrar dentro de util. Com a utilização dela, você consegue fazer o seu when não compilar caso se esqueça de implementar algum estado da sealed class. Teste você mesmo removendo MainAction.LEAVE_APP do when e verá que seu código não irá compilar.

Obs: se o seu when retorna algum valor para uma var/val você não precisa dessa extension.

P.S: exhaustive foi retirado do primeiro artigo de sealed class que foi citado cima.

Com isso, encerramos a primeira parte do nosso refactor. No próximo artigo vamos aprofundar o tema, pois na feature list jobs terá muita coisa nova.

commit com as modificações desse artigo: commit

Branch da refatoração: coroutine

Ficou com alguma dúvida?

Manda um e-mail que terei o prazer de responder.

iagofucolo@gmail.com

follow me twitter, linkedin.

--

--

Iago Mendes Fucolo
Android Dev BR

Android Engineer @NewMotion, Writer, Ex almost footballer, and Brazilian.