這一篇要討論如何使用Xamarin.Android 整合GCM以及Windows Azure來實作Android手機上的推播通知服務。
這篇文章比較著重概念的部分,在開始讀這篇之前,也可以先參考一下Xamarin網站上的文章原文來了解Android GCM的運作邏輯:
Remote Notifications:An Overview of Remote Notifications in Xamarin.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。
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後,目前這個註冊的流程才被視為完成。
*官方是說這個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個接受訊息者。
* 如果當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嘗試推播資訊給一個已經移除APP的Android Device流程
當Server application嘗試推播一個訊息給一個已經無效的registration ID,GCM會回傳一個錯誤訊息"Device Not Registered"。接著Server application就可以負責在它儲存registration ID的資料庫列表中移除掉已經失效的registration ID。
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/)
4.1.2完成建立Google apis project之後,可以在網址列上看見你的Project ID。稍後會在建立Android App時用到這個Project ID。
4.1.3接著在Google apis網站上,點選左邊的Services按鈕。這時會看到中間的網頁部分List出所有的Service服務。
4.1.4在下方找到Google Cloud Messaging for Android 這個服務,然後把它開啓。
4.1.5然後點選左邊的API Access,這個步驟我們要建立一個API Key,這個Key是要給Server Application所使用的。
在這個頁面中間下方,點選下方[Create Android key]。
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執行推播訊息的動作。
4.1.7 建立完成可以看到一個Key for server apps,底下的API Key會是在你建立Server Application時所需要的必要項目。
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可以接收這些訊息。
01 |
// This will prevent other apps on the device from receiving GCM messages for this app |
02 |
// It is crucial that the package name does not start with an uppercase letter - this is forbidden by Android. |
03 |
[assembly: Permission(Name = "xamarin.sample.com.permission.C2D_MESSAGE" )] |
04 |
[assembly: UsesPermission(Name = "xamarin.sample.com.permission.C2D_MESSAGE" )] |
05 |
06 |
// Gives the app permission to register and receive messages. |
07 |
[assembly: UsesPermission(Name = "com.google.android.c2dm.permission.RECEIVE" )] |
08 |
09 |
// This permission is necessary only for Android 4.0.3 and below. |
10 |
[assembly: UsesPermission(Name = "android.permission.GET_ACCOUNTS" )] |
11 |
12 |
// Need to access the internet for GCM |
13 |
[assembly: UsesPermission(Name = "android.permission.INTERNET" )] |
14 |
15 |
// Needed to keep the processor from sleeping when a message arrives |
16 |
[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類別的範例程式:
01 |
[BroadcastReceiver(Permission= "com.google.android.c2dm.permission.SEND" )] |
02 |
[IntentFilter( new string [] { "com.google.android.c2dm.intent.RECEIVE" }, Categories = new string [] { "@PACKAGE_NAME@" })] |
03 |
[IntentFilter( new string [] { "com.google.android.c2dm.intent.REGISTRATION" }, Categories = new string [] { "@PACKAGE_NAME@" })] |
04 |
[IntentFilter( new string [] { "com.google.android.gcm.intent.RETRY" }, Categories = new string [] { "@PACKAGE_NAME@" })] |
05 |
public class MyGCMBroadcastReceiver : BroadcastReceiver |
06 |
{ |
07 |
const string TAG = "PushHandlerBroadcastReceiver" ; |
08 |
public override void OnReceive(Context context, Intent intent) |
09 |
{ |
10 |
MyIntentService.RunIntentInService(context, intent); |
11 |
SetResult(Result.Ok, null , null ); |
12 |
} |
13 |
} |
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接收到訊息?
01 |
[Service] |
02 |
public class MyIntentService : IntentService |
03 |
{ |
04 |
static PowerManager.WakeLock sWakeLock; |
05 |
static object LOCK = new object (); |
06 |
07 |
static void RunIntentInService(Context context, Intent intent) |
08 |
{ |
09 |
lock (LOCK) |
10 |
{ |
11 |
if (sWakeLock == null ) |
12 |
{ |
13 |
// This is called from BroadcastReceiver, there is no init. |
14 |
var pm = PowerManager.FromContext(context); |
15 |
sWakeLock = pm.NewWakeLock( |
16 |
WakeLockFlags.Partial, "My WakeLock Tag" ); |
17 |
} |
18 |
} |
19 |
20 |
sWakeLock.Acquire(); |
21 |
intent.SetClass(context, typeof (MyIntentService)); |
22 |
context.StartService(intent); |
23 |
} |
24 |
25 |
protected override void OnHandleIntent(Intent intent) |
26 |
{ |
27 |
try |
28 |
{ |
29 |
Context context = this .ApplicationContext; |
30 |
string action = intent.Action; |
31 |
32 |
if (action.Equals( "com.google.android.c2dm.intent.REGISTRATION" )) |
33 |
{ |
34 |
HandleRegistration(context, intent); |
35 |
} |
36 |
else if (action.Equals( "com.google.android.c2dm.intent.RECEIVE" )) |
37 |
{ |
38 |
HandleMessage(context, intent); |
39 |
} |
40 |
} |
41 |
finally |
42 |
{ |
43 |
lock (LOCK) |
44 |
{ |
45 |
//Sanity check for null as this is a public method |
46 |
if (sWakeLock != null ) |
47 |
sWakeLock.Release(); |
48 |
} |
49 |
} |
50 |
} |
51 |
} |
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。
1 |
string senders = "<google cloud=" " messaging=" " sender=" ">" ; |
2 |
Intent intent = new Intent( "com.google.android.c2dm.intent.REGISTER" ); |
3 |
intent.SetPackage( "com.google.android.gsf" ); |
4 |
intent.PutExtra( "app" , PendingIntent.GetBroadcast(context, 0, new Intent(), 0)); |
5 |
intent.PutExtra( "sender" , senders); |
6 |
context.StartService(intent);</google> |
4.3 Roles of the Application Server
在這一篇我們是用Window Azure來當作我們的Server Application。在Windows Azure上要執行的步驟有底下幾個:
1. 建立一個Mobile Service
在Windows Azure網站上按[New]的項目,然後選擇建立Mobile Service。
這邊需要自定一個Mobile Service URL 名稱。到這個步驟Windows Azure Mobile Service就建立完成。
2. 建立一個todoitem資料庫
點選到剛剛建立的Mobile Service,展開畫面中間下方的[CREATE A NEW ANDROID APP],在中間找到
[Create a table]。這邊Windows Azure會幫我們在Windows Azure上面建立一個[Todoitem]資料庫。
3. 新增你的API Key到Windows Azure
點選Windows Azure上方的PUSH項目,找到[Google Cloud messaging settings],把在Google apis裡面建立的API Key貼過來。在Windows Azure的設定到目前為止就完成了。
4. 新增Server site的Script
01 |
function insert(item, user, request) { |
02 |
request.execute({ |
03 |
success: function() { |
04 |
// Write to the response and then send the notification in the background |
05 |
request.respond(); |
06 |
push.gcm.send(item.channel, item.text, { |
07 |
success: function(response) { |
08 |
console.log( 'Push notification sent: ' , response); |
09 |
}, error: function(error) { |
10 |
console.log( 'Error sending push notification: ' , error); |
11 |
} |
12 |
}); |
13 |
} |
14 |
}); |
15 |
} |
4.4 測試Android App
要能在模擬器上測試GCM,你的模擬器必須要有支援with Google Apps的模擬器。
範例程式可從Xamarin的網站下載。
http://components.xamarin.com/view/azure-mobile-services/
這個範例需要根據Windows Azure的資料庫結構做一些微調,在我的測試中,我在Android應用程式中新增了文字之後,按下Add Item的按鈕。
可以看到Android 手機的左上角出現了一個小icon。
下拉這一個icon,可以看到GCM送來的通知項目。
這時我們回到Windows Azure的資料庫上去查詢剛剛被我們寫入的資料,可以看到多了一筆我們剛剛新增的資料。
在這邊有兩個部分比較重要,第三個complete欄位裡面是一個false值,這個欄位是用來判斷使用者到底讀取了這則訊息沒有。
第四個channel欄位裡面記載的就是Android Device向GCM註冊後,GCM回傳的registration ID。
回到Android Device點選到剛剛推播過來的訊息,這時資訊已經被讀取。接著再回到Windows Azure查詢資料庫,可以看見complete欄位的屬性被改成true。
參考文獻
Remote Notifications
Get started with push notifications in Mobile Services
http://www.windowsazure.com/en-us/develop/mobile/tutorials/get-started-with-push-android/