ستناقش المقالة 
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 في المشتركين. يجب أن تكون على دراية بالمواقف التي قد لا تكون فيها الأخطاء متاحة للمشتركين ، وتأكد من معالجة هذه المواقف.