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

GCM بالـعربية – الدرس الأول

فى الدرس السابق GCM بالـعربية كانت عبارة عن مقدمة للـ GCM ولانشغال صديقنا حلمى وعدم استكماله للشرح سوف أقوم بمتابعة الشرح استجابة لطلبات اصدقاءنا الذين طلبوا الموضوع وسيقوم صديقنا حلمى بمتابعة الشرح بالفيديو لاحقا عندما يتوفر لديه وقت .

 

قبل أن تدرس هذا الموضوع اذا لم يكن لديك خلفية جيدة عن الـ Android WebServices ننصحك بشدة بدراسة  سلسلة Android WebServices بالعربية  أولا .

 

اتفقنا اننا سنتعلم كيفية عمل pushNotification  ولاتمام ذلك  سنحتاج لإنشاء تطبيق على الـ Developer Console الخاصة بجوجل  ثم سنقوم بانشاء ثلاثة ملفات جافا Registration Service للحصول على توكن وتسجيل الـ token الخاص بالمستخدم فى قاعدة البيانات الخاصة بنا أون لاين و ملف  InstanceID Listener لمراقبة التغير فى الـ token واتخاذ اجراء عند تحديث التوكن حيث يمكن أن يتم تحديث التوكن لأسباب امنية او لانتهاء مدة صلاحيته أو اى سبب اخر  وملف GCMListener والذى سيستقبل الرسائل القادمة  من الـ GCM وتنفيذ اكشن معين والذى يكون فى الغالب اظهار notification للمستخدم  وسيتم شرح كل شىء بالتفصيل ان شاء الله واى اسئلة سيتم الرد عليها فهيا بنا نبدأ .

 

إنشاء تطبيق على الـ Developer Console

يمكن ذلك بطريقتين عن طريق الـ Conosol نفسها من الداخل او عن طريق هذا الرابط الذى يسهل عليك الامر وهو ما سنقوم به الان

بعد الدخول للرابط ستجد هذه الصفحة قم بادخال اسم التطبيق واسم الباكيدج الخاصة بك ثم تضغط على الزر

 

نقوم بعمل Enable للـ GCM على التطبيق

 

بعد الضغط على Enable  ستجد أنه تم إنشاء الـ API Key والـ Sender ID

سوف نستخدم الـ API مع php وسنستخدم الـ Sender ID مع الأندرويد كما سترى لاحقا لكن الان قم بنسخهم والاحتفاظ بهم  فى ملف او كومنت فى تطبيقك وبعدها يمكنك اغلاق الصفحة فهذا فقط ما نحتاجه من الـ Google Developer Console

 

إضافة الـ GCM للأندرويد ستوديو

سوف نقوم بالذهاب لأعدادت المشروع File->Project Structure  ثم تقوم بعمل صح على الـ Notification ثم أوك وسيقوم تلقائيا باضافة الـ GCM للمشروع

الان نبدأ بكتابة الملفات التى اتفقنا اننا سنقوم بإنشاءها وهى الـ Registration Services ، InstanceID Listener ، GCM Listener

كلاس الـ RegistrationService 

سوف نبدأ بـ Registration Services لأنها اول شىء يتم تنفيذه فى العملية سنقوم بإنشاء كلاس جديد يرث من الـ IntentService كالتالى :

 

ومهمة هذه الكلاس ستكون الحصول على token خاص بهذا الـ Device وإرساله الى السيرفر الخاص بنا وكل ذلك يتم فى الخلفية فى سيرفس  وان لم يسبق لك التعامل مع الـ Service من قبل فان الService هى أحد مكونات الأندرويد مثل الأكتيتفى لكن ليس لها واجهه تربط بها وتعمل فى الخلفية ويمكن ان تستمر بعد اغلاق التطبيق تعمل فى الخلفية وهنا قمنا بعمل extends من الـ IntentService لان الـ Service العادية تعمل ضمن الـ MainThread وبما اننا سنستخدم الانترنت فنحن نحتاج الى تنفيذ الامر فى ثريد منفصل وهذا ما توفره لنا الـ IntentService حيث تنفذ الكود فى ثريد منفصل بعيدا عن الـ MainThread وإن شاء الله سيكون هناك تدوينة عن الـ Service لاحقا فى موقعنا .

 

وبمجرد عمل extend للـ IntentService سوف يجبرك على عمل Override للـ onHandleIntent  وهى الميثود التى تنفذ بمجرد بدء الـ Service عن طريق startService كما سترى لاحقا . بالاضافة للـ Constructor الذى يأخذ اسم افتراضى وهو اسم الثريد .

 

الان سنبدأ بكتابة كود جلب الـ token وهو ببساطه عباره عن مفتاح او رقم سرى طويل خاص بهذا الجهاز يعرفه gcm حتى اذا ما اردنا ارسال Notification عبر الـ Gcm فانه يرسل الى الـ tokens ففى بعض الحالات سترسل notifications الى اشخاص واشخاص اخرين لا واحيانا تود ارسال notification مثل الفيس بوك الى مستخدم واحد فبالتالى الـ token او الرقم السرى هذا الذى تولده جوجل لكل جهاز على حدة هو ما يميز كل جهاز عن الاخر .

 

سوف نقوم بإنشاء اوبجكت جديد من الـ InstanceID وهو الكلاس المساعد الذى سيساعدنا على الحصول على token وهنا قمنا بعمل getInstance وليس new Instance وهو ما يعرف بالـ Singleton Pattern اى تستخدم نسخة واحده من الكلاس بحيث لو استدعيته اكثر من مرة فانك تستدعى نسخة واحده من الكلاس ولا تقوم بعمل new فى كل مرة .

 

public class RegistrationService extends IntentService{

    public RegistrationService(String name) {
        super(name);
    }

    @Override
    protected void onHandleIntent(Intent intent) {

        InstanceID instanceID = InstanceID.getInstance(this);
        
    }
}

 

الان سوف نستخدم الميثود getToken للحصول  على token  لهذا الجهاز من جوجل GCM وهذه الميثود تأخذ ثلاث باراميترات الاول هو الـ Sender ID او رقم البروجكت الذى انشأناه عبر Google Console

والباراميتر الثانى هو عباره عن المدى اى اننا حددنا هنا اننا نريد هذا التوكن من اجل Google Cloud Messaging  والمتغير الثالث هو الـ bundle اعطيناه null وبرغم ان نفس الميثود قد تكتفى باثنين باراميترز الا انها مع بعض الاشخاص يحدث خطأ احيانا لذلك نستخدم التى تأخذ ثلاث باراميترز ونعطى الاخير null .

public class RegistrationService extends IntentService{

    public RegistrationService(String name) {
        super(name);
    }

    @Override
    protected void onHandleIntent(Intent intent) {

        InstanceID instanceID = InstanceID.getInstance(this);
        String token = instanceID.getToken("580967115884", GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);

    }
}

 

وستجد أنه يطالبك بعمل try catch  لاحتمال حدوث بعض الاخطاء اثناء محاولة الحصول على توكن لا بأس قم بعمل try catch

 

الان عندما تبدأ هذه السيرفس بالعمل سنحصل على الـ token وسيكون مخزن فى المتغير String token و يجب علينا عمل امرين

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

والثانى :  هو  تخزين قيمة فى الـ SharedPreference بأننا حصلنا على token وبالتالى عند فتح التطبيق كل مرة نقوم بفحص ذلك وبالتالى اذا تم الحصول على توكن وارساله من قبل فلا حاجه لارسال التوكن للسيرفر  مرة أخرى حيث يكون التوكن الخاص بهذا الجهاز مخزن فى السيرفر الخاص بنا أون لاين ويمكن ارسال الـ Notification له بكل سهولة عبر GCM  .

 

سوف اقوم بالذهاب الى الـ phpmyadmin وإنشاء جدول جديد اسمه users_token  بداخله عمود id وعمود tokens

 

 

الان سنقوم بإنشاء ملف php لادخال القيم الى الجدول يتم الاتصال به مباشرة بعد الحصول على الـ token من الـموبايل وسأسميهaddnewtoken.php

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

قم بتحميلها من هنا DB  فك الضغط وقم بتعديل بيانات الاتصال بقاعدة البيانات

 

ثم قم بملىء البيانات الخاصة بالسيرفر واسم المستخدم واسم قاعدة البيانات والباسورد ثم احفظها

 

نقوم بإنشاء ملف addnewtoken.php

<?php
include 'DB.php';
$db = DB::getInstance();
$result = $db->insert('users_tokens',['tokens'=> $_POST['token'] ]);
if($result)
    echo 1;
else
    echo 0;

ما هذا ؟

السطر الاول : وسم فتح ملف الـ php وتعريف المستند

السطر الثانى : تضمين الكلاس DB لكى نستطيع استخدام الـ Methods منه ولمن ليس لديه خبره فى php  تشبه عندنا import فى الجافا

السطر الثالث : نستدعى الـميثود getInstance() والتى تعنى اننا انشأنا اوبجكت جديد من الكلاس وقمنا بامساكه بالمتغير $db  .

السطر الرابع : قمنا باستخدام الميثود insert الموجوده بكلاس الـ DB واعطينها بارامترين الاول هو عباره عن اسم الجدول users_tokens والثانى هو عباره عن اسم العمود والقيمة على هيئة php array حيث tokens اسم العمود و $_POST[‘tokens’] هى القيمة التى سنرسلها لهذا الملف بالطريقة POST واسمها token

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

 

وسنقوم الان بكتابة ميثود سوف ترسل الـ token من الأندرويد بعد الحصول عليه وسأستخدم مكتبة Volley والتى تم  شرحها فى هذه التدوينة من سلسلة Android WebService بالعربية

سأقوم بإنشاء ميثود اسمها sendTokenToServer داخل كلاس الـ Registeration Service

    private void sendTokenToServer(final String token) {
        String ADD_TOKEN_URL = "http://developerhendy.16mb.com/addnewtoken.php";
        StringRequest request = new StringRequest(Request.Method.POST, ADD_TOKEN_URL, new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                int responseCode = Integer.parseInt(response);
                if (responseCode == 1) {
                    prefEditor.putBoolean("token_sent", true).apply();
                    Log.e("Registration Service", "Response : Send Token Success");

                } else {
                    prefEditor.putBoolean("token_sent", false).apply();
                    Log.e("Registration Service", "Response : Send Token Failed");


                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                prefEditor.putBoolean("token_sent", false).apply();
                Log.e("Registration Service", "Error :Send Token Failed");

            }
        }) {
            @Override
            protected Map<String, String> getParams() throws AuthFailureError {
                Map<String, String> params = new HashMap<>();
                params.put("token", token);
                return params;

            }
        };

        Volley.newRequestQueue(this).add(request);

    }

وهذه الميثود تقوم بكل بساطة بأخذ الـ token وإرساله الى السيرفر وذلك باستخدام Volley  يمكنك استخدام HttpUrlConnection او Retrofit او اى مكتبة اخرى ان اردت  وتقوم بحفظ حالة ارسال التوكن فى الشيرد بريفرنسس لكى نقوم بتفحص الكود لاحقا اذا لم يكن تم الارسال فيتم تشغيل السيرفس مرة اخرى لجلب توكن وارساله الى السيرفر .

 

وسأستخدم هذه الميثود بداخل onHandleIntent لتصبح الكلاس Registeration Service  بالكامل كالتالى :

RegistrationService.java

package com.hendiware.hellogcm;

import android.app.IntentService;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;

import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import com.google.android.gms.gcm.GoogleCloudMessaging;
import com.google.android.gms.iid.InstanceID;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class RegistrationService extends IntentService {
    SharedPreferences preferences;
    SharedPreferences.Editor prefEditor;

    public RegistrationService() {
        super("RegistrationService");
    }


    @Override
    protected void onHandleIntent(Intent intent) {
        preferences = PreferenceManager.getDefaultSharedPreferences(this);
        prefEditor = preferences.edit();

        InstanceID instanceID = InstanceID.getInstance(this);
        try {
            String token = instanceID.getToken("580967115884", GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
            if (!preferences.getBoolean("token_sent", false))
                sendTokenToServer(token);
        } catch (IOException e) {
            e.printStackTrace();
            Log.e("Registration Service", "Error :get Token Failed !");

        }

    }


    private void sendTokenToServer(final String token) {
        String ADD_TOKEN_URL = "http://developerhendy.16mb.com/addnewtoken.php";
        StringRequest request = new StringRequest(Request.Method.POST, ADD_TOKEN_URL, new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                int responseCode = Integer.parseInt(response);
                if (responseCode == 1) {
                    prefEditor.putBoolean("token_sent", true).apply();
                    Log.e("Registration Service", "Response : Send Token Success");

                } else {
                    prefEditor.putBoolean("token_sent", false).apply();
                    Log.e("Registration Service", "Response : Send Token Failed");


                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                prefEditor.putBoolean("token_sent", false).apply();
                Log.e("Registration Service", "Error :Send Token Failed");

            }
        }) {
            @Override
            protected Map<String, String> getParams() throws AuthFailureError {
                Map<String, String> params = new HashMap<>();
                params.put("token", token);
                return params;

            }
        };

        Volley.newRequestQueue(this).add(request);

    }
}

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

 

سنضيف السيرفس وصلاحيات الانترنت لملف الـ Manifest

AndroidManifest.java

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.hendiware.hellogcm">

    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name=".RegistrationService" />
    </application>

</manifest>

 

نضيف كود بدء الـ Service للـ MainActivity

 

MainActivity.java

package com.hendiware.hellogcm;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        startService(new Intent(this, RegistrationService.class));
    }
}

قم بتشغيل التطبيق وراقب الـ Logcat  واذا قمت بعمل الخطوات السابقة بشكل صحيح فستجد عبارة token sent success

وللتأكد قم بالذهاب الى phpmyAdmin  وتصفح الجدول user_tokens وستجد ان التوكن تم ارساله للسيرفر .

 

 

الى هنا تنتهى التدوينة وسنستكمل فى الدرس الثانى ان شاء الله .

 

السابق
مكتبة Butter Knife والوصول للمكونات بشكل أسرع
التالي
كلاس Marei DB للتعامل مع قواعد بيانات MySQL بسهولة

12 تعليق

أضف تعليقا

  1. Ahmed Ali قال:

    والله انا بجد عاجز عن الشكر
    وفي انتظار باقي الدروس 🙂

  2. علي قال:

    جزاكم الله خير
    دائما رائعين بالشرح المبسط
    كل الاحترام

  3. محمد جمال قال:

    مشكووور جدا يا هندي
    في انتظار الدرس القادم في اسرع وقت ممكن ان امكن

  4. عبد الرحمن قال:

    في قايمة ال projectstructure مش ظاهر ال developer service دى ؟

  5. Roro قال:

    يعطيك العافية ..

    InstanceID instanceID= InstanceID.getInstance(this);
    try {
    String token= instanceID.getToken(“255853467510”, GoogleCloudMessaging.INSTANCE_ID_SCOPE,null);
    if (!preferences.getBoolean(“token_sent”, false));
    sendTokenToServer(token);
    } catch (Exception e) {
    e.printStackTrace();
    Log.e(“Registration Service”, “Error :get Token Failed !”);
    }
    لماذا هذه الجزئية بالتحديد تعطيني java.lang.NumberFormatException: Invalid int: “0” ؟؟
    ما الحل اذا سمحت

  6. على قال:

    و اخيرا فضيت لتطبيق الدرس
    و تم اتمامه بنجاح
    و كل هذا بفضلكم
    كل الشكر لكم
    في المرة القادمة ساقوم بتطبيق الدرس الثاني

  7. Gemi قال:

    دايما بيدخل ف token failure…..؟

    private void sendTokenToServer(final String tokens) {

    String URL = “http://www.developergemy.16mb.com/addnewtoken.php”;
    StringRequest request = new StringRequest(Request.Method.POST, URL, new Response.Listener() {
    @Override
    public void onResponse(String response) {
    int requestCode = Integer.parseInt(response);
    if (requestCode == 1) {
    editor.putBoolean(“token_sent”, true).apply();
    Log.e(“Registration Service”, ” Token : sent successfully”);
    } else {
    editor.putBoolean(“token_sent”, false).apply();
    Log.e(“Registration Service”, ” Token : failure”);
    }
    }
    }, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
    editor.putBoolean(“token_sent”, false).apply();
    Log.e(“Registration Service”, ” Token : send Failure”);
    }
    }) {
    @Override
    protected Map getParams() throws AuthFailureError {

    Map hash = new HashMap();
    hash.put(“token”, tokens);
    return hash;
    }
    };

    Volley.newRequestQueue(this).add(request);
    }

    , وده ملف ال php

    query($insertuser)===TRUE)
    {
    echo 1;}
    else
    {
    echo 0 ;}
    ?>

    وملف connect بيحوي على معلومات للسيرفر و قاعده البيانات و المستخدم و الباسورد

  8. Gemi قال:

    addnewtoken.php

    query($insertuser)===TRUE)
    {
    echo 1;}
    else
    {
    echo 0 ;}
    ?>

    1. omar osama قال:

      لو انت عامل id فى الداتا بيز خليه auto increment

  9. ِahmed قال:

    عاوز code DB.php

  10. مؤمن اسليم قال:

    اخي العزيز ، يعطيك الف عافية
    شرح متميز جدا ..
    ولكن لدي استفسار هل يوجد صيغة كتابة لتلك الرسائل ..
    يعني هل يوجد مواصفات لاغراء الزوار للضغط على رسائل الاشعارات ، و ما هي
    اتمنى الرد بأي شكل .. وشكرا
    دمتم بخير …

  11. Muhammed Amer قال:

    شرح رائع …ربنا يجازيك خير

اترك تعليقاً

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