Introduzione a RxKotlin

1. Panoramica

In questo tutorial, esamineremo l'uso di Reactive Extensions (Rx) in Kotlin idiomatico utilizzando la libreria RxKotlin.

RxKotlin non è un'implementazione di Reactive Extensions, di per sé. Invece, è principalmente una raccolta di metodi di estensione. Cioè, RxKotlin aumenta la libreria RxJava con un'API progettata pensando a Kotlin.

Pertanto, utilizzeremo i concetti del nostro articolo, Introduzione a RxJava, così come il concetto di Flowables che abbiamo presentato in un articolo dedicato.

2. Installazione di RxKotlin

Per utilizzare RxKotlin nel nostro progetto Maven, dovremo aggiungere la dipendenza rxkotlin al nostro pom.xml:

 io.reactivex.rxjava2 rxkotlin 2.3.0 

Oppure, per un progetto Gradle, al nostro build.gradle:

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

Qui stiamo usando RxKotlin 2.x, che ha come target RxJava 2. I progetti che usano RxJava 1 dovrebbero usare RxKotlin 1.x. Gli stessi concetti si applicano a entrambe le versioni.

Nota che RxKotlin dipende da RxJava, ma non aggiorna frequentemente la dipendenza all'ultima versione. Quindi, consigliamo di includere esplicitamente la versione specifica di RxJava da cui dipenderemo, come dettagliato nel nostro articolo RxJava.

3. Creazione di osservabili in RxKotlin

RxKotlin include una serie di metodi di estensione per creare oggetti Observable e Flowable dalle raccolte.

In particolare, ogni tipo di array ha un metodo toObservable () e un metodo toFlowable () :

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. Completabile s

RxKotlin fornisce anche alcuni metodi per creare completabile istanze. In particolare, possiamo convertire le funzioni Action s, Callable s, Future se zero-arity in Completable con il metodo di estensione toCompletable:

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

4. Osservabile e scorrevole su mappa e multimappa

Quando abbiamo un osservabile o un fluido che produce istanze di coppia , possiamo trasformarli in un osservabile singolo che produce una mappa:

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())

Come possiamo vedere nell'esempio precedente, toMap sovrascrive i valori emessi in precedenza con valori successivi se hanno la stessa chiave.

Se vogliamo accumulare tutti i valori associati a una chiave in una raccolta, utilizziamo invece toMultimap :

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. La combinazione osservabili s e Fluido s

Uno dei punti di vendita Rx è la possibilità di combinare osservabili s e Flowable s in vari modi. Infatti, RxJava fornisce una serie di operatori pronti all'uso.

In aggiunta a ciò, RxKotlin include alcuni altri metodi di estensione per combinare Observable se simili.

5.1. Combinazione di emissioni osservabili

Quando abbiamo un Observable che emette altri Observable , possiamo usare uno dei metodi di estensione in RxKotlin per combinare insieme i valori emessi.

In particolare, mergeAll combina le osservabili con flatMap:

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

Che sarebbe lo stesso di:

val observable = subject.flatMap { it }

L' Osservabile risultante emetterà tutti i valori della sorgente Osservabile in un ordine non specificato.

Allo stesso modo, concatAll utilizza concatMap (i valori vengono emessi nello stesso ordine delle sorgenti), mentre switchLatest utilizza switchMap (i valori vengono emessi dall'ultimo Observable emesso ).

Come abbiamo visto finora, tutti i metodi precedenti sono forniti anche per sorgenti Flowable , con la stessa semantica.

5.2. Combinando completabile s , Forse s, e Singolo s

Quando abbiamo un Observable che emette istanze di Completable , Forse o Single , possiamo combinarli con il metodo mergeAllXs appropriato come, ad esempio, 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. Combinazione di s Iterable di Observable s

Per le collezioni di osservabili o fluidi casi, invece, RxKotlin ha un paio di altri operatori, si fondono e mergeDelayError . Entrambi hanno l'effetto di combinare tutti gli osservabili o fluidi in uno che emetterà tutti i valori in sequenza:

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

The difference between the two operators — which are directly derived from the same-named operators in RxJava — is their treatment of errors.

The merge method emits errors as soon as they're emitted by the source:

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

Whereas mergeDelayError 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. Handling Values of Different Types

Let's now look at the extension methods in RxKotlin for dealing with values of different types.

These are variants of RxJava methods, that make use of Kotlin's reified generics. In particular, we can:

  • cast emitted values from one type to another, or
  • filter out values that are not of a certain type

So, we could, for example, cast an Observable of Numbers to one of Ints:

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

Here, the cast is unnecessary. However, when combining different observables together, we might need it.

With ofType, instead, we can filter out values that aren't of the type we expect:

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

As always, cast and ofType are applicable to both Observables and Flowables.

Furthermore, Maybe supports these methods as well. The Single class, instead, only supports cast.

7. Other Helper Methods

Finally, RxKotlin includes several helper methods. Let's have a quick look.

We can use subscribeBy instead of subscribe – it allows named parameters:

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

Similarly, for blocking subscriptions we can use blockingSubscribeBy.

Additionally, RxKotlin includes some methods that mimic those in RxJava but work around a limitation of Kotlin's type inference.

For example, when using Observable#zip, specifying the zipper doesn't look so great:

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

So, RxKotlin adds Observables#zip for more idiomatic usage:

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

Notice the final “s” in Observables. Similarly, we have Flowables, Singles, and Maybes.

8. Conclusions

In questo articolo, abbiamo esaminato a fondo la libreria RxKotlin, che aumenta RxJava per rendere la sua API più simile a Kotlin idiomatica.

Per ulteriori informazioni, fare riferimento alla pagina GitHub di RxKotlin. Per ulteriori esempi, consigliamo i test RxKotlin.

L'implementazione di tutti questi esempi e frammenti di codice può essere trovata nel progetto GitHub come progetto Maven e Gradle, quindi dovrebbe essere facile da importare ed eseguire così com'è.