برمجة الأندرويد

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

<< الدرس السابق

الدرس التالى >>

فى الدرس السابق انتهينا الى جعل الشات يعمل لكن ليس بشكل فورى بل ترسل الرسالة ثم لا يستطيع الطرف الاخر معرفة انها وصلت ام لا الا عن طريق الخروج من الغرفة ثم الدخول مرة اخرى واليوم سوف نقوم بجعله فوريا وسنقوم باستخدام fcm لذلك قبل أن نبدأ قم بإعداد fcm فى جهة الأندرويد وتجهيز الملفات الرئيسية واهمها ملف FirebaseService الذى يقوم بمعالجة onMessageRecieved والتى سنتطرق اليها الان

قم بعمل تطبيق جديد على firebase console وقم باضافة الـ firebase للأندرويد ستوديو وسبق أن شرحنا هذا الامر من قبل فى  FCM بالعربية Firebase Cloud Messaging الجزء الأول و FCM بالعربية – Firebase Cloud Messaging الجزء الثانى لذلك يمكنك اتباع الشرح اذا لم تكن قادرا على تنفيذ الخطوات وحدك لانى سأكتفى الان بذكر الخطوات سريعا وباختصار

فقط قم بعمل تطبيق جديد فى firebase console ثم الحصول على ملف google-service.json واضافته للتطبيق

ثم اضافة سطور الـ gradle الازمة لتفعيل الـ google service والـ firebase cloud messaging

وطبعا نحتاج لعمل كلاسات الـ firebase لكنى سأكتفى بكلاس واحد فقط وهو كلاس استقبال الرسائل Firebase Messaging Service حيث فى الغالب نقوم بعمل service لتسجيل التوكن وسيرفس ل instance id   لكن فى هذا التطبيق لا اهتم على الاطلاق بالـ token الخاص بكل user على حدة بل سنقوم بالاعتماد على الـ topics فقط فى الاشعار اى من يدخل الغرفة كذا يأتى له الاشعارات مباشرة .

وطبعا قمنا بعمل طباعة مؤقتة لمحتوى الاشعار وسنعود لها فى وقت لاحق لكن الان نريد اضافتها فى الـمنيفست مع جعلها مختصة باستقبال الاشعارات عن طريق الـ intent filter الخاص بال fcm كالتالى :

الان نريد عند دخول كل عضو غرفة ان يقوم بالاشتراك فى topic الغرفة والـ topic هو عباره عن موضوع او مجرد نص تجعل المستخدمين يشتركون فيه وتستطيع فيما بعد اخبار fcm ان ترسل رسالة لهذا الـ topic فتقوم بارسال رسالة الاشعار لكل من قامو بعمل subscribe لهذا الـ topic وبما ان الـ roomID يميز كل غرفة عن الاخرى فسأجعل الـ topic عباره عنid الغرفة + كلمة room كالتالى :

فى onCreate الخاصة بإلـ Chat Activity

وبالتالى بمجرد أن يقوم العضو بالدخول للغرفة سيتم الاشتراك بالـ topic المسمى room12 اذا كان الغرفة 12 او room15 اذا كانت الغرفة 15 وهكذا

وعند ارسال رسالة fcm عن طريق السيرفر سيقوم الشخص باستلامها مباشرة اذا كان قد دخل للغرفة من قبل اذا لا فانه لن يستلم أى اشعارات

والان سنقوم بالتجربة نقوم بتشغيل التطبيق واختيار احد الغرف وليكن الغرفة الاولى ثم تجربة ارسال رسالة اشعار fcm وسأستخدم الـ postman للتجربة .

نقوم بوضع الـ headers نوع المرسل وكذلك الـkey الخاص بالتحقق والذى يمكنك الحصول عليه بعد انشاء التبطيق من firebase console ويمكنك استخدام اى من المفتاحين الاعلى خاص بالنظام الجديد بالـ keys والاسفل خاص بالنظام القديم

 

ثم نضع فى الـ body المحتوى الذى نريد ارساله الى سيرفر ال fcm الخاص بارسال الاشعارات واهم شىء هو to الذى نعطيه اسم topic بعد /topics لكى يقوم ال fcm بارسال الرسالة للمشتركين بهذا الtopic

اما data فهى البيانات التى سنرسلها وحاليا سأكتفى بالـ message للتجربة

نضغط على send ونراقب الـ logcat لأننا وضعنا فى الـميثود onMessageReceived داخل الكلاس FirebaseService سطرين لطباعة from وكذلك لطباعة الرسالة  وسنجد أنه تم عرضهم

عظيم الان نبدأ بكتابة الكود الذى سيقوم إما باشعار المستخدم ان كان التطبيق بالخلفية أو تحديث الشات مباشرة ان كان التطبيق ظاهرا أمام المستخدم .

لذلك سأذهب لكلاس الـ FirebaseService وهو الكلاس الوحيد الذى انشأناه لل fcm فى التطبيق الان وسنقوم بوضع كود اختبار اذا ما كان التطبيق فى الخلفية ام لا كاالتالى

هذه الميثود تفحص ان كان التطبيق مفتوحا ام فى الخلفية فى حالة كان النظام لولى بوب فما فوق  عن طريق استدعاء list الـ proccss info اى العمليات المفتوحة من قبل النظام كلها ويتم عمل loop على عملية عملية لنرى هل هناك أى عملية درجة أهميتها Forground اى الاندرويد او النظام نفسه يعطى لها اهمية كبيرة مما يدل على انها المعروضة الان امام المستخدم بعد ذلك نقوم بالدخول هذه العملية والبحث عن اسم الباكيدج الخاص بنا اذا كانت موجودة فباالتأكيد التطبيق الخاص بنا  غير موجود فى الخلفية وهو أمام المستخدم مفتوح الان وفى حالة كان النظام kitkat او اقل فانه ببساطه نقوم بعمل listمن ال runningtask info ثم نجلب التاسك الاولى والاكتيتيفى الاول بالاعلى لنرى ان كان يحتوى على اسم الباكيدج الخاصة بنا فانه يعنى ان تطبيقنا هو المفتوح امام المستخدم الان وما عدا ذلك فانه يعنى ان التطبيق فى الخلفية .

 

ايضا نقوم بكتابة الميثود sendNotification التى ستظهر اشعار للمستخدم  لكن قبل ذلك نريد جعل الـ Message الموديل الذى قمنا بعمل Parcelabe حتى نستطيع نقله بسهولة عبر الـ Intent وذلك كالتالى

وضعنا الـ room_id حتى عند الذهاب للغرفة

والان نذهب للـ onMessageReceived لنبدأ العمل

قمنا بسحب العناصر من الرسائل الواردة كاشعار

اى ان الرسالة الواردة كإشعار ستحتوى على كل هذه الأمور وسوف نقوم بعمل ذلك لاحقا فى هذه التدوينة ثم بعد ذلك قمنا بعمل Message جديدة وقمنا بملئها بالبيانات الواردة الينا والان سنقوم بالفحص اذا ما كان التطبيق فى الخلفية اما لا

اذا كان فى الخلفية فسنستخدم الميثود sendNotification ونمرر لها اوبجكت الـ Message  لنقوم باظهار الاشعار للمستخدم  ، إذا لا نقوم بعمل Intent جديد والـ Action الخاص به هو UpdateChateActivity  يمكنك تسميته اى اسم فهو مجرد اسم يميز هذا الانتنت لاننا سنستقبله فيما بعد باستخدام البرودكاست ريسيفر اضافة لانشاء انتنت جديد باكشن عباره عن String كتبناه نقوم بوضع الـ message فى الـ Extras وقمنا سابقا بعمل الـ Message كـ Parcelable وبالتالى فسيتم نقلها بشكل عادى الان نقوم بعمل sendBroadcast وفى هذا الحالة يتم ارسال برودكاست يحمل هذا الـ Intent لكى نستقبله فى اى مكان نريد فى التطبيق وفى حالتنا هذه بالتأكيد فى ChatActivity

سنقوم بالذهاب الى الـ ChatActivity لنقوم بعمل الـ Message Reciever الذى سيستقبل الرسالة وهو عباره عن برودكاست ريسيفر عادى نقوم بتفنيذ ما نريد بداخل الميثود onReceived كالعادة عند استخدام الـ Broadcast Receivers

وببساطه عند ورود رسالة لهذا البرودكاست ارسلنا بالفعل بيانات الرسالة وبالتالى نقوم باستقبالها ببساطة ونقوم باضافتها الى List الرسائل ثم اعلام الـ Adapter ان هناك رسالة تمت اضافتها فى الموضع الاخير واخيرا عمل scrollToBottom

الان لدينا البرودكاست جاهز لكنه لن يعمل ولا يعرف أنه من المفترض ان ينفذ الكود بمجرد ارسال برودكاست للانتنت السابق من FCM

لذلك لا بد من عمل register لهذا الـ receiver وببساطه سنقوم بعمل ذلك فى onResume

وبهذا اسندنا المهمة للـ reciever اى اخبرناه انه عندما يتم ارسال برودكاست الاكشن الخاص به هو UpdateChateActivity فأنت المعنى بالامر ويجب أن تنفذ الكود .

وطبعا لا ننسى أن نقوم بعمل unRegister فى حالة onPause لتقليل استهلاك الموارد وتعطيل البرودكاست ريسيفر لانه طالما الشات غير معروض فبالتأكيد لا نريد التحديث

الان نقوم بتشغيل التطبيق  والان نريد تجربة الـ FirebaseService والـ onMessageReceived التى تطلق الـ Receiver لنتأكد أن كل هذا يعمل سنقوم باختباره الان باستخدام الـ postman قبل الذهاب الى جزء الphp

عند الضغط على ارسال يتم ارسال الاشعار طالما التطبيق غير ظاهر سأقوم الان بفتح التطبيق والذهاب للغرفة الاولى صاحبة الـ id 12

وسأقوم بالضغط على send مرة اخرى من خلال الـ postman وسأجد ان الرسالة وصلت بشكل فورى وظهرت امامى شعور رائع ان كنت تقوم بعمل تطبيق شات للمرة الأولى فى حياتك

لكن ظهرت الرسالة ويكأننى من أرسلتها من الشات وهذا لان الـ id الذى ادخلته فى الـ postman هو عباره عن نفس id المسجل دخول به حاليا سأقوم بتغيره واجرب

قمت بتغيير الـ id وكذلك الاسم وعند الضغط على send نجد أن الرسالة تظهر مباشرة

ممتاز الان سنقوم بتطبيق هذا الامر على ملف add-message.php الخاص باضافة رسالة جديدة للجدول فى php لكى نستغنى عن البوست مان للأبد ويصبح الشات طبيعيا

ملف add-message.php نقوم باضافة الكود التالى اسفل الملف

 

ببساطة نقوم بجلب التاريخ الحالى بالاضافة لتنفيذ نفس ما كنا نقوم به بالـ postman لكن عن طريق php نفس الـ JSON نقوم بتكوينه لكن اعتمادا على القيم القادمة من الاندرويد وقد قمنا بشرح هذا الكلام سابقا فى شرح الـ GCM يمكنك الرجوع للتدوينة ومعرفة تفاصيل هذه الاسطر اذا لم تكن تفهمها لكن باختصار هو يعتبر برنامج يقوم بتفيذ الـ requests من داخل ملف php .

نقوم بحفظ الملف ورفعه واستبداله على الاستضافة ونقوم بتجريب الشات واذا كنت تابعت الخطوات ونفذتها بدقة فستجد أن التطبيق يعمل الان بشكل فورى جرب ان تقوم بتشغيل التطبيق على موبايلين اندرويد وان تدخل على احد الغرف وتبدأ الشات لكن سرعان ما ستدرك أن هناك مشكلة

عندما تقوم بإرسال الرسالة فانه يتم ارسال اشعار عن طريق fcm للـ topic الخاص بالغرفة والذى تكون انت مشتركا فيه بالفعل وبالتالى يتم تحديث الشات برسالتك مرة أخرى وكأنها رسالة شخص اخر فتتكرر رسالتك مرتين لذلك يجب علينا قبل أن نسمح بتمرير الرسالة وتحدثها فى الشات ان نتأكد انها رسالة من عضو اخر وليست الرسالة التى أرسلناها وذلك يمكن تحقيقها عن طريق فحص id المرسل للرسالة بداخل كلاس Firebase Service وبما أننا نستدعى الـ id الحالى للمستخدم عن طريق الـ Session التى قمنا بعملها سابقا سنستخدم الـ Session ايضا لكن لو استخدمنا getInstance بداخل كلاس الـ Firebase Service ستواجهنا مشكلة مع relam وهى باختصار انه فى relam  الثريد الذى انشأها او بدء الـكونفيج الخاص بها  هو الوحيد الذى يستطيع الوصول اليها وعمل العمليات المختلفة وبالتالى لا نستطيع عمل getInstance مباشرة لانها ستعود لنا بالـ instance الموجود مسبقا والذى غالبا تم بدءه بواسطة الـ mainThread والـ Firebase Service تعمل فى ثريد منفصل لذلك سنقوم بعمل ميثود اخرى تعود لنا بـ newInstance من الكلاس Session لذلك نقوم بكتابة الميثود التالية داخلها

لنستخدمها اثناء مقارنة الـ id الخاص بمرسل الرسالة فى كلاس الـ FirebaseService كالتالى :

 

وبالتالى عندما تأتى رسالة الى fcm قادمة من الـ topic فانه يتم فحص الـ id الخاص بالمرسل اذا كان الـ id الحالى للمستخدم فان هذا يعنى انه المرسل فلا نقوم بالتنفيذ اذا كان مرسل الرسالة شخص اخر فاننا نتابع بشكل عادى وبالتالى يعمل الشات بشكل صحيح نقوم بتشغل التطبيق الان وسنجد أن الشات يتم بطريقة فورية  لكن ستجد هناك مشكلة باخر 25 رسالة معروضين عند فتح الغرفة حيث يتم عرض اول 25 وليس اخر 25 وهذه المشكلة فى الترتيب فقط يمكننا تعديل الـ Query الخاص فى ملف get-messages.php ليأتى باخر 25 رسالة لكن سأنتظر ان يقوم صديقى مرعى بتحديث MareiDB ودعم الترتيب ثم نقوم بتعديل الكود لاحقا لكن اذا اردت فعل ذلك الان يمكنك تعديل ملف get-messages.php تحديد الـ Query الخاص بالتحديد لتجعله يقوم بتحديد اول 25 وهو تعديل بسيط لن يستغرق منك وقتا اذا اردت ان تعرض اخر 25 رسالة بشكل صحيح لكن هذا لا يهم الان .

اذا قمت بتطبيق الخطوات السابقة بشكل صحيح فاالان لديك تطبيق شات يعمل بشكل ممتاز لكن يتبقى لنا أمرين ارفاق صورة أو ارفاق اى شىء (صورة او فيديو او موقع المستخدم او مستند الخ ..) اذا اردت التوسع وموضوع اذا تم ارسال رسالة والمستخدم موجود فى صفحة غرف الشات وفى هذه الحالة تقوم بفحص اذا كان المستخدم موجود بهذه الصفحة ويمكنك تنفيذ ذلك بعدة طرق ابسطها هى وضع متغير لحالة الاكتيتفى يكون static وتقوم بعمله ture عند onStart و false عند onStop وبالتالى فى اى وقت يمكنك الوصول اليه ومعرفة هل هذا الاكتيتفى ظاهر ام لا ان كان ظاهر يمكنك وضع عداد رقمى بدائرة او ما يعرف بالـ badge او تغير شكل الغرفة او كتابة شىء فى الوصف يدل على ورود رسائل جديدة للغرفة .

 

<< الدرس السابق

الدرس التالى >>

السابق
برمجة تطبيق شات الدرس الرابع
التالي
Design Patterns بالعربية – نظرة عامة

9 تعليقات

أضف تعليقا

  1. Ali قال:

    I need more tools in that chat app ,,can you show me what to do and how??? thanks

    1. Hendiware قال:

      what’s tools you need ?

      1. ALI قال:

        I’am trying to make login by facebook , so i can get my profile image and other things , what i need is to put a small cercularimage containning my image profile beside the message

  2. Ahmed shaheen قال:

    السلام عليكم ورحمة الله وبركاته
    شكرا جزيلا على هذا الدرس الرائع … كل حاجة نفذتها والبرنامج شغال كويس جدا …
    في بس حاجة صغيرة … لما الابلكيشن يكون مقفول وعلى الرغم من وجود انترنت إلا انه مش بيستقبل الاشعارات … لازم افتحه علشان يشتغل …
    هل فى حاجة تخليه يستقبل الاشعارات زي الواتس اب … وجزاك الله خيرا

  3. Hendiware قال:

    بالفعل اذا اتبعت الشرح ونفذت ما فيه تمام فبالتأكيد يرسل اشعار عندما يكون التطبيق مغلقا .كما يظهر فى الميثود sendNotification يرجى مراجعة الكود الخاص بك .

  4. رفات قال:

    رائع جدا اشكركم جزيل الشكر والله استقدت وتعلمت منكم كثيرا ارجوكم استمروا يا ريت كل المعلمين مثلكم شكرا هنديوير شكرا شكرا

  5. Ahmed Raafat قال:

    سلام عليكم
    فى مشكله فى app انى لازم اعمل refresh لصفحه الجروب كل sec علشان اى رساله جت جديده

  6. Ahmed Raafat قال:

    السلام عليكم
    هو فى مشكله فى app
    i must refresh layout every sec to see if there is new message or no when i chat layout

  7. lin قال:

    الرجاء شرح موضوع الصورة والمستندات في تطبيق الشات باسرع وقت ممكن وشكرا لجهوكم ومواضيعكم الرائعة

اترك تعليقاً

This site uses Akismet to reduce spam. Learn how your comment data is processed.