Keddit – Parte 5: Kotlin, RxJava e RxAndroid

Tempo de leitura: 6 minutes

Parte 5: Kotlin, RxJava e RxAndroid

Finalmente, estamos aqui para falar sobre RxJava em Kotlin. Esta é uma combinação incrível e vamos aproveitar as vantagens de fornecer uma nova camada para nosso aplicativo e desacoplar nosso thread de UI principal com nossas tarefas em segundo plano, neste caso para solicitar notícias do Reddit do servidor. Vamos deixar tudo preparado para conectar com um servidor real (próxima parte), mas por enquanto os dados simulados estarão OK para emular uma solicitação do servidor.

Se esta é a primeira vez que você ouve sobre RxJava, encorajo você a ler sobre esta incrível paz de software que o deixará boquiaberto.

RxJava é a implementação de Reactive Extensions, uma biblioteca para compor programas assíncronos e baseados em eventos usando sequências observáveis.

E vamos usar a classe Observable para evitar fazer solicitações de servidor (ou qualquer execução de processo longo) de nosso famoso Main UI Thread.

 

Gerente de notícias

A classe NewsManager será responsável por fornecer notícias da API do Reddit. Ele será responsável por realizar a solicitação do servidor e nos fornecer uma lista de notícias com nosso News UI Model já criado.

A ideia principal por trás disso é fazer com que nossa chamada de API seja executada fora de nosso Thread de IU principal, em outro thread. Uma grande imagem seria algo assim:

Observable

A classe NewsManager fornecerá um método que retornará um objeto Observable que permitirá a você executar um trecho de código (como uma chamada de API) em outro contexto (neste caso, em um novo thread).

fun getNews(): Observable<List<RedditNewsItem>> {
    ...
}

Este objeto Observable será criado por nós a partir do NewsManager. Forneceremos a implementação deste objeto:

fun getNews(): Observable<List<RedditNewsItem>> {
    return Observable.create {
        subscriber ->

        val news = mutableListOf<RedditNewsItem>()
        // api call...
        subscriber.onNext(news)
        subscriber.onCompleted()
    }
}

Graças ao Kotlin, podemos usar expressões lambda para fornecer a função exigida pelo método Observable.create (…), neste caso estamos fornecendo a implementação do código que será executado no contexto que fornecemos ao Observable (nós verá mais sobre este contexto mais tarde neste post, mas em resumo, isso significa em qual thread será executado).

O método create() requer que você forneça uma função que recebe um objeto assinante, este objeto assinante permite que você envie eventos para os assinantes executando 3 métodos possíveis:

  • onNext(item: T): Vamos chamar este método para fornecer aos assinantes as notícias recebidas da chamada API. Nesse caso, T será nossa “List<RedditNewsItem>” que já foi inferida a partir do tipo de retorno na assinatura getNews().
  • onCompleted(): Este método é invocado quando terminamos de fornecer todos os dados deste Observable, neste caso, o chamaremos após enviarmos as notícias do método onNext() e estivermos prontos para finalizar este processo.
  • onError(e: Throwable): Se enfrentarmos algum problema com a chamada da API, chamaremos esse método para informar aos assinantes que houve um erro. Vamos usá-lo mais tarde para informar ao usuário que houve um problema com a solicitação, mostrando uma mensagem SnackBar.

Portanto, estamos emulando que estamos solicitando notícias fornecendo notícias simuladas e chamando onNext() e onCompleted() para informar todos os assinantes sobre esta informação.

Lazy NewsManager

Também adicionamos nosso NewsManager com nossa propriedade lazy que revisamos na 3ª parte deste tutorial:

private val newsManager by lazy { NewsManager() }

Este newsManager será inicializado com NewsManager() apenas na primeira vez que usarmos o campo newsManagers.

 

Solicitar notícias do NewsFragment

Agora é hora de usar nosso NewsManager e solicitar algumas novidades para mostrar em nosso RecyclerView.

Subscribe > Subscription > Observer

Para solicitar as notícias simuladas do Reddit do NewsManager, transformaremos nosso NewsFragment em um Observer do Observable que recebemos do NewsManager.getNews(). Faremos isso chamando o método getNews() e invocando o método “subscribe(…)do Observable recebido:

val subscription = newsManager.getNews().subscribe (
        { retrievedNews ->
            ...
        },
        { e ->
            ...
        }

O método subscribe tem várias sobrecargas e vamos usar esta:

public final Subscription subscribe(
       final Action1<? super T> onNext, 
       final Action1<Throwable> onError) {
       ...
}

Ele recebe duas funções:

  • onNext: Uma função a ser invocada quando o Observable chama o método onNext() que vimos anteriormente. Vamos usá-lo para definir o NewsAdapter com as notícias recebidas.
  • onError: Uma função a ser chamada quando o método onError() do Observable é chamado. Vamos usá-lo para mostrar um SnackBar com uma mensagem de erro.

E retorna um objeto Subscription. Este objeto nos permitirá gerenciar a assinatura, como verificar se ela ainda está assinada ou cancelar a assinatura (veremos mais sobre isso no final deste tutorial).

Graças ao Kotlin novamente, podemos fornecer essas duas funções usando expressões lambda. Por exemplo, na primeira função, a variável retrievedNews é o nome que dei às notícias recebidas do método onNext():

{ 
    retrievedNews ->
     (news_list.adapter as NewsAdapter).addNews(retrievedNews)
}

Lembre-se de que o tipo de retorno onNext é uma “List<RedditNewsItem>”, então o que fiz foi passar essas notícias diretamente para o nosso NewsAdapter.

E para a função onError, estou apenas dizendo ao usuário que houve um erro com um SnackBar. Posso usar “e”, que é um tipo Throwable, para obter mais detalhes sobre a exceção recebida:

{ e ->
    Snackbar.make(...).show()
}

Ainda estamos no Tópico Principal

Se executarmos o aplicativo, ele simplesmente funcionará porque estamos simulando os dados fornecidos neste observável. Mas se você realmente fizer uma operação de longa duração em vez dos dados simulados que geramos, o aplicativo irá parar de funcionar, pois ainda estamos no thread de interface do usuário principal.

Como não fornecemos nenhum detalhe específico ao nosso Observable, ele será executado com a configuração padrão que deve ser executada no mesmo thread em que foi invocado. Portanto, vamos configurar nosso Observable para ser executado em outro thread, mas ainda assim notificar qualquer evento em nosso Thread de IU principal atual.

SubscribeOn

val subscription = newsManager.getNews()
        .subscribeOn(Schedulers.io())
        .subscribe (...)

O método subscribeOn(…) permite que você mova a execução do código Observable para outra thread e com um comportamento específico. Para fazer isso, RxJava usa Schedulers que fornecem o comportamento que você precisa para seu caso de uso. RxJava também fornece uma lista de agendadores para cenários comuns:

  • io: destinado a trabalho vinculado a E/S.
  • computation: destinado ao trabalho computacional.
  • newThread: cria um novo Thread para cada unidade de trabalho.
  • test: útil para depuração.

Em nosso caso, usaremos Schedulers.io(), pois estaremos executando uma solicitação de API.

ObserveOn

ObserveOn é outro método da classe Observable e permite definir onde deseja executar as funções fornecidas no método “subscribe(…)”. Em outras palavras, em qual thread serão executadas as funções que fornecemos no método subscribe com o código onNext e onError. Como precisamos executar este código no Thread de IU principal, pode ser uma boa ideia tornar nosso Observable para observar no Thread de IU principal.

Aqui é onde precisamos adicionar a dependência RxAndroid para ter um novo Scheduler chamado:

AndroidSchedulers.mainThread()

E precisamos atualizar nosso código para definir o observeOn Scheduler:

val subscription = newsManager.getNews()
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe ( ...
)

E com esta última mudança estamos criando nossos dados fictícios em outro thread e mostrando nossas novidades na tela 🙂

Mas temos que perceber que nosso aplicativo pode ser fechado a qualquer momento ou realizar uma operação de mudança de orientação no meio de uma assinatura. Isso pode nos levar a um problema se não gerenciarmos nossas assinaturas adequadamente, então vamos fazer algo com isso.

 

Subscriptions

Para evitar deixar nossas assinaturas no meio de um processo enquanto saímos de um fragmento ou fechamos nosso App, devemos gerenciá-lo com cuidado e uma boa prática é cancelar a assinatura de todas as assinaturas que criamos.

Uma boa técnica para fazer isso é adicionar todas as assinaturas que você cria em um objeto CompositeSubscription, esta classe é fornecida por RxJava e permite que você cancele a assinatura de todas as assinaturas que ela possui com apenas uma chamada de método. Vamos inicializar um objeto CompositeSubscription dentro do método onResume e cancelar a assinatura quando o método onPause for invocado. O código será mais ou menos assim:

var subscriptions = CompositeSubscription()

override fun onResume() {
    super.onResume()
    subscriptions = CompositeSubscription()
}

override fun onPause() {
    super.onPause()
    subscriptions.clear()
}

A única coisa que temos que fazer agora é adicionar nossa assinatura a este novo objeto CompositeSubscription e podemos deixar nosso aplicativo (ou fragmento, neste caso) a qualquer momento:

private fun requestNews() {
    val subscription = newsManager.getNews()
            .subscribeOn(Schedulers.io())
            .subscribe (...)
    subscriptions.add(subscription) // add the subscription
}

E apenas para torná-lo mais legível, movo toda essa lógica para um novo fragmento de base chamado “RxBaseFragment” que me dá toda essa lógica por padrão. Verifique o código para ver com mais detalhes.

 

Conclusão

Como você pode ver, Kotlin e RxJava funcionam perfeitamente juntos e neste tutorial estamos apenas vendo uma pequena parte de todas as coisas que você pode fazer com RxJava e graças ao Kotlin o código é totalmente legível e fácil de entender.

Até logo no próximo artigo!

Twitter: https://twitter.com/caneto

 

Artigo seguinte

Parte 6: API – Retrofit e Kotlin

Visits: 2 Visits: 1191930