تعرف على RxJava: مكتبة البرمجة التفاعلية المفقودة لنظام Android
نشرت: 2022-03-11إذا كنت مطور Android ، فمن المحتمل أنك سمعت عن RxJava. إنها واحدة من أكثر المكتبات التي تمت مناقشتها لتمكين البرمجة التفاعلية في تطوير Android. يوصف بأنه إطار عمل go-to لتبسيط المهام المتزامنة / غير المتزامنة المتأصلة في برمجة الأجهزة المحمولة.
لكن ... ما هو RxJava وكيف يعمل على "تبسيط" الأشياء؟
في حين أن هناك الكثير من الموارد المتاحة بالفعل على الإنترنت تشرح ماهية RxJava ، فإن هدفي في هذه المقالة هو أن أقدم لك مقدمة أساسية عن RxJava وتحديدًا كيف تتناسب مع تطوير Android. سأقدم أيضًا بعض الأمثلة والاقتراحات الملموسة حول كيفية دمجها في مشروع جديد أو قائم.
لماذا تفكر في RxJava؟
في جوهره ، يبسط RxJava التطوير لأنه يرفع مستوى التجريد حول الخيوط. أي ، بصفتك مطورًا ، لا داعي للقلق كثيرًا بشأن تفاصيل كيفية إجراء العمليات التي يجب أن تحدث على سلاسل رسائل مختلفة. يعد هذا أمرًا جذابًا بشكل خاص نظرًا لصعوبة الوصول إلى مؤشر الترابط بشكل صحيح ، وإذا لم يتم تنفيذه بشكل صحيح ، فقد يتسبب في بعض أصعب الأخطاء في التصحيح والإصلاح.
من المؤكد أن هذا لا يعني أن RxJava مضاد للرصاص عندما يتعلق الأمر بالخيوط ولا يزال من المهم فهم ما يحدث وراء الكواليس ؛ ومع ذلك ، يمكن لـ RxJava بالتأكيد أن تجعل حياتك أسهل.
لنلقي نظرة على مثال.
مكالمة الشبكة - RxJava مقابل AsyncTask
لنفترض أننا نريد الحصول على البيانات عبر الشبكة وتحديث واجهة المستخدم كنتيجة لذلك. تتمثل إحدى طرق القيام بذلك في (1) إنشاء فئة فرعية AsyncTask
داخلية في Activity
/ Fragment
الخاص بنا ، و (2) إجراء عملية الشبكة في الخلفية ، و (3) أخذ نتيجة تلك العملية وتحديث واجهة المستخدم في السلسلة الرئيسية .
public class NetworkRequestTask extends AsyncTask<Void, Void, User> { private final int userId; public NetworkRequestTask(int userId) { this.userId = userId; } @Override protected User doInBackground(Void... params) { return networkService.getUser(userId); } @Override protected void onPostExecute(User user) { nameTextView.setText(user.getName()); // ...set other views } } private void onButtonClicked(Button button) { new NetworkRequestTask(123).execute() }
كما قد يبدو هذا غير ضار ، فإن هذا النهج به بعض المشكلات والقيود. على وجه التحديد ، يتم إنشاء تسريبات الذاكرة / السياق بسهولة نظرًا لأن NetworkRequestTask
هي فئة داخلية وبالتالي فهي تحتوي على مرجع ضمني للفئة الخارجية. أيضًا ، ماذا لو أردنا ربط عملية طويلة أخرى بعد مكالمة الشبكة؟ سيتعين علينا تضمين اثنين من AsyncTask
مما يقلل بشكل كبير من إمكانية القراءة.
في المقابل ، قد تبدو طريقة RxJava لإجراء مكالمة شبكة على النحو التالي:
private Subscription subscription; private void onButtonClicked(Button button) { subscription = networkService.getObservableUser(123) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Action1<User>() { @Override public void call(User user) { nameTextView.setText(user.getName()); // ... set other views } }); } @Override protected void onDestroy() { if (subscription != null && !subscription.isUnsubscribed()) { subscription.unsubscribe(); } super.onDestroy(); }
باستخدام هذا الأسلوب ، نحل المشكلة (التسريبات المحتملة للذاكرة الناتجة عن مؤشر ترابط قيد التشغيل يحمل إشارة إلى السياق الخارجي) عن طريق الاحتفاظ بمرجع إلى كائن Subscription
المرتجع. ثم يتم ربط كائن Subscription
هذا #onDestroy()
الخاص بكائن Activity
/ Fragment
لضمان عدم تنفيذ عملية Action1#call
عندما يلزم تدمير Activity
/ Fragment
.
لاحظ أيضًا أن نوع الإرجاع #getObservableUser(...)
(على سبيل المثال ، يمكن ملاحظته Observable<User>
) مرتبط بمزيد من الاستدعاءات إليه. من خلال واجهة برمجة التطبيقات السائلة هذه ، يمكننا حل المشكلة الثانية المتمثلة في استخدام AsyncTask
وهو أنه يسمح بمزيد من الاتصال بالشبكة / تسلسل العمليات الطويلة. أنيق جدا ، أليس كذلك؟
دعنا نتعمق في بعض مفاهيم RxJava.
يمكن ملاحظته ومراقبته وعامل تشغيله - العناصر الثلاثة لـ RxJava Core
في عالم RxJava ، يمكن تصميم كل شيء على هيئة تدفقات. يصدر الدفق عنصرًا (عناصر) بمرور الوقت ، ويمكن استهلاك / ملاحظة كل انبعاثات.
إذا كنت تفكر في ذلك ، فإن الدفق ليس مفهومًا جديدًا: انقر فوق الأحداث يمكن أن يكون دفقًا ، ويمكن أن تكون تحديثات الموقع دفقًا ، ويمكن أن تكون الإشعارات الفورية دفقًا ، وما إلى ذلك.
يتم تنفيذ التجريد من خلال 3 بنى أساسية أحب أن أسميها "3 O's" ؛ وهي: O bservable ، و O bserver ، و O perator. المرصد يصدر العناصر (الدفق) ؛ والمراقب يستهلك هذه العناصر. يمكن أيضًا تعديل الانبعاثات الصادرة عن الكائنات المرصودة وتحويلها ومعالجتها من خلال تسلسل مكالمات المشغل .
يمكن ملاحظتها
الملاحظة هي التجريد في RxJava. إنه مشابه لـ Iterator في أنه ، نظرًا للتسلسل ، فإنه يتكرر وينتج تلك العناصر بطريقة منظمة. يمكن للمستهلك بعد ذلك استهلاك هذه العناصر من خلال نفس الواجهة ، بغض النظر عن التسلسل الأساسي.
لنفترض أننا أردنا إصدار الأرقام 1 ، 2 ، 3 بهذا الترتيب. للقيام بذلك ، يمكننا استخدام طريقة Observable<T>#create(OnSubscribe<T>)
.
Observable<Integer> observable = Observable.create(new Observable.OnSubscribe<Integer>() { @Override public void call(Subscriber<? super Integer> subscriber) { subscriber.onNext(1); subscriber.onNext(2); subscriber.onNext(3); subscriber.onCompleted(); } });
يؤدي استدعاء Subscriber.onNext subscriber.onNext(Integer)
إلى إرسال عنصر في الدفق ، وعند الانتهاء من إرسال الدفق ، يتم استدعاء subscriber.onCompleted()
.
هذا النهج لإنشاء الملاحظة هو مطوّل إلى حد ما. لهذا السبب ، توجد طرق ملائمة لإنشاء مثيلات يمكن ملاحظتها والتي يجب تفضيلها في جميع الحالات تقريبًا.
إن أبسط طريقة لإنشاء Observable هي استخدام Observable#just(...)
. كما يوحي اسم الطريقة ، فإنها ترسل فقط العنصر (العناصر) التي تمررها إليها كوسيطات أسلوب.
Observable.just(1, 2, 3); // 1, 2, 3 will be emitted, respectively
مراقب
المكون التالي للتيار المرصود هو المراقب (أو المراقبون) المشترك فيه. يتم إخطار المراقبين متى حدث شيء "مثير للاهتمام" في ساحة المشاركات. يتم إخطار المراقبين عبر الأحداث التالية:
-
Observer#onNext(T)
- يتم استدعاؤه عند انبعاث عنصر من الدفق -
Observable#onError(Throwable)
- يتم استدعاؤها عند حدوث خطأ داخل الدفق -
Observable#onCompleted()
- يتم استدعاؤه عند انتهاء الدفق من إرسال العناصر.
للاشتراك في بث ، ما عليك سوى استدعاء Observable<T>#subscribe(...)
وتمرير مثيل Observer.
Observable<Integer> observable = Observable.just(1, 2, 3); observable.subscribe(new Observer<Integer>() { @Override public void onCompleted() { Log.d("Test", "In onCompleted()"); } @Override public void onError(Throwable e) { Log.d("Test", "In onError()"); } @Override public void onNext(Integer integer) { Log.d("Test", "In onNext():" + integer); } });
سيصدر الكود أعلاه ما يلي في Logcat:
In onNext(): 1 In onNext(): 2 In onNext(): 3 In onNext(): 4 In onCompleted()
قد تكون هناك أيضًا بعض الحالات التي لم نعد مهتمين فيها بانبعاثات عنصر يمكن ملاحظته. هذا مهم بشكل خاص في Android عندما ، على سبيل المثال ، يحتاج Activity
/ Fragment
إلى استعادة في الذاكرة.
لإيقاف مراقبة العناصر ، نحتاج ببساطة إلى استدعاء Subscription#unsubscribe()
على كائن الاشتراك الذي تم إرجاعه.
Subscription subscription = someInfiniteObservable.subscribe(new Observer<Integer>() { @Override public void onCompleted() { // ... } @Override public void onError(Throwable e) { // ... } @Override public void onNext(Integer integer) { // ... } }); // Call unsubscribe when appropriate subscription.unsubscribe();
كما هو موضح في مقتطف الشفرة أعلاه ، عند الاشتراك في Observable ، فإننا نحتفظ بالإشارة إلى كائن Subscription المرتجع ثم نستدعي لاحقًا subscription#unsubscribe()
عند الضرورة. في Android ، من الأفضل استدعاء هذا في Activity#onDestroy()
أو Fragment#onDestroy()
.
المشغل أو العامل
يمكن تحويل العناصر الصادرة عن Observable وتعديلها وتصفيتها من خلال المشغلين قبل إخطار كائن (عناصر) المراقب المشترك. يمكن أيضًا تطبيق بعض العمليات الأكثر شيوعًا الموجودة في البرمجة الوظيفية (مثل الخريطة ، والتصفية ، والتقليل ، وما إلى ذلك) على دفق يمكن ملاحظته. لنلق نظرة على الخريطة كمثال:
Observable.just(1, 2, 3, 4, 5).map(new Func1<Integer, Integer>() { @Override public Integer call(Integer integer) { return integer * 3; } }).subscribe(new Observer<Integer>() { @Override public void onCompleted() { // ... } @Override public void onError(Throwable e) { // ... } @Override public void onNext(Integer integer) { // ... } });
سيأخذ مقتطف الشفرة أعلاه كل انبعاثات من المرصد ويضرب كل منها في 3 ، مما ينتج التدفق 3 ، 6 ، 9 ، 12 ، 15 ، على التوالي. عادةً ما يؤدي تطبيق عامل تشغيل إلى إرجاع "مرصود آخر" نتيجة لذلك ، وهو أمر مناسب لأن هذا يسمح لنا بربط عمليات متعددة للحصول على النتيجة المرجوة.
بالنظر إلى التدفق أعلاه ، لنفترض أننا أردنا تلقي أرقام زوجية فقط. يمكن تحقيق ذلك من خلال تسلسل عملية المرشح .
Observable.just(1, 2, 3, 4, 5).map(new Func1<Integer, Integer>() { @Override public Integer call(Integer integer) { return integer * 3; } }).filter(new Func1<Integer, Boolean>() { @Override public Boolean call(Integer integer) { return integer % 2 == 0; } }).subscribe(new Observer<Integer>() { @Override public void onCompleted() { // ... } @Override public void onError(Throwable e) { // ... } @Override public void onNext(Integer integer) { // ... } });
هناك العديد من عوامل التشغيل المضمنة في مجموعة أدوات RxJava التي تعدل الدفق المرصود ؛ إذا كنت تستطيع التفكير في طريقة لتعديل الدفق ، فمن المحتمل أن يكون هناك عامل له. على عكس معظم الوثائق الفنية ، فإن قراءة مستندات RxJava / ReactiveX بسيطة إلى حد ما ودقيقة. يأتي كل مشغل في الوثائق مصحوبًا بتصور حول كيفية تأثير المشغل على التدفق. تسمى هذه التصورات "المخططات الرخامية".
إليك كيف يمكن نمذجة عامل افتراضي يسمى Flip من خلال مخطط رخامي:
تعدد مع RxJava
يتم التحكم في الخيط الذي تحدث فيه العمليات في السلسلة التي يمكن ملاحظتها عن طريق تحديد المجدول الذي يجب أن يحدث المشغل من خلاله. بشكل أساسي ، يمكنك التفكير في المجدول على أنه تجمع مؤشرات الترابط الذي ، عند تحديده ، سيستخدمه عامل التشغيل ويعمل عليه. بشكل افتراضي ، إذا لم يتم توفير برنامج الجدولة هذا ، فستعمل السلسلة التي يمكن ملاحظتها على نفس مؤشر الترابط حيث يتم استدعاء Observable#subscribe(...)
. خلاف ذلك ، يمكن تحديد المجدول عبر Observable#subscribeOn(Scheduler)
و / أو Observable#observeOn(Scheduler)
حيث ستحدث العملية المجدولة على مؤشر ترابط يختاره المجدول.
يتمثل الاختلاف الرئيسي بين الطريقتين في أن Observable#subscribeOn(Scheduler)
يرشد المصدر المرصود إلى أي برنامج جدولة يجب أن يعمل عليه. ستستمر السلسلة في العمل على مؤشر الترابط من المجدول المحدد في Observable#subscribeOn(Scheduler)
حتى يتم إجراء استدعاء لـ Observable#observeOn(Scheduler)
باستخدام برنامج جدولة مختلف. عند إجراء مثل هذه المكالمة ، سيتلقى جميع المراقبين من هناك (أي العمليات اللاحقة أسفل السلسلة) إشعارات في سلسلة مأخوذة من برنامج observeOn
Scheduler.
فيما يلي رسم تخطيطي من الرخام يوضح كيفية تأثير هذه الطرق على مكان تشغيل العمليات:
في سياق Android ، إذا كانت هناك حاجة إلى إجراء عملية واجهة المستخدم كنتيجة لعملية طويلة ، فنحن نريد أن تتم هذه العملية في مؤشر ترابط واجهة المستخدم. لهذا الغرض ، يمكننا استخدام AndroidScheduler#mainThread()
، أحد أدوات الجدولة المتوفرة في مكتبة RxAndroid.
RxJava على Android
الآن بعد أن أصبح لدينا بعض الأساسيات تحت حزامنا ، قد تتساءل - ما هي أفضل طريقة لدمج RxJava في تطبيق Android؟ كما قد تتخيل ، هناك العديد من حالات الاستخدام لـ RxJava ، ولكن في هذا المثال ، دعنا نلقي نظرة على حالة واحدة محددة: استخدام الكائنات المرصودة كجزء من مكدس الشبكة.

في هذا المثال ، سننظر في Retrofit ، عميل HTTP مفتوح المصدر بواسطة Square والذي يحتوي على روابط مضمنة مع RxJava للتفاعل مع واجهة برمجة تطبيقات GitHub. على وجه التحديد ، سننشئ تطبيقًا بسيطًا يقدم جميع المستودعات المميزة بنجمة لمستخدم لديه اسم مستخدم GitHub. إذا كنت تريد القفز إلى الأمام ، فإن الكود المصدري متاح هنا.
قم بإنشاء مشروع Android جديد
- ابدأ بإنشاء مشروع Android جديد وتسميته GitHubRxJava .
- في شاشة أجهزة Android المستهدفة ، حافظ على تحديد الهاتف والجهاز اللوحي وقم بتعيين الحد الأدنى لمستوى SDK وهو 17. لا تتردد في تعيينه على مستوى واجهة برمجة تطبيقات أقل / أعلى ، ولكن في هذا المثال ، سيكون مستوى API 17 كافياً.
- حدد نشاط فارغ في الموجه التالي.
- في الخطوة الأخيرة ، احتفظ باسم النشاط باعتباره نشاطًا رئيسيًا وأنشئ ملف تخطيط Activity_main .
إعداد مشروع
قم بتضمين RxJava و RxAndroid ومكتبة التعديل التحديثي في app/build.gradle
. لاحظ أن تضمين RxAndroid ضمنيًا يتضمن أيضًا RxJava. ومع ذلك ، فمن الأفضل دائمًا تضمين هاتين المكتبتين بشكل صريح نظرًا لأن RxAndroid لا يحتوي دائمًا على أحدث إصدار من RxJava. يضمن تضمين أحدث إصدار من RxJava صراحة استخدام أحدث إصدار.
dependencies { compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0' compile 'com.squareup.retrofit2:converter-gson:2.1.0' compile 'com.squareup.retrofit2:retrofit:2.1.0' compile 'io.reactivex:rxandroid:1.2.0' compile 'io.reactivex:rxjava:1.1.8' // ...other dependencies }
تكوين كائن البيانات
قم بإنشاء فئة كائن بيانات GitHubRepo
. تحتوي هذه الفئة على مستودع في GitHub (تحتوي استجابة الشبكة على المزيد من البيانات ولكننا مهتمون فقط بمجموعة فرعية من ذلك).
public class GitHubRepo { public final int id; public final String name; public final String htmlUrl; public final String description; public final String language; public final int stargazersCount; public GitHubRepo(int id, String name, String htmlUrl, String description, String language, int stargazersCount) { this.id = id; this.name = name; this.htmlUrl = htmlUrl; this.description = description; this.language = language; this.stargazersCount = stargazersCount; } }
الإعداد التحديثي
- قم بإنشاء واجهة
GitHubService
. سنقوم بتمرير هذه الواجهة إلى Retrofit وسيقوم التعديل التحديثي بإنشاء تطبيقGitHubService
.
public interface GitHubService { @GET("users/{user}/starred") Observable<List<GitHubRepo>> getStarredRepositories(@Path("user") String userName); }
قم بإنشاء فئة
GitHubClient
. سيكون هذا هو الكائن الذي سنتفاعل معه لإجراء مكالمات الشبكة من مستوى واجهة المستخدم.عند إنشاء تطبيق
GitHubService
من خلال التعديل التحديثي ، نحتاج إلى تمريرRxJavaCallAdapterFactory
اتصال حتى تتمكن مكالمات الشبكة من إرجاع الكائنات القابلة للرصد (يلزم تمرير محول مكالمة لأي مكالمة شبكة تقوم بإرجاع نتيجة غيرCall
).نحتاج أيضًا إلى تمرير
GsonConverterFactory
حتى نتمكن من استخدام Gson كطريقة لتنظيم كائنات JSON إلى كائنات Java.
public class GitHubClient { private static final String GITHUB_BASE_URL = "https://api.github.com/"; private static GitHubClient instance; private GitHubService gitHubService; private GitHubClient() { final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); final Retrofit retrofit = new Retrofit.Builder().baseUrl(GITHUB_BASE_URL) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create(gson)) .build(); gitHubService = retrofit.create(GitHubService.class); } public static GitHubClient getInstance() { if (instance == null) { instance = new GitHubClient(); } return instance; } public Observable<List<GitHubRepo>> getStarredRepos(@NonNull String userName) { return gitHubService.getStarredRepositories(userName); } }
إعداد التخطيطات
بعد ذلك ، قم بإنشاء واجهة مستخدم بسيطة تعرض عمليات إعادة الشراء المستردة بإدخال اسم مستخدم GitHub. قم بإنشاء Activity_home.xml - مخطط activity_home.xml
- بشيء من هذا القبيل:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:andro android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ListView android: android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <EditText android: android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:hint="@string/username"/> <Button android: android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/search"/> </LinearLayout> </LinearLayout>
قم item_github_repo.xml
- تخطيط عنصر ListView
لكائن مستودع GitHub - بشيء مشابه لما يلي:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:andro xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="6dp"> <TextView android: android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="24sp" android:text tools:text="Cropper"/> <TextView android: android:layout_width="match_parent" android:layout_height="wrap_content" android:lines="2" android:ellipsize="end" android:textSize="16sp" android:layout_below="@+id/text_repo_name" tools:text="Android widget for cropping and rotating an image."/> <TextView android: android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/text_repo_description" android:layout_alignParentLeft="true" android:textColor="?attr/colorPrimary" android:textSize="14sp" android:text tools:text="Language: Java"/> <TextView android: android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/text_repo_description" android:layout_alignParentRight="true" android:textColor="?attr/colorAccent" android:textSize="14sp" android:text tools:text="Stars: 1953"/> </RelativeLayout>
صمغ كل شيء معًا
أنشئ ListAdapter
المسؤول عن ربط كائنات GitHubRepo
في عناصر ListView
. تتضمن العملية بشكل أساسي تضخيم item_github_repo.xml
في View
إذا لم يتم توفير View
معاد تدويرها ؛ خلاف ذلك ، يتم إعادة استخدام View
المعاد تدويرها لمنع تضخيم عدد كبير جدًا من كائنات View
.
public class GitHubRepoAdapter extends BaseAdapter { private List<GitHubRepo> gitHubRepos = new ArrayList<>(); @Override public int getCount() { return gitHubRepos.size(); } @Override public GitHubRepo getItem(int position) { if (position < 0 || position >= gitHubRepos.size()) { return null; } else { return gitHubRepos.get(position); } } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { final View view = (convertView != null ? convertView : createView(parent)); final GitHubRepoViewHolder viewHolder = (GitHubRepoViewHolder) view.getTag(); viewHolder.setGitHubRepo(getItem(position)); return view; } public void setGitHubRepos(@Nullable List<GitHubRepo> repos) { if (repos == null) { return; } gitHubRepos.clear(); gitHubRepos.addAll(repos); notifyDataSetChanged(); } private View createView(ViewGroup parent) { final LayoutInflater inflater = LayoutInflater.from(parent.getContext()); final View view = inflater.inflate(R.layout.item_github_repo, parent, false); final GitHubRepoViewHolder viewHolder = new GitHubRepoViewHolder(view); view.setTag(viewHolder); return view; } private static class GitHubRepoViewHolder { private TextView textRepoName; private TextView textRepoDescription; private TextView textLanguage; private TextView textStars; public GitHubRepoViewHolder(View view) { textRepoName = (TextView) view.findViewById(R.id.text_repo_name); textRepoDescription = (TextView) view.findViewById(R.id.text_repo_description); textLanguage = (TextView) view.findViewById(R.id.text_language); textStars = (TextView) view.findViewById(R.id.text_stars); } public void setGitHubRepo(GitHubRepo gitHubRepo) { textRepoName.setText(gitHubRepo.name); textRepoDescription.setText(gitHubRepo.description); textLanguage.setText("Language: " + gitHubRepo.language); textStars.setText("Stars: " + gitHubRepo.stargazersCount); } } }
ألصق كل شيء معًا في MainActivity
. هذا هو Activity
الذي يتم عرضه عند تشغيل التطبيق لأول مرة. هنا ، نطلب من المستخدم إدخال اسم مستخدم GitHub الخاص به ، وأخيرًا ، عرض جميع المستودعات المميزة بنجمة باسم المستخدم هذا.
public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getSimpleName(); private GitHubRepoAdapter adapter = new GitHubRepoAdapter(); private Subscription subscription; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final ListView listView = (ListView) findViewById(R.id.list_view_repos); listView.setAdapter(adapter); final EditText editTextUsername = (EditText) findViewById(R.id.edit_text_username); final Button buttonSearch = (Button) findViewById(R.id.button_search); buttonSearch.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final String username = editTextUsername.getText().toString(); if (!TextUtils.isEmpty(username)) { getStarredRepos(username); } } }); } @Override protected void onDestroy() { if (subscription != null && !subscription.isUnsubscribed()) { subscription.unsubscribe(); } super.onDestroy(); } private void getStarredRepos(String username) { subscription = GitHubClient.getInstance() .getStarredRepos(username) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<List<GitHubRepo>>() { @Override public void onCompleted() { Log.d(TAG, "In onCompleted()"); } @Override public void onError(Throwable e) { e.printStackTrace(); Log.d(TAG, "In onError()"); } @Override public void onNext(List<GitHubRepo> gitHubRepos) { Log.d(TAG, "In onNext()"); adapter.setGitHubRepos(gitHubRepos); } }); } }
قم بتشغيل التطبيق
يجب أن يقدم تشغيل التطبيق شاشة بها مربع إدخال لإدخال اسم مستخدم GitHub. يجب أن يقدم البحث بعد ذلك قائمة بجميع عمليات إعادة الشراء المميزة بنجمة.
خاتمة
آمل أن يكون هذا بمثابة مقدمة مفيدة لـ RxJava ونظرة عامة على قدراتها الأساسية. هناك الكثير من المفاهيم القوية في RxJava وأنا أحثك على استكشافها من خلال التعمق أكثر في ويكي RxJava الموثق جيدًا.
لا تتردد في ترك أي أسئلة أو تعليقات في مربع التعليقات أدناه. يمكنك أيضًا متابعتي على Twitter علىarriolachris حيث أغرد كثيرًا عن RxJava وجميع الأشياء التي تعمل بنظام Android.
إذا كنت ترغب في مصدر تعليمي شامل على RxJava ، فيمكنك الاطلاع على الكتاب الإلكتروني الذي كتبته مع Angus Huang على Leanpub.