استخدام الـ Callable مع نمط الـ Thread Pools
بسم الله الرحمن الرحيم
السلام عليكم ورحمة الله وبركاته
في الدرس السابق رأينا طريقة استخدام نمط الـ الـ Thread Pools (الـ Executors) جنباً الى جنب مع نمط الـ Singleton لإعادة استخدامه في اكثر من مره مع قاعدة البيانات الـ Room في كلاس الـ Repository. في هذا الدرس سنرى طريقة استخدامه مع المنادى (الـ Callable) والكائنات المستقبلية (الـ Future). ربما تريد الاطلاع على الدرس التالي: مكونات الخيوط الحاسوبية (Thread).
قبل اكمال القرائه في هذا الدرس:
هذا الدرس قائم على استخدام قاعدة البيانات الـ Room والعديد من انماط التصميم الـ MVVM و Repository لذلك ستحتاج معرفه بسيطه بهم لفهم هذا الدرس. وبالمناسبة لقد قمت بكتابة دورة تتعلق بهذة الاشياء كلها تستطيع الاطلاع عليها في هذا الرابط: Android Architecture Components (دروس لتعلم Android Architecture Components من حزمة Android JetPack بلغة الجافا لتصميم تطبيقات الاندرويد).
لاحظ الصورة فالـ Executor من هذا النوع وهو الـ newSingleThreadExecutor نستطيع تخيله على انه مشابه تماماً للنمط العادي, الاختلاف فقط اننا قمنا بإرسال Callable الذي هو عباره عن كائن مستقبلي. على كل حال في الدرس القادم عندما نستخدم النوع newFixedThreadPool لإنشاء بركة من الخيوط ستتغير وظائف كثيرة بداخل الـ Executor فسيكون مسؤل عن استلام الشفرات البرمجية سواء غلفت بـ Runnable او Callable ثم توزيعها على الخيوط الموجودة في البركة.
استخدام الـ ThreadPools مع Callable لإرجاع قيم مستقبليه Future
في بعض الاحيان نريد ارجاع قيمه بعد انتهاء الشفرة البرمجية المرسله على الخيط الحاسوبي من عملها. ولذلك نستخدم الـ Callable فهو قادر على ارجاع تلك القيمه لنا (بعكس الـ Runnable). فبعض الدوال في قواعد البيانات تتيح ارجاع بيانات بعد اتمام عملها, كمثل دالة الـ Insert فهي تتيح ارجاع قيمة الحقل الجديد. ولذلك سوف نستخدم Callable لإرجاع تلك القيمه بعد عمل Insert لحقل جديد في قاعدة البيانات, كما جاء في الـ Docs الخاص بالـ Android SDK:
If the
@Insert
method receives only 1 parameter, it can return along
, which is the newrowId
for the inserted item. If the parameter is an array or a collection, it should returnlong[]
orList<Long>
instead.
الخطوات:
نجهز عنصرنا من Long:
Long insertedColumnId = -1L;
عنصر Long عادي حتى في المستقبل نسند له قيمة الحقل الذي تم عمل له insert في قاعدة البيانات.
ننشئ الـ Executor وذلك بأستخدام الكلاس المساعده ExecutorService:
ExecutorService executorService = Executors.newSingleThreadExecutor();
قمنا بإنشائه هذه المره بإستخدام واجهة ExecutorService ثم قمنا بعمل init له بإستخدام الكلاس Executors الذي توفرة الـ Android SDK لنا, ربما تود قرائة الدرس ماهو نمط الـ Thread Pools (الـ Executors).
ننشئ عنصر الـ Callable يحتوي على الشفرة المراد تشغيلها ونحدد له نوع الارجاع من خلال الجواهر <> كالتالي:
Callable<Long> callable = new Callable<Long>() {
@Override
public Long call() throws Exception {
return mNameDao.insertAndReturnColumnId(nameEntity);
}
};
مشابه تماماً للـ Runnable فهو يحتوي على دالة call وذلك يحتوي على دالة run الفرق هو ان دالة call تستطيع ارجاع قيم مستقبليه. ومن الملاحظ اننا قمنا بجعل هذا الـ Callable يرجع نوع Long بإستخدام الجواهر <Long>.
اذا اردنا ارسال الـ Callable الى الـ Executor (ارساله الى خيط حاسوبي) ولكنا لن نفعل هذه الخطوة:
executorService.submit(callable);
ننشئ عنصر مستقبلي Future من نوع Long حتى يستقبل البيانات المرجعه من قبل الـ Callable. وفي نفس الوقت نقوم بإرساله على الخيط الحاسوبي:
Future<Long> future = executorService.submit(callable);
اسناد مايتم ارجاعه من الـ Callable المرسل الى العنصر المستقبلي.
واخيراً نستطيع الحصول على القيمه للحقل المنشئ في قاعدة البيانات من خلال عنصر الـ Future هكذا:
try {
insertedColumnId = future.get(1, TimeUnit.SECONDS);
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
لاحظ استخدمنا دالة get و حددناها بوقت وهو ثانيه واحده! لماذا؟ ذلك بسبب ان هذه الداله تقوم بحجب هذا الخيط الى ان ترجع النتيجه. تخيل ماذا سيحدث لو لم ترجع النتيجه او حدثة مشكلة في شفرة الـ Callable.
الدالة كاملة في كلاس الـ Repository:
public Long insertRetrieveColumnId(final NameEntity nameEntity) {
// Prepare column id
Long insertedColumnId = -1L;
// Create the Callable object that carry our code to the executor
Callable<Long> callable = new Callable<Long>() {
@Override
public Long call() throws Exception {
return mNameDao.insertAndReturnColumnId(nameEntity);
}
};
// Create an executor
ExecutorService executorService = Executors.newSingleThreadExecutor();
// Create a Future object
Future<Long> future = executorService.submit(callable);
// Retrieve the result into our future object, within 1 second or cancel it.
// .get() -> Blocks the Thread, so we should put a max duration for it.
// We can use future.isDone() to know if the task is done or not before using future.get().
try {
insertedColumnId = future.get(1, TimeUnit.SECONDS);
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
// Return the column id
return insertedColumnId;
}
في هذا الدرس تعرفنا على طريقة استخدام نمط الـ Thread Pools (الـ Executors) مع المنادى (الـ Callable) والكائنات المستقبلية (الـ Future) لإرجاع قيم من دالة الـ Insert لقاعدة البيانات الـ Room. في الدرس القادم سنقوم بإنشاء بركة كاملة من الخيوط باستخدام هذا النمط, حتى يسرع لنا الاستعلام في قواعد البيانات.
رابط الكلاسات المستخدمه في هذا الدرس:
- تم استخدام في كلاس NameRepository.java.
المصادر والمراجع:
- Callable | Android Developers.
- Future | Android Developers.
- راجع ايضاً درس ماهو نمط الـ Thread Pools (الـ Executors).
للمزيد راجع درس المقدمة.
نهاية الدرس
لاتنسى تتبع الدرس والدورة كذلك لإشعارك عندما يتم التعديل على المتحوى او اضافة المزيد من المعلومات. ايضاً لاتنسى الاعجاب بالدرس ومشاركته مع الاخرين.
محتوى الدورة
عن الدرس
0 إعجاب |
1 متابع |
0 مشاركة |
1987 مشاهدات |
منذ 5 سنوات |
التعليقات (0)
لايوجد لديك حساب في عالم البرمجة؟
تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !