هذا مستودع يحتوي على أمثلة مفيدة من العالم الحقيقي لاستخدام RxJava مع Android. عادةً ما يكون في حالة ثابتة من "العمل قيد التقدم" (WIP).
لقد أجريت أيضًا محادثات حول Learning Rx باستخدام العديد من الأمثلة المدرجة في هذا الريبو.
using
)أحد المتطلبات الشائعة هو إلغاء تحميل عمليات الإدخال/الإخراج المكثفة الطويلة إلى سلسلة رسائل في الخلفية (سلسلة رسائل غير تابعة لواجهة المستخدم) وإرسال النتائج مرة أخرى إلى سلسلة رسائل واجهة المستخدم/الرئيسية، عند الانتهاء. هذا عرض توضيحي لكيفية إلغاء تحميل العمليات طويلة الأمد إلى سلسلة رسائل في الخلفية. بعد الانتهاء من العملية، نستأنف مرة أخرى على الموضوع الرئيسي. كل ذلك باستخدام RxJava! فكر في هذا كبديل لـ AsyncTasks.
تتم محاكاة العملية الطويلة عن طريق حظر استدعاء Thread.sleep (نظرًا لأن ذلك يتم في سلسلة محادثات في الخلفية، فلن تتم مقاطعة واجهة المستخدم الخاصة بنا أبدًا).
لرؤية هذا المثال يلمع حقا. اضغط على الزر عدة مرات وشاهد كيف لا يتم حظر النقر على الزر (وهو عملية واجهة مستخدم) أبدًا لأن العملية الطويلة تعمل فقط في الخلفية.
هذا عرض توضيحي لكيفية تجميع الأحداث باستخدام عملية "المخزن المؤقت".
يتم توفير زر ونقوم بتجميع عدد النقرات على هذا الزر، على مدار فترة زمنية ثم نخرج النتائج النهائية.
إذا ضغطت على الزر مرة واحدة، فستصلك رسالة تفيد بأنه تم الضغط على الزر مرة واحدة. إذا قمت بالضغط عليه 5 مرات متواصلة خلال ثانيتين، فستحصل على سجل واحد يفيد بأنك ضغطت على هذا الزر 5 مرات (مقابل 5 سجلات فردية تقول "اضغط على الزر مرة واحدة").
ملحوظة:
إذا كنت تبحث عن حل أكثر ضمانًا يقوم بتجميع النقرات "المستمرة" مقابل عدد النقرات فقط خلال فترة زمنية، فانظر إلى العرض التوضيحي لـ EventBus حيث يتم استخدام مجموعة من عوامل publish
والمخزن buffer
. للحصول على شرح أكثر تفصيلاً، يمكنك أيضًا إلقاء نظرة على منشور المدونة هذا.
هذا عرض توضيحي لكيفية ابتلاع الأحداث بطريقة لا تحترم إلا الأحداث الأخيرة. ومن الأمثلة النموذجية على ذلك مربعات نتائج البحث الفورية. أثناء قيامك بكتابة كلمة "Bruce Lee"، فإنك لا تريد تنفيذ عمليات بحث عن B وBr وBru وBruce وBruce وBruce L... إلخ. ولكن بدلاً من ذلك انتظر بذكاء بضع لحظات، وتأكد من أن المستخدم انتهى من كتابة الكلمة بأكملها، ثم أطلق مكالمة واحدة لـ "Bruce Lee".
أثناء الكتابة في مربع الإدخال، لن يتم إرسال رسائل السجل عند كل تغيير في حرف الإدخال، بل سيختار فقط الحدث الذي تم إرساله أخيرًا (أي الإدخال) وتسجيل ذلك.
هذه هي طريقة debounce/throttleWithTimeout في RxJava.
التحديثية من Square هي مكتبة مذهلة تساعد في سهولة التواصل (حتى لو لم تكن قد انتقلت إلى RxJava بعد، فيجب عليك التحقق من ذلك). إنه يعمل بشكل أفضل مع RxJava وهذه أمثلة على GitHub API، مأخوذة مباشرة من حديث Jake Wharton، مطور android demigod في Netflix. يمكنكم مشاهدة المحاضرة على هذا الرابط. وبالمناسبة، كان دافعي لاستخدام RxJava هو حضور هذه المحادثة في Netflix.
(ملاحظة: من المرجح أن تصل إلى حصة GitHub API بسرعة كبيرة، لذا أرسل رمز OAuth المميز كمعلمة إذا كنت تريد الاستمرار في تشغيل هذه الأمثلة كثيرًا).
يعد التحديث التلقائي لطرق العرض أمرًا رائعًا. إذا كنت قد تعاملت مع Angular JS من قبل، فلديهم مفهوم أنيق جدًا يسمى "ربط البيانات ثنائي الاتجاه"، لذلك عندما يرتبط عنصر HTML بكائن نموذج/كيان، فإنه "يستمع" باستمرار إلى التغييرات في هذا الكيان و يقوم بالتحديث التلقائي لحالته بناءً على النموذج. باستخدام التقنية المذكورة في هذا المثال، يمكنك استخدام نمط مثل نمط عرض العرض التقديمي بسهولة كبيرة.
على الرغم من أن المثال هنا بدائي جدًا، إلا أن التقنية المستخدمة لتحقيق الربط المزدوج باستخدام Publish Subject
هي أكثر إثارة للاهتمام.
هذا مثال على الاستقصاء باستخدام برنامج جدولة RxJava. يعد هذا مفيدًا في الحالات التي تريد فيها إجراء استطلاع مستمر للخادم وربما الحصول على بيانات جديدة. يتم "محاكاة" استدعاء الشبكة، لذا فهو يفرض تأخيرًا قبل إرجاع السلسلة الناتجة.
هناك نوعان مختلفان لهذا:
المثال الثاني هو في الأساس متغير من الأسي Backoff.
بدلاً من استخدام RetryWithDelay، نستخدم RepeatWithDelay هنا. لفهم الفرق بين إعادة المحاولة (متى) والتكرار (متى) أود أن أقترح مشاركة دان الرائعة حول هذا الموضوع.
هناك طريقة بديلة لتأخير الاستقصاء دون استخدام repeatWhen
وهي استخدام عناصر التأخير المتداخلة المتسلسلة. راجع startExecutingWithExponentialBackoffDelay في مثال ExponentialBackOffFragment.
التراجع الأسي هو استراتيجية حيث نقوم، بناءً على ردود الفعل من مخرجات معينة، بتغيير معدل العملية (عادةً تقليل عدد مرات إعادة المحاولة أو زيادة وقت الانتظار قبل إعادة المحاولة أو إعادة تنفيذ عملية معينة).
المفهوم أكثر منطقية مع الأمثلة. يجعل RxJava من السهل (نسبيًا) تنفيذ مثل هذه الإستراتيجية. شكري لمايك لاقتراح الفكرة.
لنفترض أن لديك فشل في الشبكة. تتمثل الإستراتيجية المعقولة في عدم الاستمرار في إعادة محاولة الاتصال بالشبكة كل ثانية واحدة. سيكون من الذكاء بدلاً من ذلك (لا... أنيق!) إعادة المحاولة مع زيادة التأخير. لذا، حاولت في الثانية الأولى تنفيذ مكالمة الشبكة، بدون نرد؟ حاول بعد 10 ثواني... سلبي؟ حاول بعد 20 ثانية، لا يوجد ملف تعريف الارتباط؟ حاول بعد 1 دقيقة. إذا كان هذا الشيء لا يزال يفشل، فعليك أن تتخلى عن الشبكة!
نحن نحاكي هذا السلوك باستخدام RxJava مع عامل التشغيل retryWhen
.
مقتطف التعليمات البرمجية RetryWithDelay
من باب المجاملة:
انظر أيضًا إلى مثال الاقتراع حيث نستخدم آلية تراجع أسية مشابهة جدًا.
البديل الآخر لاستراتيجية التراجع الأسي هو تنفيذ عملية لعدد معين من المرات ولكن مع فترات زمنية متأخرة. لذلك تقوم بتنفيذ عملية معينة بعد ثانية واحدة من الآن، ثم تنفذها مرة أخرى بعد 10 ثوانٍ من الآن، ثم تنفذ العملية بعد 20 ثانية من الآن. بعد إجمالي 3 مرات تتوقف عن التنفيذ.
إن محاكاة هذا السلوك هي في الواقع أبسط بكثير من آلية إعادة المحاولة السابقة. يمكنك استخدام متغير من عامل delay
لتحقيق ذلك.
.combineLatest
)شكرًا لدان ليو على إعطائي هذه الفكرة في البودكاست المجزأ - الحلقة رقم 4 (حوالي الساعة 4:30).
يتيح لك .combineLatest
مراقبة حالة العديد من العناصر القابلة للملاحظة في وقت واحد بشكل مضغوط وفي مكان واحد. يوضح المثال الموضح كيف يمكنك استخدام .combineLatest
للتحقق من صحة النموذج الأساسي. هناك 3 مدخلات أساسية لهذا النموذج تعتبر "صالحة" (بريد إلكتروني وكلمة مرور ورقم). سيصبح النموذج صالحًا (يتحول النص أدناه إلى اللون الأزرق :P) بمجرد أن تكون جميع المدخلات صالحة. إذا لم تكن كذلك، فسيتم عرض خطأ مقابل المدخلات غير الصالحة.
لدينا 3 عناصر مراقبة مستقلة تتتبع تغييرات النص/الإدخال لكل حقل من حقول النموذج (يعد WidgetObservable
الخاص بـ RxAndroid مفيدًا لمراقبة تغييرات النص). بعد ملاحظة تغيير في الحدث من جميع المدخلات الثلاثة، يتم "دمج" النتيجة ويتم تقييم النموذج للتأكد من صحته.
لاحظ أن وظيفة Func3
التي تتحقق من الصلاحية، تبدأ فقط بعد أن تتلقى جميع المدخلات الثلاثة حدث تغيير النص.
تصبح قيمة هذه التقنية أكثر وضوحًا عندما يكون لديك عدد أكبر من حقول الإدخال في النموذج. إن التعامل معها بطريقة أخرى باستخدام مجموعة من القيم المنطقية يجعل التعليمات البرمجية تشوشًا ويصعب متابعتها نوعًا ما. لكن باستخدام .combineLatest
يتركز كل هذا المنطق في كتلة مدمجة لطيفة من التعليمات البرمجية (ما زلت أستخدم القيم المنطقية ولكن ذلك كان لجعل المثال أكثر قابلية للقراءة).
لدينا مصدران يمكن ملاحظتهما: ذاكرة تخزين مؤقت على القرص (سريعة) ومكالمة شبكة (حديثة). عادةً ما يكون القرص القابل للملاحظة أسرع بكثير من الشبكة القابلة للملاحظة. ولكن من أجل توضيح كيفية العمل، استخدمنا أيضًا ذاكرة تخزين مؤقت زائفة على القرص "أبطأ" فقط لنرى كيف يتصرف المشغلون.
يتم توضيح ذلك باستخدام 4 تقنيات:
.concat
.concatEager
.merge
.publish
محدد + دمج + takeUntilربما يكون الأسلوب الرابع هو ما تريد استخدامه في النهاية ولكن من المثير للاهتمام متابعة تطور التقنيات لفهم السبب.
concat
عظيم. يقوم باسترداد المعلومات من الشبكة الأولى التي يمكن ملاحظتها (ذاكرة التخزين المؤقت على القرص في حالتنا) ثم الشبكة اللاحقة التي يمكن ملاحظتها. نظرًا لأن ذاكرة التخزين المؤقت على القرص من المفترض أن تكون أسرع، فإن كل شيء يبدو جيدًا ويتم تحميل ذاكرة التخزين المؤقت على القرص بسرعة، وبمجرد انتهاء اتصال الشبكة، نقوم بتبديل النتائج "الجديدة".
المشكلة في concat
هي أن ما يمكن ملاحظته اللاحق لا يبدأ حتى حتى يكتمل أول ما يمكن ملاحظته. يمكن أن يكون ذلك مشكلة. نريد أن تبدأ جميع العناصر القابلة للملاحظة في وقت واحد ولكننا ننتج النتائج بالطريقة التي نتوقعها. لحسن الحظ، قدم RxJava concatEager
الذي يفعل ذلك بالضبط. يبدأ كلاً من العناصر القابلة للملاحظة ولكنه يقوم بتخزين النتيجة من الأخيرة حتى انتهاء الملاحظة السابقة. وهذا خيار قابل للتطبيق تمامًا.
ومع ذلك، في بعض الأحيان، قد ترغب فقط في البدء في عرض النتائج على الفور. بافتراض أن العنصر الأول الذي يمكن ملاحظته (لسبب غريب) يستغرق وقتًا طويلاً جدًا للتشغيل عبر جميع عناصره، حتى لو كانت العناصر القليلة الأولى من العنصر الثاني الذي يمكن ملاحظته قد وصلت إلى أسفل السلك، فسيتم وضعها في قائمة الانتظار بالقوة. لا تريد بالضرورة "الانتظار" على أي شيء يمكن ملاحظته. في هذه الحالات، يمكننا استخدام عامل merge
. يقوم بتشذير العناصر عند انبعاثها. يعمل هذا بشكل رائع ويبدأ في ظهور النتائج بمجرد ظهورها.
على غرار عامل التشغيل concat
، إذا كان Observable الأول الخاص بك دائمًا أسرع من Observable الثاني، فلن تواجه أي مشاكل. ومع ذلك، فإن مشكلة merge
هي: إذا تم إصدار عنصر من ذاكرة التخزين المؤقت لسبب غريب أو كان يمكن ملاحظته بشكل أبطأ بعد ما يمكن ملاحظته الأحدث/الأحدث، فسوف يحل محل المحتوى الأحدث. انقر فوق الزر "دمج (القرص البطيء)" في المثال لرؤية هذه المشكلة أثناء العمل. مساهمات @JakeWharton و @swankjesse تصل إلى 0! في العالم الحقيقي، قد يكون هذا أمرًا سيئًا، لأنه يعني أنه سيتم تجاوز البيانات الجديدة بواسطة بيانات القرص القديمة.
لحل هذه المشكلة، يمكنك استخدام الدمج مع عامل publish
الرائع الذي يأخذ "محددًا". لقد كتبت عن هذا الاستخدام في منشور بالمدونة ولكن يجب أن أشكر Jedi JW على تذكيري بهذه التقنية. publish
الشبكة التي يمكن ملاحظتها ونزودها بمحدد يبدأ في البث من ذاكرة التخزين المؤقت على القرص، حتى النقطة التي تبدأ فيها الشبكة التي يمكن ملاحظتها في البث. بمجرد أن تبدأ الشبكة التي يمكن ملاحظتها في البث، فإنها تتجاهل جميع النتائج من القرص الذي يمكن ملاحظته. هذا مثالي ويعالج أي مشاكل قد نواجهها.
في السابق، كنت أستخدم عامل merge
ولكني تغلبت على مشكلة الكتابة فوق النتائج من خلال مراقبة "resultAge". راجع مثال PseudoCacheMergeFragment
القديم إذا كنت مهتمًا برؤية هذا التنفيذ القديم.
هذا مثال بسيط ومباشر للغاية يوضح لك كيفية استخدام عوامل timer
interval
delay
في RxJava للتعامل مع مجموعة من الحالات التي تريد فيها تشغيل مهمة على فترات زمنية محددة. قل لا لـ Android TimerTask
s.
الحالات الموضحة هنا:
هناك منشورات مدونة مصاحبة تقوم بعمل أفضل بكثير في شرح التفاصيل في هذا العرض التوضيحي:
السؤال الشائع الذي يتم طرحه عند استخدام RxJava في Android هو "كيف يمكنني استئناف عمل عنصر يمكن ملاحظته في حالة حدوث تغيير في التكوين (تدوير النشاط، تغيير لغة اللغة وما إلى ذلك)؟".
يوضح لك هذا المثال استراتيجية واحدة. باستخدام الأجزاء المحتجزة. لقد بدأت في استخدام الأجزاء المحتجزة باعتبارها "أجزاء عاملة" بعد قراءة هذا المنشور الرائع الذي كتبه Alex Lockwood في وقت ما.
اضغط على زر البداية وقم بتدوير الشاشة بما يناسب قلبك؛ سترى ما يمكن ملاحظته يستمر من حيث توقف.
هناك بعض المراوغات حول "سخونة" المصدر الذي يمكن ملاحظته المستخدم في هذا المثال. تحقق من مشاركة مدونتي حيث أشرح التفاصيل.
لقد قمت منذ ذلك الحين بإعادة كتابة هذا المثال باستخدام نهج بديل. على الرغم من نجاح أسلوب ConnectedObservable
، إلا أنه يدخل إلى أراضي "البث المتعدد" الذي يمكن أن يكون صعبًا (أمان الخيط، .refcount وما إلى ذلك). المواضيع من ناحية أخرى هي أكثر بساطة بكثير. يمكنك أن ترى إعادة كتابته باستخدام Subject
هنا.
لقد كتبت تدوينة أخرى حول كيفية التفكير في الموضوعات التي أتناول فيها بعض التفاصيل.
Volley هي مكتبة شبكات أخرى قدمتها Google في IO '13. ساهم أحد مواطني جيثب الطيبين بهذا المثال حتى نعرف كيفية دمج Volley مع RxJava.
أنا الاستفادة من الاستخدام البسيط للموضوع هنا. بصراحة، إذا لم تكن قد وصلت العناصر الخاصة بك عبر Observable
بالفعل (مثل التحديث التحديثي أو طلب الشبكة)، فلا يوجد سبب وجيه لاستخدام Rx وتعقيد الأمور.
يرسل هذا المثال بشكل أساسي رقم الصفحة إلى الموضوع، ويتولى الموضوع إضافة العناصر. لاحظ استخدام concatMap
وإرجاع Observable<List>
من _itemsFromNetworkCall
.
بالنسبة للركلات، فقد قمت أيضًا بتضمين مثال PaginationAutoFragment
، وهو "ترقيم الصفحات التلقائي" دون الحاجة إلى الضغط على زر. يجب أن يكون من السهل اتباعه إذا فهمت كيفية عمل المثال السابق.
فيما يلي بعض التطبيقات الرائعة الأخرى (على الرغم من أنني استمتعت بقراءتها، إلا أنني لم أتمكن من استخدامها لتطبيقي الواقعي لأنني شخصيًا لا أعتقد أنها ضرورية):
يعبر مخطط ascii أدناه عن نية مثالنا التالي مع المهارة. f1,f2,f3,f4,f5 هي في الأساس مكالمات شبكة والتي عند إجرائها تعطي النتيجة المطلوبة لإجراء عملية حسابية مستقبلية.
(flatmap)
f1 ___________________ f3 _______
(flatmap) | (zip)
f2 ___________________ f4 _______| ___________ final output
|
____________ f5 _______|
تمت كتابة الكود الخاص بهذا المثال بالفعل بواسطة Mr.skehlet في interwebs. توجه إلى جوهر الكود. إنه مكتوب بلغة Java (6) النقية، لذا فهو مفهوم جدًا إذا كنت قد فهمت الأمثلة السابقة. سأقوم بطرحها هنا مرة أخرى عندما يسمح الوقت بذلك وإلا نفدت الأمثلة المقنعة الأخرى.
هذا مثال بسيط يوضح استخدام عامل التشغيل .timeout
. سيكمل الزر 1 المهمة قبل انتهاء قيد المهلة، بينما سيفرض الزر 2 خطأ المهلة.
لاحظ كيف يمكننا توفير ملاحظة مخصصة تشير إلى كيفية التصرف في ظل استثناء المهلة.
using
) المشغل الذي using
هو أقل شهرة نسبيًا ومن الصعب جدًا على Google الوصول إليه. إنها واجهة برمجة تطبيقات جميلة تساعد في إعداد مورد (مكلف) واستخدامه ثم التخلص منه بطريقة نظيفة.
الشيء الجميل في هذا المشغل هو أنه يوفر آلية لاستخدام الموارد التي يحتمل أن تكون مكلفة بطريقة محددة النطاق. باستخدام -> الإعداد والاستخدام والتخلص. فكر في اتصالات قاعدة البيانات (مثل مثيلات Realm)، واتصالات المقبس، وأقفال الخيوط، وما إلى ذلك.
البث المتعدد في Rx يشبه الفن المظلم. لا يعرف الكثير من الناس كيفية القيام بذلك دون قلق. يوضح هذا المثال مشتركين اثنين (على شكل أزرار) ويسمح لك بإضافة/إزالة المشتركين في نقاط زمنية مختلفة ومعرفة كيفية تصرف المشغلين المختلفين في ظل تلك الظروف.
مصدر الملاحظة هو مؤقت ( interval
) يمكن ملاحظته والسبب في اختيار ذلك هو اختيار عنصر يمكن ملاحظته غير منتهٍ عن عمد، حتى تتمكن من اختبار/تأكيد ما إذا كانت تجربة البث المتعدد الخاصة بك ستتسرب.
لقد ألقيت أيضًا محاضرة تفصيلية حول البث المتعدد في 360|Andev. إذا كان لديك الرغبة والوقت، أقترح بشدة مشاهدة هذا الحديث أولاً (على وجه التحديد مقطع تبديل مشغل البث المتعدد) ثم العبث بالمثال هنا.
لقد تم ترحيل كافة الأمثلة هنا لاستخدام RxJava 2.X.
نحن نستخدم مكتبة David Karnok's Interop في بعض الحالات حيث أن بعض المكتبات مثل RxBindings وRxRelays وRxJava-Math وما إلى ذلك لم يتم نقلها بعد إلى 2.x.
أحاول التأكد من أن الأمثلة ليست مفتعلة بشكل مفرط ولكنها تعكس حالة استخدام في العالم الحقيقي. إذا كان لديك أمثلة مفيدة مماثلة توضح استخدام RxJava، فلا تتردد في إرسال طلب سحب.
أنا ألتف حول RxJava أيضًا، لذا إذا كنت تشعر أن هناك طريقة أفضل للقيام بأحد الأمثلة المذكورة أعلاه، فافتح مشكلة تشرح كيفية القيام بذلك. والأفضل من ذلك، إرسال طلب سحب.
خيوط Rx هي عمل فوضوي. للمساعدة، يستخدم هذا المشروع أدوات YourKit للتحليل.
يدعم YourKit المشاريع مفتوحة المصدر بأدوات مبتكرة وذكية لمراقبة تطبيقات Java وتحديد مواصفاتها. YourKit هو منشئ ملف تعريف YourKit Java.
مرخص بموجب ترخيص Apache، الإصدار 2.0 ("الترخيص"). يمكنك الحصول على نسخة من الترخيص على
http://www.apache.org/licenses/LICENSE-2.0
ما لم يكن ذلك مطلوبًا بموجب القانون المعمول به أو تم الاتفاق عليه كتابيًا، يتم توزيع البرامج الموزعة بموجب الترخيص على أساس "كما هي"، دون ضمانات أو شروط من أي نوع، سواء كانت صريحة أو ضمنية. راجع الترخيص لمعرفة الأذونات والقيود التي تحكم اللغة المحددة بموجب الترخيص.
أنت توافق على أن جميع المساهمات في هذا المستودع، في شكل إصلاحات وطلبات سحب وأمثلة جديدة وما إلى ذلك، تتبع الترخيص المذكور أعلاه.