1. Gambaran keseluruhan
Dalam tutorial ini, kita akan mengkaji penggunaan Reactive Extensions (Rx) dalam Kotlin idiomatik menggunakan perpustakaan RxKotlin.
RxKotlin bukanlah pelaksanaan Reactive Extensions, per se. Sebaliknya, ini kebanyakannya adalah kumpulan kaedah peluasan. Maksudnya, RxKotlin menambah perpustakaan RxJava dengan API yang dirancang dengan mempertimbangkan Kotlin.
Oleh itu, kami akan menggunakan konsep dari artikel kami, Pengantar RxJava, dan juga konsep Flowables yang telah kami paparkan dalam artikel khusus.
2. Persediaan RxKotlin
Untuk menggunakan RxKotlin dalam projek Maven kami, kami perlu menambahkan kebergantungan rxkotlin ke pom.xml kami :
io.reactivex.rxjava2 rxkotlin 2.3.0
Atau, untuk projek Gradle, ke build.gradle kami :
implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0'
Di sini, kami menggunakan RxKotlin 2.x, yang mensasarkan RxJava 2. Projek yang menggunakan RxJava 1 harus menggunakan RxKotlin 1.x. Konsep yang sama berlaku untuk kedua versi.
Perhatikan bahawa RxKotlin bergantung pada RxJava, tetapi mereka tidak sering mengemas kini kebergantungan ke rilis terbaru. Oleh itu, kami mengesyorkan untuk memasukkan secara jelas versi RxJava tertentu yang akan kami bergantung, seperti yang diperincikan dalam artikel RxJava kami.
3. Mewujudkan diperhatikan s dalam RxKotlin
RxKotlin merangkumi sebilangan kaedah peluasan untuk membuat objek yang Dapat Diperhatikan dan Alir dari koleksi.
Khususnya, setiap jenis array mempunyai kaedah toObservable () dan kaedah 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. Selesai s
RxKotlin juga menyediakan beberapa kaedah untuk membuat contoh Selesai . Khususnya, kita dapat mengubah fungsi Action s, Callable s, Future s, dan zero-arity menjadi Completable dengan kaedah peluasan menjadiCompletable:
var value = 0 val completable = { value = 3 }.toCompletable() assertFalse(completable.test().isCancelled()) assertEquals(3, value)
4. diperhatikan dan flowable ke Peta dan Multimap
Apabila kita mempunyai diperhatikan atau flowable yang menghasilkan Pasangan contoh, kita boleh mengubah mereka ke dalam Single diperhatikan yang menghasilkan Map:
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())
Seperti yang dapat kita lihat pada contoh sebelumnya, toMap menimpa nilai yang dikeluarkan lebih awal dengan nilai kemudian jika mempunyai kunci yang sama.
Sekiranya kita ingin mengumpulkan semua nilai yang dikaitkan dengan kunci ke dalam koleksi, kita gunakan untukMultimap sebagai gantinya:
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. Menggabungkan diperhatikan s dan flowable s
Salah satu mata jualan Rx kemungkinan untuk menggabungkan diperhatikan s dan flowable s dalam pelbagai cara. Memang, RxJava menyediakan sebilangan pengendali di luar kotak.
Selain itu, RxKotlin menyertakan beberapa kaedah peluasan lagi untuk menggabungkan Observable dan sejenisnya.
5.1. Menggabungkan Pelepasan yang Dapat Diperhatikan
Apabila kita memiliki Observable yang memancarkan Observable lain , kita dapat menggunakan salah satu kaedah peluasan di RxKotlin untuk menggabungkan nilai yang dipancarkan.
Khususnya, mergeAll menggabungkan pemerhatian dengan flatMap:
val subject = PublishSubject.create
() val observable = subject.mergeAll()
Yang sama seperti:
val observable = subject.flatMap { it }
Observable yang dihasilkan akan memancarkan semua nilai sumber Observable dalam urutan yang tidak ditentukan.
Begitu juga, concatAll menggunakan concatMap (nilai-nilai yang dipancarkan dalam perintah yang sama sebagai sumber), manakala switchLatest menggunakan switchMap (nilai-nilai yang dipancarkan daripada yang sebelumnya dipancarkan diperhatikan ).
Seperti yang telah kita lihat setakat ini, semua kaedah di atas disediakan untuk sumber Alir juga, dengan semantik yang sama.
5.2. Menggabungkan Completable s , Mungkin s, dan Single s
Apabila kita mempunyai Observable yang mengeluarkan contoh Selesai , Mungkin , atau Tunggal , kita dapat menggabungkannya dengan kaedah mergeAllXs yang sesuai seperti, misalnya, 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. Menggabungkan Iterable s untuk diperhatikan s
Sebagai gantinya, kumpulan contoh yang dapat Diperhatikan atau Alir , RxKotlin mempunyai beberapa operator lain, menggabungkan dan mergeDelayError . Mereka kedua-duanya mempunyai kesan menggabungkan semua diperhatikan s atau flowable menjadi sebuah yang akan mengeluarkan semua nilai dalam urutan:
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
Dalam artikel ini, kami telah meninjau perpustakaan RxKotlin secara menyeluruh, yang menambah RxJava untuk menjadikan API-nya lebih mirip Kotlin yang idiom.
Untuk maklumat lebih lanjut, sila rujuk halaman RxKotlin GitHub. Untuk lebih banyak contoh, kami mengesyorkan ujian RxKotlin.
Pelaksanaan semua contoh dan potongan kode ini boleh didapati di projek GitHub sebagai projek Maven dan Gradle, jadi mudah untuk diimport dan dijalankan sebagaimana adanya.