انشاء نمط الـ Handler و Looper و Thread

Mohammad Laifمنذ 5 سنوات

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

 

في الدرس السابق قمنا بعمل خيوط حاسوبية باستخدام ثلاثة امثله. كان الاول باستخدام كلاس الـ Thread ثم الثاني باستخدام واجهة الـ Runnable ثم الثالث باستخدام كلاس الـ Thread بشكل سريع. في هذا الدرس سنقوم بإنشاء خيط حاسوبي بنمط الـ Handler\Looper\Thread مستخدمين بعض من مكونات الخيط الحاسوبي لإرسال Message من الخيط الرئيسي الى الخيط الذي سوف ننشئه. هذه الـ Message ستحتوي على رساله من خلالها يقوم الخيط الذي سننشئه بتشغيل الشفرة التي تتطابق مع هذه الرساله (اي اننا لن نقوم بإرسال شفره برمجية, فقط رسالة, اما الشفرة فهي مخزنه بداخل كلاس الخيط encapsulated).

 

لاحظ الصورة, سنقوم بإنشاء خيط اخر, ثم سنقوم بإرسال رسالة Message من الخيط الرئيسي الى الخيط الذي سننشئه, من خلال هذه الرسالة سيقوم الخيط الاخر بتشغيل الشفرة البرمجية التي سنقوم بكتابتها بداخله. تستطيع الاطلاع على الدرس مكونات الخيوط الحاسوبية (Thread) لفهم المزيد.

 

يعتبر هذا النمط بدائي نوعاً ما ولكن عند فهمك له ستفهم كيفية عمل الانماط الاخرى بسهولة.

 

عيوب هذا النمط

  • يحتاج انشاءه الى اسطر برمجيه كثيرة جداً.
  • ان هذا النمط ليس LifeCycle Aware لذلك اي خطئ في تطبيقه يؤدي الى مشاكل في الذاكرة Memory Leaks.

 

ماذا سننشئ

  • كلاس خيط حاسوبي خلفي بإسم MyFirstThread.
    • به حقل للـ Handler الخاص بالخيط الرئيسي, وايضاً Handler خاص به.
    • كلاس داخليه لإنشاء الـ Handler الخاص به, وسنعمل override داخلها للدالة handleMessage التي سوف تحتوي على الشفرة البرمجية المراد تشغيلها بحسب الرسالة.
    • دالة بأسم sendMessageToFirstThread لإستخدامها من الخيط الحاسوبي الرئيسي لإرسال الـ Message الى هذا الخيط.
  • كلاس الـ MainActivity.
    • سنقوم بعمل implements للواجهة Handler.Callback الذي من خلالها نستقبل النتائج من الخيط الحاسوبي الذي سننشئه عبر عمل Override للدالة handleMessage.

 

طريقة الاستخدام

النتيجة: سنقوم بإرسال رسالة Message من الخيط الحاسوبي الرئيسي تحتوي على اسم العملية, ثم سيقوم الخيط الذي انشئناه بتشغيل الشفرة البرمجية التي توافق تلك الرسالة.

 

خطوات انشاء نمط الـ Handler و Looper و Thread في الاندرويد

انشاء كلاس خيط حاسوبي مختلف عن الخيط الرئيسي

  • ننشئ كلاس جديده ولنسميها بـ MyFirstThread.
  • نجعل كلاسنا تعمل extends لكلاس الـ Thread.
  • ننشئ حقل من نوع boolean ليساعدنا على تحديد هل هذا الخيط نشط ام لا ولنسميه بـ isRunning.
  • ننشئ حقل من نوع Handler خاص للخيط الحاسوبي الرئيسي (حتى يكون حلقه وصل) ولنسميه بـ mMainThreadHandler.
  • ننشئ حقل من نوع Handler مخصص للكلاس هذه ولنسميه بـ mCustomHandler وطبعاً لإنشائه نحتاج الى انشاء كلاس داخليه له.
  • ننشئ كلاس الـ Handler المخصص للخيط.
  • نعمل Constructor خاص لهذه الكلاس يأخد Handler الخيط الرئيسي.
  • نعمل Override للداله Run ونجهز الـ Looper و الـ Handler لهذا الخيط.
  • نعمل داله public لإرسال الـ Message الى هذا الخيط من اي مكان ولنسميها بـ sendMessageToFirstThread.
  • نعمل دالة public لإطفاء هذا الخيط ولنسميها بـ quit.

 

كلاس الـ MyFirstThread والتي تعمل extends للـ Thread:

public class MyFirstThread extends Thread {

}

 

ننشئ الحقول:

private Handler mMainThreadHandler = null;
private boolean isRunning = false;
private CustomHandler mCustomHandler;

 

كلاس الـ CustomHandler:

ننشئ كلاس داخليه ونعمل extends لكلاس الـ Handler ونجعلها تاخد عنصر من نوع Looper في الـ Constructor الخاص بها:

private class CustomHandler extends Handler {
    public CustomHandler(Looper looper) {
        super(looper);
    }
}

 

نعمل Override لدالة handleMessage (هنا تضع شفراتك البرمجية المراد تشغيلها على هذا الخيط):

@Override
public void handleMessage(Message msg) {
        
}

وهكذا انتهينا من انشاء كلاس الـ Handler الخاص بنا. والان لنعد الى كلاس الخيط الحاسوبي المخصص.

 

الـ Constructor الخاص لهذا الخيط الحاسوبي (لاحظ انه يأخد Handler للخيط الرئيسي):

public MyFirstThread(Handler mainThreadHandler) {
    mMainThreadHandler = mainThreadHandler;
    isRunning = true;
}

لاحظ ايضاً اننا قمنا بجعل قيمة الحقل isRunning بـ True اي اذا تم انشاء عنصر خيط من هذه الكلاس فعلمه بانه نشط.

 

نعمل Override للداله Run (نجهز الـ Looper و الـ Handler لهذا الخيط):

@Override
public void run() {
    if (isRunning) {
        Looper.prepare(); 
        mCustomHandler = new CustomHandler(Looper.myLooper());
        Looper.loop();
    }
}

لاحظ اننا قمنا باعداد الـ Looper ثم قمنا باعداد الـ Handler واخيراً قمنا بتشغيل الـ Looper لهذه الكلاس.

 

نعمل داله public لأستقبال الـ Message في خيطنا هذا:

public void sendMessageToFirstThread(Message message) {
    while (true) {
        try {
            mCustomHandler.sendMessage(message);
            break;
        } catch (NullPointerException e) {
            Log.d(TAG, "sendMessageToFirstThread: " + e.getMessage());
            try {
                Thread.sleep(100);
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
        }
    }
}

سنقوم باستخدام هذه الداله في اي Activity نريدها على الخيط الحاسوبي الرئيسي لإرسال Message الى هذا الخيط, ومن ثم نقوم بارسالها الى الـ CustomHnadler الخاص بهذا الخيط. لاحظ قيامنا بتغليف هذه الاسطر بطريقه تجعل من الثرد ان تنتظر بعض الوقع (١٠٠ جزء من الثانيه) بسبب ان في اغلب الاحيان يوجد تأخير في التشغيل من الاندرويد للخيوط ولذلك نريد تفادي المشاكل التي تحصل بسبب ذلك.

 

نعمل دالة public لإطفاء هذا الخيط:

public void quit() {
    isRunning = false;
    mMainThreadHandler = null;
}

سنقوم باستخدام هذه الداله في اي Activity نريدها على الخيط الحاسوبي الرئيسي لإطفاء هذا الخيط.

 

اجراء التعديلات على الـ Activity الموجوده بالخيط الرئيسي حتى يمكننا التخاطب واستخدام الخيط المخصص الذي قمنا بانشائه

  • في الـ Activity بالخيط الرئيسي نعمل implement للواجهه Handler.Callback.
  • نعمل override للداله handleMessage.
  • ننشئ حقل لكلاس الخيط المخصص الذي قمنا بعمله.
  • ننشئ حقل للـ Handler للخيط الرئيسي (لاداعي لإنشاء كلاس Handler للخيط الرئيسي كما قمنا بعمله بالخيط المخصص, لإن الاندرويد عند انشاء هذا الخيط قد انشئ Handler له).
  • نجهز الـ Handler في دالة onCreate.
  • نعمل override للداله onStart نقوم بتجهيز الخيط المخصص فيها.
  • نعمل override للداله onStop نقوم فيها بانهاء الخيط المخصص فبها.
  • في زر FloatingActionButton سنقوم بارسال Message الى الخيط المخصص وعلى ذلك سيجري خيطنا المخصص العمليه الحسابيه.

 

عمل implement للواجهه Handler.Callback في الـ Activity:

public class Main2Activity extends AppCompatActivity implements Handler.Callback{

}

 

عمل override للداله handleMessage:

@Override
public boolean handleMessage(Message msg) {
    return false;
}

هنا نستلم الـ Message من الخيط المخصص الى هذا الخيط الرئيسي في الـ Activity.

 

ننشئ الحقول:

private MyFirstThread mMyFirstThread;
private Handler mMainThreadHandler = null;

 

تجهيز الـ Handler في دالة onCreate:

mMainThreadHandler = new Handler(this);

 

عمل override للداله onStart نقوم بتجهيز الخيط المخصص فيها:

@Override
protected void onStart() {
    super.onStart();
    mMyFirstThread = new MyFirstThread(mMainThreadHandler);
    mMyFirstThread.start();
}

 

عمل override للداله onStop نقوم فيها بانهاء الخيط المخصص فبها:

@Override
protected void onStop() {
    super.onStop();
    mMyFirstThread.quit();
}

 

في زر FloatingActionButton ارسال الـ Message الى الخيط المخصص وعلى حسب ذلك سيجري خيطنا الذي انشئناه العملية الحسابية المطلوبة:

Message message = Message.obtain(null, Constant.MULTIPLICATION_TASK);
mMyFirstThread.sendMessageToFirstThread(message);

 

امثله كاملة

  • تستطيع القاء نظرة كاملة على مثال يقوم بإرسال الرسالة "MULTIPLICATION_TASK" من الخيط الحاسوبي الرئيسي الى الخيط الحاسوبي الاخر في مشروع هذه الدورة وبالذات كلاس الـ Main2Activity.java.
  • ايضاً تستطيع القاء نظرة كاملة على مثال يقوم بإستقبال هذه الرسالة من الخيط الحاسوبي الرئيسي ويشغل الشفرة البرمجية التي توافق تلك الرسالة في مشروع هذه الدورة وبالذات كلاس الـ MyFirstThread.java.

 

في هذا الدرس تعرفنا على طريقة صنع خيط حاسوبي بنمط الـ Handler\Looper\Thread لإرسال رسالة من الخيط الاول الى الخيط الثاني لينفذ الشفرة البرمجية التي تتوافق مع تلك الرسالة. في الدرس القادم سنتعلم طريقة مشابهه لهذه الطريقة ولكنها اسهل واقل كتابة اسطر برمجية باستخدام نمط اسهل ويسمى بالـ HandlerThreads وسوف نقوم بإرسال Runnable يحتوي على شفرة برمجية وليس فقط Message كما هو الحال في هذا الدرس.

 

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

 

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

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

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

المحاضر

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 مشاركة
3960 مشاهدات
منذ 5 سنوات

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

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

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