Artikel ini akan membahas
RxJava2
di
RxJava2
versi
2.0.6
dan yang lebih baru. Jika seseorang bertabrakan dan tidak bisa mengetahuinya, atau sama sekali tidak mendengar masalah ini - saya minta kucing. Mereka mendorong terjemahan masalah dalam
production
setelah transisi dari
RxJava1
ke
RxJava2
. Dokumen asli ditulis pada 28 Desember 2017, tetapi lebih baik untuk mencari tahu terlambat daripada tidak sama sekali.
Kita semua adalah pengembang yang baik dan menangkap kesalahan di
onError
ketika kita menggunakan
RxJava
. Ini berarti bahwa kami telah melindungi diri dari gangguan aplikasi, bukan?
Tidak, tidak benar.Di bawah ini kita akan melihat beberapa contoh di mana aplikasi akan
RxJava
karena
RxJava
, bahkan jika
onError
diimplementasikan dengan benar.
Basic Error Handler di RxJava
RxJava
menggunakan
RxJavaPlugins.onError
sebagai pengendali kesalahan dasar. Ini menangani semua kesalahan yang tidak bisa dikirim ke pelanggan. Secara default, semua kesalahan dikirim ke sana, sehingga crash aplikasi kritis dapat terjadi.
2.0.6
menggambarkan perilaku ini:
Salah satu tujuan dari desain 2.x adalah kurangnya kesalahan yang hilang. Terkadang urutan berakhir atau dibatalkan sebelum sumber meningkatkan onError
. Dalam hal ini, kesalahan tidak memiliki tempat untuk pergi dan dikirim ke RxJavaPlugins.onError
Jika RxJava tidak memiliki penangan kesalahan dasar, kesalahan seperti itu akan disembunyikan dari kami dan pengembang akan berada dalam kegelapan tentang potensi masalah dalam kode.
Dimulai dengan versi
2.0.6
,
RxJavaPlugins.onError
mencoba menjadi lebih cerdas dan berbagi pustaka / kesalahan implementasi dan situasi saat kesalahan tidak dapat disampaikan. Kesalahan yang diberikan pada kategori "bug" disebut apa adanya, sedangkan sisanya dibungkus dalam
UndeliverableException
dan kemudian dipanggil. Anda dapat melihat semua logika ini di
sini (
isBug
dan
isBug
).
Salah satu kesalahan utama pemula dalam
RxJava
OnErrorNotImplementedException
adalah
OnErrorNotImplementedException
. Kesalahan ini terjadi jika
observable
menyebabkan kesalahan dan metode
onError
tidak diterapkan pada pelanggan. Kesalahan ini adalah contoh kesalahan yang untuk penangan kesalahan dasar
RxJava
adalah "bug" dan tidak berubah menjadi
UndeliverableException
.
Pengecualian tidak terkirim
Karena kesalahan yang terkait dengan "bug" mudah diperbaiki, kami tidak akan membahasnya. Kesalahan yang
RxJava
bungkus dalam
RxJava
lebih menarik, karena mungkin tidak selalu jelas mengapa kesalahan tidak dapat dikirim ke
onError
.
Kasus-kasus di mana hal ini dapat terjadi tergantung pada apa yang dilakukan sumber dan pelanggan secara khusus. Kami akan mempertimbangkan contoh-contoh di bawah ini, tetapi secara umum kami dapat mengatakan bahwa kesalahan semacam itu terjadi jika tidak ada pelanggan aktif kepada siapa kesalahan tersebut dapat disampaikan.
Contoh dengan zipWith ()
Opsi pertama di mana Anda dapat meningkatkan
zipWith
.
val observable1 = Observable.error<Int>(Exception()) val observable2 = Observable.error<Int>(Exception()) val zipper = BiFunction<Int, Int, String> { one, two -> "$one - $two" } observable1.zipWith(observable2, zipper) .subscribe( { System.out.println(it) }, { it.printStackTrace() } )
Kami menggabungkan dua sumber bersama-sama, yang masing-masing menyebabkan kesalahan. Apa yang kita harapkan Kita dapat mengasumsikan bahwa
onError
akan dipanggil dua kali, tetapi ini bertentangan
dengan spesifikasi Reactive streams
.
Setelah satu panggilan ke acara terminal ( onError
, onCompelete
), diperlukan bahwa tidak ada lagi panggilan yang dilakukan
Ternyata dengan satu panggilan ke
onError
panggilan kedua tidak lagi mungkin. Apa yang terjadi ketika kesalahan kedua terjadi di sumber? Ini akan dikirimkan ke
RxJavaPlugins.onError
.
Cara mudah untuk masuk ke situasi ini adalah dengan menggunakan
zip
untuk menggabungkan panggilan jaringan (misalnya, dua panggilan
Retrofit
Observable
). Jika kesalahan terjadi di kedua panggilan (misalnya, tidak ada koneksi Internet), kedua sumber akan menyebabkan kesalahan, yang pertama akan jatuh ke dalam implementasi
onError
, dan yang kedua akan dikirim ke penangan kesalahan dasar (
RxJavaPlugins.onError
).
Contoh ConnectableObservable tanpa pelanggan
ConnectableObservable
juga dapat meningkatkan
UndeliverableException
. Patut diingat bahwa
ConnectableObservable
memunculkan peristiwa terlepas dari keberadaan pelanggan aktif, panggil saja metode
connect()
. Jika kesalahan terjadi di
ConnectableObservable
ketika tidak ada pelanggan, itu akan dikirimkan ke penangan kesalahan dasar.
Berikut adalah contoh yang sangat tidak bersalah yang dapat menyebabkan kesalahan seperti itu:
someApi.retrofitCall()
Jika
someApi.retrofitCall()
menyebabkan kesalahan (misalnya, tidak ada koneksi Internet), aplikasi akan macet karena kesalahan jaringan akan dikirim ke
RxJava
kesalahan dasar
RxJava
.
Contoh ini tampaknya fiktif, tetapi sangat mudah untuk masuk ke situasi di mana
ConnectableObservable
masih terhubung, tetapi tidak memiliki pelanggan. Saya menemukan ini ketika menggunakan
autoConnect()
saat memanggil API.
autoConnect()
tidak secara otomatis menonaktifkan
Observable
. Saya berhenti berlangganan dalam metode
Activity
onStop
, tetapi hasil panggilan jaringan kembali setelah penghancuran
Activity
dan aplikasi
UndeliverableException
dengan
UndeliverableException
.
Menangani kesalahan
Jadi apa yang harus dilakukan dengan kesalahan ini?
Langkah pertama adalah melihat kesalahan yang terjadi dan mencoba menentukan apa yang menyebabkannya. Idealnya, jika Anda dapat memperbaiki masalah pada sumbernya untuk mencegah kesalahan dari
RxJavaPlugins.onError
ke
RxJavaPlugins.onError
.
Solusi untuk contoh
zipWith
adalah dengan mengambil satu atau kedua sumber dan mengimplementasikan
salah satu metode untuk menangkap kesalahan di dalamnya. Misalnya, Anda dapat menggunakan
onErrorReturn
untuk meneruskan nilai default alih-alih kesalahan.
Contoh
ConnectableObservable
lebih mudah untuk diperbaiki - hanya pastikan Anda memutuskan
Observable
ketika pelanggan terakhir berhenti berlangganan.
autoConnect()
, misalnya, memiliki implementasi kelebihan beban yang mengambil fungsi yang menangkap waktu koneksi (
lebih banyak dapat dilihat di sini ).
Cara lain untuk memecahkan masalah adalah mengganti handler kesalahan dasar dengan Anda sendiri. Metode
RxJavaPlugins.setErrorHandler(Consumer<Throwable>)
akan membantu Anda dengan ini. Jika ini adalah solusi yang tepat untuk Anda, Anda dapat menangkap semua kesalahan yang dikirim ke
RxJavaPlugins.onError
dan menanganinya sesuai keinginan Anda. Solusi ini bisa sangat rumit - ingat bahwa
RxJavaPlugins.onError
menerima kesalahan dari semua aliran
RxJava
dalam aplikasi.
Jika Anda secara manual membuat
Observable
Anda, Anda dapat memanggil
emitter.tryOnError()
alih-alih
emitter.tryOnError()
. Metode ini melaporkan kesalahan hanya jika aliran tidak dihentikan dan memiliki pelanggan. Ingat bahwa metode ini eksperimental.
Moral dari artikel ini adalah Anda tidak dapat memastikan bahwa tidak ada kesalahan ketika bekerja dengan RxJava jika Anda hanya menerapkan
onError
di pelanggan. Anda harus mengetahui situasi di mana kesalahan mungkin tidak tersedia untuk pelanggan, dan pastikan bahwa situasi ini ditangani.