معالج الأخطاء القياسي في RxJava2 أو سبب تسبب RxJava في تعطل التطبيق حتى إذا تم تنفيذ onError

ستناقش المقالة 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() //     Retrofit .publish() .connect() 

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

Source: https://habr.com/ru/post/ar422611/


All Articles