[Xamarin.Android] 結合Windows Azure與Google cloud message 來實現Push Notification

摘要:[Xamarin.Android] 結合Windows Azure 與 Google cloud message 來實現Push Notification

這一篇要討論如何使用Xamarin.Android 整合GCM以及Windows Azure來實作Android手機上的推播通知服務。

這篇文章比較著重概念的部分,在開始讀這篇之前,也可以先參考一下Xamarin網站上的文章原文來了解Android GCM的運作邏輯:

Remote Notifications:An Overview of Remote Notifications in Xamarin.Android

http://docs.xamarin.com/guides/cross-platform/application_fundamentals/notifications/android/remote_notifications_in_android

 

1. GCM Overview

GCM(Google cloud messaging )是Google提供的一個免費的 Service,這個Service功能是在handles,sending,routing,queuing從Service 端要推播給你手機的訊息。

若你的手機要能接收從GCM傳送的訊息,你的手機上必須要有安裝Google Service Framework。Google Service Framework是在你的手機上有安裝Google Play Application的時候會自動安裝起來。在你的手機開機執行的時候,Google Service Framework會Run在背景執行程序中。然後在背景程序裡面接聽GCM傳送過來的訊息。接著它會負責將訊息反序列化給註冊在App中的Intents 或是broadcast。

clip_image002

 

2. GCM Requirements

  •  4kb message limit -你要推送的訊息大小不可以超過4kb。
  •  Android 2.2-你的Android devices必須run在Android 2.2(API level 8)以上的版本。
  •  Google Play Store – Android Devices 必須有安裝Google Play Store。如果你要在模擬器上測試GCM功能的話,模擬器上就需要有安裝Google APIs。
  •  Google Account-如果你的Android Device目前run的版本低於4.0.4的話,你就需要申請一個Google Account。

3. Google Cloud Messaging in Action

3.1 App GCM註冊流程

App在一開始行時,App必須先向GCM註冊。註冊完成,GCM會回傳一個registration ID給App。

這個registration ID是你的App run在這個Android Device的唯一識別。這個App會負責傳送registration ID給Server Application。當Server Application收到了registration ID後,目前這個註冊的流程才被視為完成。

clip_image004

*官方是說這個registration ID不是常常更改,所以你不需要在每次App被啓用的時候都去Run這個註冊過程。

如果GCM要改變registration ID,GCM會在發送個通知給Android Application。

 

3.1 Server Application 推播訊息給Android Device流程

在Server Application要發送一個推播通知到Client端的時候,Server Application會把Message送給GCM,GCM會負責傳送這個訊息給Device。GCM允許Server Application一次指定1000個接受訊息者。

clip_image006

* 如果當Server嘗試要推播資訊給一個Device,而這個Device剛好是離線的狀態。(ex.關機中,收不到訊號)。

GCM會Queue著這個訊息,等到Device重新連上線時,GCM會再重新傳送這個訊息。不過GCM只會傳送近期的訊息,這個設計是為了避免一些過期的資訊或是重複性的訊息。(ex.你的Server正在推播一些限時優惠,但是使用者可能是關機的狀態,等使用者重新開機後,這個限時優惠有可能已經過期了,這時候就沒有在推播的必要。)

*如果使用者從手機上移除這個Application。但是Server application 與 GCM都還不知道這個Device上App已經被移除了。這個推播訊息會繼續被傳送到Device上。不過Google Services Framework會接收到Server Application推送過來的訊息,然後它會發現這個App已經在手機上被移除了。這時候這個Device會回送一個訊息給GCM,告知GCM這個registration ID已經無效了。

 

3.3 GCM嘗試推播資訊給一個已經移除APPAndroid Device流程

當Server application嘗試推播一個訊息給一個已經無效的registration ID,GCM會回傳一個錯誤訊息"Device Not Registered"。接著Server application就可以負責在它儲存registration ID的資料庫列表中移除掉已經失效的registration ID

clip_image008

 

4. Setting Up Google Cloud Messaging

要建立一個整合GCM功能的App,有底下三個主要步驟要執行:

1. 建立一個Google API Project – 必須建立一個Google API 專案,然後啓用CGM服務與建立所對應的API Key。

2. 開發一個Android App – 這邊可以建立或者是維護一個Android App。

3. 建立一個Server Application – 這個Server Application是負責推送訊息給Device。這個Server Application可以是任何平台來假設,例如ASP.Net,PHP…。在這一次的範例我先用Windows Azure來擔任Server Application。

所以在這個範例中主要的執行流程如下:

1. 前往Google APIs網站上啓用 Google Cloud Message服務。

2. 建立API鑰匙。

3. 在Windows Azure上建立Mobile Service。

4. 在Windows Azure上輸入CGM的API Key。

5. 在Visual Studio 2012裡面新建一個Android Application 專案。

6. 修改程式專案程式

4.1 Creating a Google API Project

4.1.1在開始Google Cloud Message前,首先要在Google apis裡面建立一個Project.

請連結底下的網址來登入Google apis:

(https://code.google.com/apis/console/)

clip_image010

 

4.1.2完成建立Google apis project之後,可以在網址列上看見你的Project ID。稍後會在建立Android App時用到這個Project ID。

clip_image011

 

4.1.3接著在Google apis網站上,點選左邊的Services按鈕。這時會看到中間的網頁部分List出所有的Service服務。

clip_image013

 

4.1.4在下方找到Google Cloud Messaging for Android 這個服務,然後把它開啓。

clip_image015

 

4.1.5然後點選左邊的API Access,這個步驟我們要建立一個API Key,這個Key是要給Server Application所使用的。

在這個頁面中間下方,點選下方[Create Android key]。

clip_image017

 

4.1.6在彈出的Configure Server Key for Xamarin Evolve GCM Example對話筐中,在Accept requests from these server IP adderss下方輸入你的GCM允許從那些IP位置來的Server Application可以傳送推播訊息。若你在這邊沒有輸入特定的IP位置,那就代表預設你允許任何IP位置來的Server Application要求這個GCM執行推播訊息的動作。

clip_image019

 

4.1.7 建立完成可以看到一個Key for server apps,底下的API Key會是在你建立Server Application時所需要的必要項目。

clip_image021

 

4.2 Create the Android Application

在建立Android App時,首先很重要的是要取得Google API Project ID,這是作為App中的Sender ID。

接下來會有三個主要的步驟要處理:

1. 權限(Permissions):一個Android application 必須要設定權限可以使用Internet以及從GCM接收訊息。

2. 廣播接受器(BroadcastReceiver:當GCM要推送一個訊息到Android Device,Device上的背景程序Google Service Framework會負責接收由GCM推送過來的訊息,接著Google Service Framework會把這個訊息在傳送給App。

在這個App裡面我們就要建立一個廣播接受器(BroadcastReceiver),來接收一個由Google Service Framework送過來的訊息。

3. IntentService : 當廣播接受器(BroadcastReceiver)並不會去對訊息做一些處理,如果要處理這些訊息,廣播接受器會啓動一個IntentService來處理接收到的訊息。

 

4.2.1. Declare Permissions

l 在Android Application裡面我們有幾個權限必須要宣告,

l android.permission.INTERNET 要與GCM互動,必須允許APP有連接網路的權限。

l android.permission.WAKE_LOCK 這是要防止在處理關於推播通知時,處理器不會進入睡眠狀態。

l com.google.android.c2dm.permission.RECEIVE-這是讓App有權限去向GCM註冊以及接收GCM要推播的訊息。

l <package_name>.permission.C2D_MESSAGE - 這個權限有兩個目的,告知我們的Device這個APP可以去接收所有C2D_Messages。也告知Device沒有其他的Application可以接收這些訊息。


// This will prevent other apps on the device from receiving GCM messages for this app
// It is crucial that the package name does not start with an uppercase letter - this is forbidden by Android.
[assembly: Permission(Name = "xamarin.sample.com.permission.C2D_MESSAGE")]
[assembly: UsesPermission(Name = "xamarin.sample.com.permission.C2D_MESSAGE")]

// Gives the app permission to register and receive messages.
[assembly: UsesPermission(Name = "com.google.android.c2dm.permission.RECEIVE")]

// This permission is necessary only for Android 4.0.3 and below.
[assembly: UsesPermission(Name = "android.permission.GET_ACCOUNTS")]

// Need to access the internet for GCM
[assembly: UsesPermission(Name = "android.permission.INTERNET")]

// Needed to keep the processor from sleeping when a message arrives
[assembly: UsesPermission(Name = "android.permission.WAKE_LOCK")]

 

 

 

4.2.2. Creating a Broadcast Receiver

接下來建立一個BroadcastReceiver類別,這個會接聽以下從GCM回傳的Intent:

com.google.android.c2dm.intent.RECEIVE – 這是接收GCM推播的訊息。

com.google.android.c2dm.intent.REGISTRATION – 這是接收前往GCM註冊相關的訊息。

com.google.android.gcm.intent.RETRY – 這是接收重新嘗試前往GCM註冊的訊息。

下方是建立BroadcastReceiver類別的範例程式:


[BroadcastReceiver(Permission= "com.google.android.c2dm.permission.SEND")]
[IntentFilter(new string[] { "com.google.android.c2dm.intent.RECEIVE" }, Categories = new string[] {"@PACKAGE_NAME@" })]
[IntentFilter(new string[] { "com.google.android.c2dm.intent.REGISTRATION" }, Categories = new string[] {"@PACKAGE_NAME@" })]
[IntentFilter(new string[] { "com.google.android.gcm.intent.RETRY" }, Categories = new string[] { "@PACKAGE_NAME@"})]
public class MyGCMBroadcastReceiver : BroadcastReceiver
{
    const string TAG = "PushHandlerBroadcastReceiver";
    public override void OnReceive(Context context, Intent intent)
    {
        MyIntentService.RunIntentInService(context, intent);
        SetResult(Result.Ok, null, null);
    }
}

 

 

4.2.3. AndroidManifest.xml裡面新增底下的權限 to support the receiver:

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

 

 

4.2.4. Creating the IntentService

BroadcastReceiver裡面不應該包括著處理通知訊息的邏輯,所以我們這邊要在撰寫一個IntentService類別,讓這個Serveice來負責處理當收到訊息時要和使用者進行的互動。

在上一個小節,在BroadcastReceiver的OnReceive事件裡面呼叫了MyIntentService.RunIntentInService(context, intent);

這個方法。可以看到這個方法是被宣告在MyIntenService類別裡面的一個靜態方法。而在下方Service類別中,裡面有一段intent.Action;宣告,表示你可以經由這個宣告來判定目前所接收到的訊息到底是由App向GCM註冊後得到回傳的訊息還是由GCM發起推播,Device接收到訊息?


[Service]
public class MyIntentService : IntentService
{
    static PowerManager.WakeLock sWakeLock;
    static object LOCK = new object();

    static void RunIntentInService(Context context, Intent intent)
    {
        lock (LOCK)
        {
            if (sWakeLock == null)
            {
                // This is called from BroadcastReceiver, there is no init.
                var pm = PowerManager.FromContext(context);
                sWakeLock = pm.NewWakeLock(
                WakeLockFlags.Partial, "My WakeLock Tag");
            }
        }

        sWakeLock.Acquire();
        intent.SetClass(context, typeof(MyIntentService));
        context.StartService(intent);
    }

    protected override void OnHandleIntent(Intent intent)
    {
        try
        {
            Context context = this.ApplicationContext;
            string action = intent.Action;

            if (action.Equals("com.google.android.c2dm.intent.REGISTRATION"))
            {
                HandleRegistration(context, intent);
            }
            else if (action.Equals("com.google.android.c2dm.intent.RECEIVE"))
            {
                HandleMessage(context, intent);
            }
        }
        finally
        {
            lock (LOCK)
            {
                //Sanity check for null as this is a public method
                if (sWakeLock != null)
                    sWakeLock.Release();
            }
        }
    }
}

 

 

 

4.2.5. Registering with Google Cloud Messaging

 

接下來就是了解Android App如何去向GCM做註冊的動作。在Android Application要去向GCM註冊前,首先Android App會傳送一個com.google.android.c2dm.intent.REGISTER Intent給GCM。

 

這個Intent會需要兩個參數值:

app – 這是一個允許Google Services Framework去向Application取得向GCM註冊必要資訊的PendingIntent。

Sender- 這是一個用逗號分隔字串陣列,裡面包含著要推播資訊給這個App的Sender IDs。

 

請參考下方的Sample code。

  


string senders = "";
Intent intent = new Intent("com.google.android.c2dm.intent.REGISTER");
intent.SetPackage("com.google.android.gsf");
intent.PutExtra("app", PendingIntent.GetBroadcast(context, 0, new Intent(), 0));
intent.PutExtra("sender", senders);
context.StartService(intent);

 

 

4.3 Roles of the Application Server

在這一篇我們是用Window Azure來當作我們的Server Application。在Windows Azure上要執行的步驟有底下幾個:

1. 建立一個Mobile Service

在Windows Azure網站上按[New]的項目,然後選擇建立Mobile Service。

clip_image022

 

 

這邊需要自定一個Mobile Service URL 名稱。到這個步驟Windows Azure Mobile Service就建立完成。

clip_image024

 

 

2. 建立一個todoitem資料庫

點選到剛剛建立的Mobile Service,展開畫面中間下方的[CREATE A NEW ANDROID APP],在中間找到

[Create a table]。這邊Windows Azure會幫我們在Windows Azure上面建立一個[Todoitem]資料庫。

clip_image026

 

3. 新增你的API KeyWindows Azure

點選Windows Azure上方的PUSH項目,找到[Google Cloud messaging settings],把在Google apis裡面建立的API Key貼過來。在Windows Azure的設定到目前為止就完成了。

clip_image028

 

clip_image030

4. 新增Server siteScript

 


function insert(item, user, request) {
    request.execute({
        success: function() {
            // Write to the response and then send the notification in the background
            request.respond();
            push.gcm.send(item.channel, item.text, {
                success: function(response) {
                    console.log('Push notification sent: ', response);
                }, error: function(error) {
                    console.log('Error sending push notification: ', error);
                }
            });
        }
    });
}

 

 

 

4.4 測試Android App

 

要能在模擬器上測試GCM,你的模擬器必須要有支援with Google Apps的模擬器。

範例程式可從Xamarin的網站下載。

 

http://components.xamarin.com/view/azure-mobile-services/

這個範例需要根據Windows Azure的資料庫結構做一些微調,在我的測試中,我在Android應用程式中新增了文字之後,按下Add Item的按鈕。

clip_image032

 

可以看到Android 手機的左上角出現了一個小icon。

clip_image034

 

下拉這一個icon,可以看到GCM送來的通知項目。

clip_image035

這時我們回到Windows Azure的資料庫上去查詢剛剛被我們寫入的資料,可以看到多了一筆我們剛剛新增的資料。

在這邊有兩個部分比較重要,第三個complete欄位裡面是一個false值,這個欄位是用來判斷使用者到底讀取了這則訊息沒有。

第四個channel欄位裡面記載的就是Android Device向GCM註冊後,GCM回傳的registration ID。

clip_image037

回到Android Device點選到剛剛推播過來的訊息,這時資訊已經被讀取。接著再回到Windows Azure查詢資料庫,可以看見complete欄位的屬性被改成true。

clip_image039

參考文獻

Remote Notifications

http://docs.xamarin.com/guides/cross-platform/application_fundamentals/notifications/android/remote_notifications_in_android

Get started with push notifications in Mobile Services

http://www.windowsazure.com/en-us/develop/mobile/tutorials/get-started-with-push-android/