ستناقش المقالة
UndeliverableException
في
RxJava2
الإصدار
2.0.6
والإصدارات الأحدث. إذا اصطدم شخص ما ولم يستطع اكتشافه ، أو لم يسمع بهذه المشكلة على الإطلاق - أطلب قطة.
RxJava1
إلى ترجمة المشاكل في
production
بعد الانتقال من
RxJava1
إلى
RxJava2
. تمت كتابة النسخة الأصلية في 28 ديسمبر 2017 ، ولكن من الأفضل اكتشافها في وقت متأخر من عدمها.
نحن جميعًا مطورون
onError
الأخطاء في
onError
عندما نستخدم
RxJava
. هذا يعني أننا قمنا بحماية أنفسنا من أعطال التطبيق ، أليس كذلك؟
لا ، ليس صحيحًا.أدناه
RxJava
نظرة على اثنين من الأمثلة التي
RxJava
فيها التطبيق بسبب
RxJava
، حتى إذا
onError
تنفيذ
onError
بشكل صحيح.
معالج الأخطاء الأساسية في RxJava
يستخدم
RxJavaPlugins.onError
الأساسية. يعالج جميع الأخطاء التي لا يمكن تسليمها إلى المشترك. بشكل افتراضي ، يتم إرسال جميع الأخطاء إليه ، لذلك يمكن أن تحدث أعطال التطبيق الحرجة.
تصف
2.0.6
هذا السلوك:
أحد أهداف تصميم 2.x هو عدم وجود أخطاء مفقودة. في بعض الأحيان ينتهي التسلسل أو يتم إلغاؤه قبل رفع المصدر على onError
. في هذه الحالة ، لا يوجد خطأ للذهاب إلى الخطأ ويتم إرساله إلى RxJavaPlugins.onError
إذا لم يكن RxJava يحتوي على معالج أخطاء أساسي ، فسيتم إخفاء مثل هذه الأخطاء منا وسيكون المطورون في غموض حول المشاكل المحتملة في التعليمات البرمجية.
بدءًا من الإصدار
2.0.6
، يحاول
RxJavaPlugins.onError
أن يكون أكثر ذكاءً ويشارك أخطاء المكتبة / التنفيذ والمواقف التي يتعذر فيها تسليم الخطأ. الأخطاء المعينة لفئة "البق" تسمى كما هي ، في حين يتم تغليف الباقي في
UndeliverableException
ثم يتم استدعاؤها. يمكنك رؤية كل هذا المنطق
هنا (
isBug
و
isBug
).
أحد الأخطاء الرئيسية في نوبي
RxJava
هو
OnErrorNotImplementedException
. يحدث هذا الخطأ إذا
onError
observable
حدوث خطأ ولم يتم تنفيذ طريقة
onError
في المشترك. هذا الخطأ هو مثال على الخطأ الذي يعتبر
RxJava
معالج الخطأ" بالنسبة لمعالج الخطأ الأساسي ولا يتحول إلى
UndeliverableException
.
استثناءات غير قابلة للتسليم
نظرًا لسهولة إصلاح الأخطاء المتعلقة بـ "الأخطاء" ، فلن نتطرق إليها. تعتبر الأخطاء التي
RxJava
في
UndeliverableException
أكثر إثارة للاهتمام ، حيث قد لا يكون من الواضح دائمًا عدم إمكانية تسليم الخطأ إلى
onError
.
تعتمد الحالات التي يمكن أن يحدث فيها ذلك على ما تفعله المصادر والمشتركون على وجه التحديد. سننظر في الأمثلة أدناه ، ولكن بشكل عام يمكننا القول أن مثل هذا الخطأ يحدث إذا لم يكن هناك مشترك نشط قد يتم تسليم الخطأ إليه.
مثال مع zipWith ()
الخيار الأول الذي يمكنك من خلاله رفع
UndeliverableException
هو
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() } )
نجمع بين مصدرين معًا ، كل منهما يسبب خطأ. ماذا نتوقع؟ يمكننا أن نفترض أنه سيتم استدعاء
onError
مرتين ، لكن هذا يتعارض مع
مواصفات Reactive streams
.
بعد مكالمة واحدة إلى حدث طرفي ( onError
، onCompelete
) ، يلزم عدم إجراء مكالمات أخرى
اتضح أنه مع مكالمة واحدة إلى
onError
لم تعد مكالمة ثانية ممكنة. ماذا يحدث عندما يحدث خطأ ثان في المصدر؟ سيتم تسليمها إلى
RxJavaPlugins.onError
.
هناك طريقة سهلة للدخول في هذا الموقف وهي استخدام
zip
لدمج مكالمات الشبكة (على سبيل المثال ،
Observable
). في حالة حدوث خطأ في كلتا المكالمتين (على سبيل المثال ، لا يوجد اتصال بالإنترنت) ، سيتسبب كلا المصدرين في حدوث أخطاء ، يقع الأول منها في تنفيذ
onError
، وسيتم تسليم الثاني إلى معالج الأخطاء الأساسي (
RxJavaPlugins.onError
).
مثال ConnectableObservable بدون مشتركين
يمكن لـ
ConnectableObservable
أيضًا رفع
UndeliverableException
. تجدر الإشارة إلى أن
ConnectableObservable
يثير الأحداث بغض النظر عن وجود مشتركين نشطين ، ما عليك سوى استدعاء طريقة
connect()
. إذا حدث خطأ في
ConnectableObservable
في حالة عدم وجود مشتركين ، فسيتم تسليمه إلى معالج الأخطاء الأساسي.
في ما يلي مثال بريء جدًا قد يتسبب في حدوث مثل هذا الخطأ:
someApi.retrofitCall()
إذا
someApi.retrofitCall()
حدوث خطأ (على سبيل المثال ، لا يوجد اتصال بالإنترنت) ، فسوف يتعطل التطبيق ، حيث سيتم تسليم خطأ الشبكة إلى
RxJava
الأخطاء الأساسي
RxJava
.
يبدو هذا المثال خياليًا ، ولكن من السهل جدًا الدخول في موقف حيث لا يزال
ConnectableObservable
متصلًا ، ولكن ليس لديه مشتركون. لقد صادفت هذا عند استخدام
autoConnect()
عند الاتصال بواجهة برمجة التطبيقات. لا يقوم
autoConnect()
بتعطيل
autoConnect()
تلقائيًا. لقد
onStop
اشتراكي في طريقة
onStop
، ولكن نتيجة مكالمة الشبكة عادت بعد تدمير
Activity
onStop
التطبيق مع
UndeliverableException
.
معالجة الخطأ
فماذا تفعل مع هذه الأخطاء؟
الخطوة الأولى هي النظر في الأخطاء التي تحدث ومحاولة تحديد أسبابها. من الناحية المثالية ، إذا كان بإمكانك إصلاح المشكلة في مصدرها لمنع
RxJavaPlugins.onError
عن الخطأ إلى
RxJavaPlugins.onError
.
الحل لمثال
zipWith
هو أخذ أحد المصادر أو كليهما وتنفيذ
إحدى طرق التقاط الأخطاء فيها. على سبيل المثال ، يمكنك استخدام
onErrorReturn
لتمرير القيمة الافتراضية بدلاً من الخطأ.
من السهل إصلاح المثال
ConnectableObservable
- فقط تأكد من قطع الاتصال بـ
Observable
عندما يقوم المشترك الأخير
Observable
الاشتراك. على سبيل المثال ، يحتوي
autoConnect()
على تنفيذ زائد الحمل يأخذ وظيفة تحدد وقت الاتصال (
يمكن رؤية المزيد هنا ).
هناك طريقة أخرى لحل المشكلة وهي استبدال معالج الأخطاء الأساسية الخاص بك.
RxJavaPlugins.setErrorHandler(Consumer<Throwable>)
ذلك. إذا كان هذا هو الحل المناسب لك ، يمكنك التقاط جميع الأخطاء المرسلة إلى
RxJavaPlugins.onError
ومعالجتها على النحو الذي تراه مناسبًا. يمكن أن يكون هذا الحل معقدًا جدًا - تذكر أن
RxJavaPlugins.onError
يتلقى أخطاء من جميع تيارات
RxJava
في التطبيق.
إذا قمت بإنشاء
emitter.tryOnError()
الخاصة بك يدويًا ، يمكنك استدعاء
emitter.tryOnError()
بدلاً من
emitter.tryOnError()
. تُبلغ هذه الطريقة عن خطأ فقط إذا لم يتم إنهاء الدفق ولديه مشتركون. تذكر أن هذه الطريقة تجريبية.
إن جوهر هذه المقالة هو أنه لا يمكنك التأكد من عدم وجود أخطاء عند العمل مع RxJava إذا قمت ببساطة بتنفيذ
onError
في المشتركين. يجب أن تكون على دراية بالمواقف التي قد لا تكون فيها الأخطاء متاحة للمشتركين ، وتأكد من معالجة هذه المواقف.