استخدام الـ Callable مع نمط الـ Thread Pools

Mohammad Laifمنذ 3 سنوات

بسم الله الرحمن الرحيم
السلام عليكم ورحمة الله وبركاته

 

في الدرس السابق رأينا طريقة استخدام نمط الـ الـ 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 a long, which is the new rowId for the inserted item. If the parameter is an array or a collection, it should return long[] or List<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. في الدرس القادم سنقوم بإنشاء بركة كاملة من الخيوط باستخدام هذا النمط, حتى يسرع لنا الاستعلام في قواعد البيانات.

 

رابط الكلاسات المستخدمه في هذا الدرس:

 

المصادر والمراجع:

للمزيد راجع درس المقدمة.

 

نهاية الدرس
لاتنسى تتبع الدرس والدورة كذلك لإشعارك عندما يتم التعديل على المتحوى او اضافة المزيد من المعلومات. ايضاً لاتنسى الاعجاب بالدرس ومشاركته مع الاخرين.

المحاضر

Mohammad Laif

محتوى الدورة

تمهيد
1 مقدمة
2 تعرف على التزامن (الـ Concurrency)
3 اخطاء التزامن (الـ Concurrency) الشائعة
العمليات Processes
1 الهيكلة الهندسية لبيئة نظام الاندرويد
2 طبقة الـ Android Runtime و العمليات Processes
3 انواع العمليات (Processes)
الخيوط الحاسوبية Threads
1 الخيوط الحاسوبية (Threads)
2 مكونات الخيوط الحاسوبية (Thread)
3 انشاء الخيط الحاسوبي
أنماط التصميم للخيوط الحاسوبية
1 انشاء نمط الـ Handler و Looper و Thread
2 انشاء نمط الـ HandlerThreads
3 ماهو نمط الـ Thread Pools (الـ Executors)
4 استخدام نمط الـ Thread Pools كـ Singleton
5 استخدام الـ Callable مع نمط الـ Thread Pools الدرس الحالي
6 انشاء بركة خيوط حاسوبية Thread Pools لتسريع قاعدة البيانات
7 انشاء نمط الـ AsyncTask
8 انشاء نمط الـ Loader
الـ Broadcasts
1 تعرف على الـ Broadcast Receiver
2 انشاء الـ Broadcast Receiver بشكل ثابت
3 انشاء الـ Broadcast Receiver بشكل ديناميكي
4 استخدام الـ Local Broadcast للتخاطب بين المكونات
الـ Services
1 تعرف على الـ Services
2 انشاء الـ Started Service
3 انشاء الـ Intent Service
4 انشاء الـ Bound Service
5 استخدام الـ ResultReceiver للتخاطب مع الـ Intent Service
6 استخدام الـ Broadcast للتخاطب مع الـ Started Service
الـ Alarm Manager
1 تعرف على الـ Alarm Manager
2 طرق استخدام الـ Alarm Manager
الـ Jobs
1 استخدام الـ Android JobSchedualer
2 استخدام الـ Firebase JobDispatcher
3 استخدام الـ WorkManager من حزمة JetPack

الكلمات الدليلية

عن الدرس

0 إعجاب
1 متابع
0 مشاركة
1499 مشاهدات
منذ 3 سنوات

التعليقات (0)

لايوجد لديك حساب في عالم البرمجة؟

تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !