أفضل مشغلي RxJava لتطبيقات REST في Android

هناك العديد من العوامل المختلفة في حزمة RxJava القياسية. بعضها قوي حقًا ومعقد للاستخدام ، والبعض الآخر بسيط إلى حد ما. ولكن هناك شيء واحد مشترك بين العديد من مشغلي RxJava:

معظمهم لن تستخدمه أبدًا

باعتباري مطورًا Android يوميًا يقوم بكل الأشياء في RxJava ، سعت كثيرًا إلى استخدام مشغل zip () وفي كل مرة أخفق فيها. لقد وجدت دائمًا شيئًا أفضل منه ، أو موقف لن يغطيه هذا المشغل. أنا لا أقول أن الرمز البريدي () ليس له أي استخدام على الإطلاق ، وقد يعجبك شخص ما ، وإذا كان ذلك مناسبًا لك - فهذا رائع. ولكن دعنا نناقش بعض العوامل التي أجدها مفيدة للغاية ، وهي رائعة وسهلة الاستخدام في تطبيق يستند إلى REST.

وهنا هم:

  • شارك()
  • الإعادة (1) .refCount ()
إذا كنت تعرف بالفعل ما الذي يقومون به ، فقد تترك صفقًا بالنسبة لي وتنتهي من القراءة في هذه المرحلة.

حار أو بارد؟

أحد أهم الأشياء التي يصعب فهمها في كثير من الأحيان هو ما إذا كان يمكن ملاحظته ساخن أو بارد. كان هناك العديد من المقالات العظيمة التي تشرح ذلك وليست لدي نية للقيام بذلك مرة أخرى ، بدلاً من ذلك سأعرض لك أمثلة على كيفية عمله في الممارسة العملية.

بعد كل شيء ، هل يهم إذا كانت مكالمتك ملحوظة ساخنة أو باردة أو دافئة؟

لا.

كل ما يهم هو: إذا كان يقوم بهذه المهمة.

بشكل عام قد تحتاج إلى نوعين من الملاحظات:

  • يمكن ملاحظتها أن يتذكر آخر قيمة ينبعث منها ، ويصدرها لجميع المشتركين الجدد ،
  • يمكن ملاحظتها أن لا تتذكر آخر قيمة تم إصدارها.

كلام رخيص. أرني الرمز

دعنا نقول أنه في تطبيقنا نريد تنزيل بعض البيانات وعرضها. دعونا نتخيل أسهل طريقة للقيام بذلك:

val usersObservable = service.getUsers ()
         .subscribeOn (networkScheduler)
         .observeOn (UiScheduler)
         .subscribe {view.update (it)}
         .subscribe {view.update (it)}

هناك. الآن دعونا نضيف معالجة الأخطاء:

val usersObservable = service.getUsers ()
         .subscribeOn (networkScheduler)
         .observeOn (UiScheduler)
usersObservable
         .filter {it.isNotError ()}
         .subscribe {view.update (it)}
usersObservable
         .filter {it.isError ()}
         .subscribe {view.showErrorMessage ()}

عظيم. ولكن أيضًا دعونا نضيف حدث تقدم وقائمة فارغة لأفضل UX:

val usersObservable = service.getUsers ()
         .subscribeOn (networkScheduler)
         .observeOn (UiScheduler)
usersObservable
         .filter {it.isNotError ()}
         .subscribe {view.update (it)}
usersObservable
         .filter {it.isError ()}
         .subscribe {view.showErrorMessage ()}
usersObservable
         .MAP (كاذبة)
         .startWith (صحيح)
         .subscribe {progressLoading.visibility = it}
usersObservable
         .MAP (it.isEmpty ())
         .startWith (كاذبة)
         .subscribe {blankMessage.visibility = it}

الآن ... هل هناك شيء خاطئ في هذا الرمز؟ يمكننا اختباره.

@اختبار
اختبار ممتع () {
    val usersOrError = Observable.just (listOf ("user1"، "user2"))
            .mergeWith (Observable.never ())
            .doOnNext {println (it)}

    usersOrError.subscribe ()
    usersOrError.subscribe ()
    usersOrError.subscribe ()
    usersOrError.subscribe ()

}

في الاختبار أعلاه ، يوجد isbservable.just () بدلاً من طلب REST. لماذا دمج مع (أبدا ())؟ لأننا لا نريد إكمال ملاحظتنا قبل أن تتاح لكل مشترك فرصة الاشتراك فيه. يمكن ملاحظة موقف مماثل (لا يمكن ملاحظته نهائيًا) عندما يتم تشغيل بعض الطلبات عن طريق إدخال نقرات المستخدم. سيتم تغطية هذه الحالة لاحقًا في المقالة. كما تم تبسيط الملاحظات الأربعة المستخدمة في المثال السابق للاشتراك () فقط. يمكننا تجاهل جزء المجدول ، حيث أن كل شيء يحدث في موضوع واحد. النتيجة النهائية هي:

[user1 ، user2]
[user1 ، user2]
[user1 ، user2]
[user1 ، user2]

لقد أدى كل اشتراك في userOrError يمكن ملاحظته إلى تشغيل println () مما يعني أننا في تطبيق الحياة الواقعية قمنا بتشغيل أربعة طلبات فقط بدلاً من طلب واحد. هذا يمكن أن يكون وضعا خطيرا للغاية. تخيل أنه بدلاً من طلب GET المحتمل أن يكون غير ضار ، فسنقوم بإجراء POST أو استدعاء طريقة أخرى تغير حالة البيانات أو التطبيق. سيتم تنفيذ الطلب نفسه أربع مرات ، على سبيل المثال سيتم إنشاء أربع مشاركات أو تعليقات متطابقة.

لحسن الحظ ، يمكننا إصلاحه بسهولة عن طريق إضافة إعادة (1) .refCount ().

@اختبار
متعة `إعادة تشغيل refCount operator` () {
    val usersOrError = Observable.just (listOf ("user1"، "user2"))
            .mergeWith (Observable.never ())
            .doOnNext {println (it)}
            .replay (1)
            .refCount ()

    usersOrError.subscribe ()
    usersOrError.subscribe ()
    usersOrError.subscribe ()
    usersOrError.subscribe ()

}

نتيجة هذا الاختبار هي:

[user1 ، user2]

رائع ، لقد شاركنا اشتراكنا بنجاح بين جميع المشتركين. الآن لا يوجد تهديد بتقديم طلبات متعددة غير ضرورية. لنجرب نفس الشيء الذي يمكن ملاحظته باستخدام عامل المشاركة () بدلاً من إعادة التشغيل (1) .refCount ().

@اختبار
متعة `مشغل حصة الاختبار` () {
    val usersOrError = Observable.just (listOf ("user1"، "user2"))
            .mergeWith (Observable.never ())
            .doOnNext {println (it)}
            .شارك()

    usersOrError.subscribe ()
    usersOrError.subscribe ()
    usersOrError.subscribe ()
    usersOrError.subscribe ()

}

من المثير للدهشة (أم لا) أن النتيجة هي نفسها كما في السابق:

[user1 ، user2]

لمشاهدة الفرق بين share () و replay (1) .refCount () ، دعونا نجري اختبارين آخرين. هذه المرة سوف نتصل بطلبنا وهمية بعد الحصول على الحدث نقرة من المستخدم. انقر فوق الحدث سيتم الاستهزاء به بواسطة aPublishSubject. سطر إضافي: doOnNext {println ("1")} سيعرض المشترك الذي حصل على الحدث من usersOrError

سيتم استخدام الاختبار الأول share () ثم إعادة قراءة ثانية واحدة (1) .refCount.

@اختبار
متعة `مشغل حصة الاختبار مع click` () {
    val clickEvent = PublishSubject.create  ()

    val usersOrError = clickEvent
            .flatMap {Observable.just (listOf ("user1"، "user2"))}}
            .شارك()

    usersOrError.doOnNext {println ("1")} .subscribe ()
    usersOrError.doOnNext {println ("2")} .subscribe ()

    clickEvent.onNext (Any ()) // click click

    usersOrError.doOnNext {println ("3")} .subscribe ()
    usersOrError.doOnNext {println ("4")} .subscribe ()

}

نتيجة:

1
2

@اختبار
متعة `إعادة تشغيل مشغلي refCount بنقرة` () {
    val clickEvent = PublishSubject.create  ()

    val usersOrError = clickEvent
            .flatMap {Observable.just (listOf ("user1"، "user2"))}}
            .replay (1)
            .refCount ()

    usersOrError.doOnNext {println ("1")} .subscribe ()
    usersOrError.doOnNext {println ("2")} .subscribe ()

    clickEvent.onNext (Any ()) // click click

    usersOrError.doOnNext {println ("3")} .subscribe ()
    usersOrError.doOnNext {println ("4")} .subscribe ()

}

نتيجة:

1
2
3
4

خاتمة

تعد كل من share () و replay (1) .refCount () عوامل تشغيل مهمة للتعامل مع طلبات REST وأكثر من ذلك بكثير. في كل مرة تحتاج فيها إلى الملاحظة نفسها في أماكن متعددة ، إنها أفضل طريقة للذهاب. فكر فقط إذا كنت تريد أن تتذكر ملاحظتك آخر الأحداث وتمريرها إلى كل مشترك جديد أو ربما تكون مهتمًا بعملية لمرة واحدة فقط. فيما يلي بعض أمثلة التطبيق الواقعي:

  • getUsers () أو getPosts () أو ما شابه يمكن ملاحظته والتي يتم استخدامها للحصول على البيانات من المحتمل أن تستخدم (1) .refCount () ،
  • updateUser () ، addComment () من ناحية أخرى هي عمليات لمرة واحدة فقط وفي هذه الحالة ، ستؤدي المشاركة () بشكل أفضل ،
  • يجب أن يشتمل تمرير حدث النقر الملف في Observable - RxView.clicks (عرض) - أيضًا على عامل تشغيل share () ، للتأكد من أنه سيتم بث حدث النقر إلى كل مشترك.

TL، DR

  • share () -> المشاركات التي يمكن ملاحظتها لجميع المشتركين ، لا تنبعث منها أحدث قيمة للمشتركين الجدد
  • إعادة (1) .refCount () -> يشارك الملاحظ لجميع المشتركين وتصدر أحدث قيمة لكل مشترك جديد

إذا كنت تحب عملي اضغط على زر and واسمحوا لي أن أعرف ما هو رأيك في التعليقات.