Introdução ao RxKotlin

Introdução ao RxKotlin

1. Visão geral

Neste tutorial, vamos revisar o uso deReactive Extensions (Rx) in idiomatic Kotlin using the RxKotlin library.

O RxKotlin não é uma implementação de Extensões Reativas, por si só. Em vez disso, é principalmente uma coleção de métodos de extensão. Ou seja, o RxKotlin aumenta a bibliotecaRxJava com uma API projetada com Kotlin em mente.

Portanto, usaremos conceitos de nosso artigo,Introduction to RxJava, bem como o conceito deFlowables que apresentamos em um artigo dedicado.

2. Configuração RxKotlin

Para usar RxKotlin em nosso projeto Maven, precisaremos adicionarthe rxkotlin dependency ao nossopom.xml:


    io.reactivex.rxjava2
    rxkotlin
    2.3.0

Ou, para um projeto Gradle, para nossobuild.gradle:

implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0'

Here, we’re using RxKotlin 2.x, which targets RxJava 2. Projetos usando RxJava 1 devem usar RxKotlin 1.x. Os mesmos conceitos se aplicam às duas versões.

Observe que o RxKotlin depende do RxJava, mas eles não atualizam a dependência com frequência para a versão mais recente. Portanto, recomendamos incluir explicitamente a versão específica do RxJava da qual dependemos, conforme detalhado emour RxJava article.

3. CriandoObservables em RxKotlin

RxKotlin inclui vários métodos de extensão para criar objetosObservableeFlowable a partir de coleções.

Em particular,every type of array has a toObservable() method and a toFlowable() method:

val observable = listOf(1, 1, 2, 3).toObservable()
observable.test().assertValues(1, 1, 2, 3)
val flowable = listOf(1, 1, 2, 3).toFlowable()
flowable.buffer(2).test().assertValues(listOf(1, 1), listOf(2, 3))

3.1. Completables

RxKotlin também fornece alguns métodos para criar instâncias deCompletable. Em particular,we can convert Actions, Callables, Futures, and zero-arity functions to Completable with the extension method toCompletable:

var value = 0
val completable = { value = 3 }.toCompletable()
assertFalse(completable.test().isCancelled())
assertEquals(3, value)

4. Observable eFlowable aMap eMultimap

Quando temos umObservable ouFlowable que produzPair instâncias, podemos transformá-los em umSingle observável que produz umMap:

val list = listOf(Pair("a", 1), Pair("b", 2), Pair("c", 3), Pair("a", 4))
val observable = list.toObservable()
val map = observable.toMap()
assertEquals(mapOf(Pair("a", 4), Pair("b", 2), Pair("c", 3)), map.blockingGet())

Como podemos ver no exemplo anterior,toMap substitui os valores emitidos anteriormente por valores posteriores se eles tiverem a mesma chave.

Se quisermos acumular todos os valores associados a uma chave em uma coleção, usamostoMultimap em vez disso:

val list = listOf(Pair("a", 1), Pair("b", 2), Pair("c", 3), Pair("a", 4))
val observable = list.toObservable()
val map = observable.toMultimap()
assertEquals(
  mapOf(Pair("a", listOf(1, 4)), Pair("b", listOf(2)), Pair("c", listOf(3))),
  map.blockingGet())

5. CombinandoObservables eFlowables

Um dos pontos de venda do Rx é a possibilidade de combinarObservables eFlowables de várias maneiras. De fato, o RxJava fornece vários operadores prontos para uso.

Além disso, o RxKotlin inclui mais alguns métodos de extensão para combinarObservables e semelhantes.

5.1. Combinando Emissões Observáveis

Quando temos umObservable que emite outroObservables, podemos usar um dos métodos de extensão em RxKotlin para combinar os valores emitidos.

Em particular,mergeAll combines the observables with flatMap:

val subject = PublishSubject.create>()
val observable = subject.mergeAll()

Qual seria o mesmo que:

val observable = subject.flatMap { it }

OObservable resultante emitirá todos os valores da fonteObservables em uma ordem não especificada.

Similarly, concatAll uses concatMap (os valores são emitidos na mesma ordem das fontes),while switchLatest uses switchMap (os valores são emitidos a partir do últimoObservable emitido).

Como vimos até agora,all the above methods are provided for Flowable sources as well, com a mesma semântica.

5.2. CombinandoCompletables _, _Maybes eSingles

Quando temos umObservable que emite instâncias deCompletable,Maybe ouSingle, podemos combiná-los com o métodomergeAllXs apropriado como, por exemplo, mergeAllMaybes:

val subject = PublishSubject.create>()
val observable = subject.mergeAllMaybes()
subject.onNext(Maybe.just(1))
subject.onNext(Maybe.just(2))
subject.onNext(Maybe.empty())
subject.onNext(Maybe.error(Exception("error")))
subject.onNext(Maybe.just(3))
observable.test().assertValues(1, 2).assertError(Exception::class.java)

5.3. CombinandoIterables deObservables

Em vez disso, para coleções de instânciasObservable ouFlowable, RxKotlin tem alguns outros operadores,mergeemergeDelayError. Ambos têm o efeito decombining all the Observables or Flowables into one that will emit all the values in sequence:

val observables = mutableListOf(Observable.just("first", "second"))
val observable = observables.merge()
observables.add(Observable.just("third", "fourth"))
observable.test().assertValues("first", "second", "third", "fourth")

A diferença entre os dois operadores - que são derivados diretamente dos operadores com o mesmo nome em RxJava - é seutreatment of errors.

O métodomerge emite erros assim que eles são emitidos pela fonte:

// ...
observables.add(Observable.error(Exception("e")))
observables.add(Observable.just("fifth"))
// ...
observable.test().assertValues("first", "second", "third", "fourth")

Considerando quemergeDelayError emits them at the end of the stream:

// ...
observables.add(Observable.error(Exception("e")))
observables.add(Observable.just("fifth"))
// ...
observable.test().assertValues("first", "second", "third", "fourth", "fifth")

6. Lidando com Valores de Tipos Diferentes

Vejamos agora os métodos de extensão no RxKotlin para lidar com valores de diferentes tipos.

Estas são variantes dos métodos RxJava, que fazem uso dos genéricos reificados de Kotlin. Em particular, podemos:

  • cast emitted values from one type to another, ou

  • filtrar valores que não sejam de um determinado tipo

Então, poderíamos, por exemplo, lançar umObservable deNumbers para um deInts:

val observable = Observable.just(1, 1, 2, 3)
observable.cast().test().assertValues(1, 1, 2, 3)

Aqui, o elenco é desnecessário. No entanto, ao combinar diferentes observáveis, podemos precisar.

Em vez disso, comofType,, podemos filtrar os valores que não são do tipo que esperamos:

val observable = Observable.just(1, "and", 2, "and")
observable.ofType().test().assertValues(1, 2)

Como sempre,cast eofType são aplicáveis ​​aObservables eFlowables.

Além disso,Maybe suporta esses métodos também. A classeSingle, em vez disso, suporta apenascast.

7. Outros métodos auxiliares

Finalmente, o RxKotlin inclui vários métodos auxiliares. Vamos dar uma olhada rápida.

Podemos usarsubscribeBy em vez desubscribe - permite parâmetros nomeados:

Observable.just(1).subscribeBy(onNext = { println(it) })

Da mesma forma, para bloquear assinaturas, podemos usarblockingSubscribeBy.

Além disso, o RxKotlin inclui alguns métodos que imitam aqueles do RxJava, mas contornam uma limitação da inferência de tipo de Kotlin.

Por exemplo, ao usarObservable#zip,  especificandozipper  não parece tão bom:

Observable.zip(Observable.just(1), Observable.just(2), BiFunction { a, b -> a + b })

Então,RxKotlin adds Observables#zip for more idiomatic usage:

Observables.zip(Observable.just(1), Observable.just(2)) { a, b -> a + b }

Observe o “s” final emObservables. Da mesma forma, temosFlowables, Singles, eMaybes.

8. Conclusões

Neste artigo, revisamos completamente a biblioteca RxKotlin, que aumenta o RxJava para tornar sua API mais parecida com o Kotlin idiomático.

Para obter mais informações, consulteRxKotlin GitHub page. Para mais exemplos, recomendamosRxKotlin tests.

A implementação de todos esses exemplos e trechos de código pode ser encontrada emthe GitHub project como um projeto Maven e Gradle, portanto, deve ser fácil importar e executar como está.