كلما قرأت وشاهدت التقارير المتعلقة بالكوروتينات في Kotlin ، كلما أعجبت بأداة اللغة هذه. تم إصدار إصدارهم المستقر مؤخرًا في Kotlin 1.3 ، مما يعني أن الوقت قد حان لبدء الغوص وتجربة coroutines أثناء العمل باستخدام رمز RxJava الحالي كمثال. في هذا المنشور ، سنركز على كيفية تلقي طلبات الشبكة الحالية وتحويلها عن طريق استبدال RxJava بالكوروتونات.

بصراحة ، قبل أن أجرب Coroutines ، اعتقدت أنها مختلفة تمامًا عما كانت عليه في السابق. ومع ذلك ، فإن المبدأ الأساسي للكوروتين يتضمن نفس المفاهيم التي اعتدنا عليها في التدفقات التفاعلية RxJava. كمثال ، لنأخذ تكوين RxJava بسيطًا لإنشاء طلب شبكة من أحد تطبيقاتي:
- نحدد واجهة الشبكة لـ Retrofit باستخدام محول Rx ( retrofit2: adapter-rxjava2 ). ستقوم الدالات بإرجاع الكائنات من إطار عمل Rx ، مثل مفرد أو قابل للملاحظة . (يتم استخدام الوظائف هنا وليس الطرق ، حيث يُفترض أن الشفرة القديمة تمت كتابتها أيضًا في Kotlin. حسنًا ، أو تم تحويلها من Java من خلال Android Studio).
- نسمي وظيفة معينة من فئة أخرى (على سبيل المثال ، مستودع أو نشاط).
- نحدد لمؤشرات الترابط التي سيتم تنفيذها المجدول عليها وإرجاع النتيجة (الأساليب .subscribeOn () و .observeOn () ).
- نقوم بحفظ ارتباط الكائن لإلغاء الاشتراك (على سبيل المثال ، في CompositeObservable).
- اشترك في تيار الأحداث.
- إلغاء الاشتراك من الدفق اعتمادًا على أحداث دورة حياة النشاط.
هذه هي الخوارزمية الأساسية للعمل مع Rx (بدون مراعاة وظائف التعيين وتفاصيل معالجة البيانات الأخرى). أما بالنسبة للكوروتين ، فإن المبدأ لا يتغير كثيرًا. نفس المفهوم ، فقط المصطلحات تتغير.
- نحدد واجهة الشبكة من أجل التعديل التحديثي باستخدام محول للكوروتين . ستعيد الدالات الكائنات المؤجلة من واجهة برمجة تطبيقات Corutin.
- نسمي هذه الوظائف من فئة أخرى (على سبيل المثال ، مستودع أو نشاط). الفرق الوحيد: يجب وضع علامة تعليق لكل وظيفة.
- حدد المرسل الذي سيتم استخدامه للكوروتين.
- نقوم بحفظ الرابط إلى كائن المهمة لإلغاء الاشتراك.
- قم بتشغيل coroutine بأي طريقة ممكنة.
- نلغي coroutines تبعا لأحداث دورة حياة النشاط.
كما ترى من التسلسل أعلاه ، فإن عملية تنفيذ Rx و Corutin متشابهة للغاية. إذا لم نأخذ في الاعتبار تفاصيل التنفيذ ، فهذا يعني أنه يمكننا الحفاظ على النهج الذي نتبعه - فنحن نستبدل بعض الأشياء فقط لجعل تنفيذنا ملائمًا للتطبيق.

الخطوة الأولى التي يجب أن نتخذها هي السماح لـ Retrofit بإرجاع الكائنات المؤجلة . الكائنات المؤجلة هي عقود آجلة غير قابلة للحظر يمكن التراجع عنها إذا لزم الأمر. هذه الأشياء هي في الأساس مهمة coroutine ، والتي تحتوي على قيمة المهمة المقابلة. يسمح لنا استخدام النوع المؤجل بخلط نفس فكرة الوظيفة ، مع إضافة القدرة على الحصول على حالات إضافية ، مثل النجاح أو الفشل - مما يجعلها مثالية لطلبات الشبكة.
إذا كنت تستخدم Retrofit مع RxJava ، فمن المحتمل أنك تستخدم RxJava Call Adapter Factory. لحسن الحظ ، كتبت Jake Worton مكافئها لـ coroutine .
يمكننا استخدام محول المكالمات هذا في أداة إنشاء التحديثية ، ثم تنفيذ واجهة التحديث التحديثي بنفس الطريقة التي تستخدمها مع RxJava:
private fun makeService(okHttpClient: OkHttpClient): MyService { val retrofit = Retrofit.Builder() .baseUrl("some_api") .client(okHttpClient) .addCallAdapterFactory(CoroutineCallAdapterFactory()) .build() return retrofit.create(MyService::class.java) }
الآن دعونا نلقي نظرة على واجهة MyService ، والمستخدمة أعلاه. يجب علينا استبدال الأنواع التي يمكن ملاحظتها والتي تم إرجاعها بـ Defired في واجهة التعديل التحديثي. إذا كان الأمر على هذا النحو:
@GET("some_endpoint") fun getData(): Observable<List<MyData>>
الآن نستبدلها بـ:
@GET("some_endpoint") fun getData(): Deferred<List<MyData>>
في كل مرة نستدعي getData () ، سيعود لنا الكائن المؤجل - وهو تناظري لطلبات الشبكة. سابقا ، كنا نسمي هذه الوظيفة بطريقة أو بأخرى مع RxJava:
override fun getData(): Observable<List<MyData>> { return myService.getData() .map { result -> result.map { myDataMapper.mapFromRemote(it) } } }
في تيار RxJava هذا ، نسمي وظيفة الأداة المساعدة ، ثم نطبق عملية الخريطة من RxJava API مع التعيين اللاحق للبيانات التي يتم إرجاعها من الطلب إلى شيء مستخدم في طبقة واجهة المستخدم. سيتغير هذا قليلاً عندما نستخدم التنفيذ مع coroutines. بالنسبة للمبتدئين ، يجب تعليق وظيفتنا (تأجيلها) ، من أجل القيام بعملية كسولة داخل نص الوظيفة. ولهذا ، يجب أيضًا تأجيل وظيفة الاستدعاء. الوظيفة المؤجلة غير قابلة للحظر ، ويمكن التحكم فيها بعد استدعائها في البداية. يمكنك تشغيله أو إيقافه مؤقتًا أو استئنافه أو إلغاؤه.
override suspend fun getData(): List<MyData> { ... }
الآن علينا استدعاء وظيفة الأداة المساعدة. للوهلة الأولى ، نحن نفعل الشيء نفسه ، ولكن يجب أن نتذكر أنه تم تأجيلنا الآن بدلاً من الملاحظة .
override suspend fun getData(): List<MyData> { val result = myService.getData() ... }
بسبب هذا التغيير ، لم يعد بإمكاننا استخدام سلسلة تشغيل الخريطة من RxJava API. وحتى في هذه المرحلة ، البيانات غير متاحة لنا - لدينا فقط نسخة مؤجلة. الآن يجب علينا استخدام الدالة await () من أجل انتظار نتيجة الاستعلام ثم الاستمرار في تنفيذ الكود داخل الوظيفة:
override suspend fun getData(): List<MyData> { val result = myService.getData().await() ... }
في هذه المرحلة ، نحصل على الطلب المكتمل والبيانات المتاحة للاستخدام. لذلك ، يمكننا الآن إجراء عمليات رسم الخرائط:
override suspend fun getData(): List<MyData> { val result = myService.getData().await() return result.map { myDataMapper.mapFromRemote(it) } }
لقد اتخذنا واجهة التحديثية مع فئة الاستدعاء و Coroutines المستخدمة. نريد الآن استدعاء هذا الرمز من نشاطنا أو أجزاء منه واستخدام البيانات التي حصلنا عليها من الشبكة.
في نشاطنا ، سنبدأ بإنشاء رابط إلى Job ، والذي يمكننا من خلاله تعيين عملية Coroutine الخاصة بنا ثم استخدامها للتحكم ، على سبيل المثال ، في إلغاء الطلب ، أثناء مكالمة onDestroy () .
private var myJob: Job? = null override fun onDestroy() { myJob?.cancel() super.onDestroy() }
الآن يمكننا تعيين شيء لمتغير myJob. دعونا ننظر في طلبنا مع coroutines:
myJob = CoroutineScope(Dispatchers.IO).launch { val result = repo.getLeagues() withContext(Dispatchers.Main) { //do something with result } }
في هذا المنشور ، لا أرغب في الخوض في المرسلين أو تنفيذ العمليات داخل coroutines ، لأن هذا موضوع للمشاركات الأخرى. باختصار ، ما يحدث هنا:
- إنشاء مثيل CoroutineScope باستخدام IO Dispatcher كمعلمة. يتم استخدام هذا المرسل لتنفيذ عمليات حظر الإدخال / الإخراج ، مثل طلبات الشبكة.
- أطلقنا كوروتين الخاص بنا مع وظيفة الإطلاق - تقوم هذه الوظيفة بإطلاق كوروتين جديد وإرجاع ارتباط إلى متغير من نوع الوظيفة.
- ثم نستخدم الرابط إلى مستودعنا لتلقي البيانات عن طريق إجراء طلب الشبكة.
- في النهاية ، نستخدم المرسل الرئيسي للقيام بالعمل على مؤشر ترابط واجهة المستخدم. هنا يمكننا عرض البيانات المستلمة للمستخدمين.
في المنشور التالي ، يعد المؤلف بحفر أعمق قليلاً في التفاصيل ، ولكن المادة الحالية يجب أن تكون كافية لبدء دراسة coroutines.
في هذا المنشور ، استبدلنا تطبيق RxJava لاستجابات التعديل التحديثي بالكائنات المؤجلة من واجهة برمجة تطبيقات Corutin. نحن نسمي هذه الوظائف لاستقبال البيانات من الشبكة ، ثم نعرضها في نشاطنا. آمل أن تكون قد رأيت عددًا قليلًا من التغييرات التي تحتاج إلى إجرائها لبدء استخدام coroutines ، وقدرت بساطة واجهة برمجة التطبيقات ، خاصة عند قراءة التعليمات البرمجية وكتابتها.
في التعليقات على المنشور الأصلي ، وجدت طلبًا تقليديًا: إظهار الرمز بالكامل. لذلك ، قدمت تطبيقًا بسيطًا ، عند بدء التشغيل ، يتلقى جدولًا للقطارات مع Yandex. جداول مواعيد API ويعرضه في RecyclerView. الرابط: https://github.com/AndreySBer/RetrofitCoroutinesExample
أود أيضًا أن أضيف أنه يبدو أن coroutines بديلًا أدنى لـ RxJava ، لأنها لا تقدم مجموعة مكافئة من العمليات لمزامنة سلاسل العمليات. في هذا الصدد ، يجدر النظر في تنفيذ ReactiveX لـ Kotlin: RxKotlin .
إذا كنت تستخدم Android Jetpack ، فقد عثرت أيضًا على مثال مع Retrofit و coroutines و LiveData و MVVM: https://codinginfinite.com/kotlin-coroutine-call-adapter-retrofit/