[Azure][Xamarin] 推送Azure的Notification Hub訊息至Xamarin Android平台 (適用於Xamarin.Forms)

Microsoft Azure上提供了一個Notification Hub的服務,可以將訊息推送至不同平台的行動裝置之中
本篇文章會針對Notification Hub推送訊息至Xamarin的Android平台的作法作詳細的說明

要將Notification Hub的訊息送至Xamarin Android的平台上,過程步驟頗為繁瑣,但是只要照著步驟作,基本上一定就能完成推送訊息的功能

首先,先至Google APIs,建立一個新的專案

輸入專案名稱後,按下建立

接著在資料庫頁面,點選[行動服務類 API] => [Google Cloud Messaging]

在Google Cloud Messaging的頁面中,點選[啟用]

啟用之後,到[憑證]的畫面,點選[建立憑證] => [API 金鑰]

建立金鑰的類別,選擇[伺服器金鑰]

輸入金鑰名稱,並建立後,會出現API金鑰的值,請先將這組金鑰記下來,等一下在Azure的Notification Hub設定中會用到

點選右上角三個點的直槓,並在選單中點選[Project settings]

Project settings裡有一個[專案編號],請先將他記下來,等一下程式裡會用到

開啟Azure的Portal畫面,並建立一個Notification Hub

建立Notification Hub時,請把Hub的名稱與命名空間記下來,等一下也會用到

建立完成後,到Notification Hub裡,點選[Settings] => [Notification Services] => [Google (GCM)],將剛剛在Google APIs上申請到的API金鑰填入並儲存

接著,點選[Access Policies],將[PERMISSION]為[Listen]的[CONNECTION STRING]記下來,等一下程式裡會用到

到這裡,雲端的設定已經都完成了,剩下來的,就是Xamarin Android平台程式碼要作的動作了
在Xamarin.Dorid的專案中,先建立一個Services資料夾,並在裡面加入一個GcmService.cs的類別庫

在專案中,[Components]的項目中按下右鍵,並點選[Get More Components]

加入[Google Cloud Messaging Client]的元件

第二個加入[Azure Messaging]的元件

都加入完成後,會在專案中的[Components]下看到元件都已經被加入了

接著打開Services\GcmService.cs的類別庫,將下面的程式碼,加入至GcmService.cs的類別庫之中

using Gcm;
using WindowsAzure.Messaging;

[BroadcastReceiver(Permission = Constants.PERMISSION_GCM_INTENTS)]
[IntentFilter(new[] { Intent.ActionBootCompleted })]
[IntentFilter(new string[] { Constants.INTENT_FROM_GCM_MESSAGE }, Categories = new string[] { "@PACKAGE_NAME@" })]
[IntentFilter(new string[] { Constants.INTENT_FROM_GCM_REGISTRATION_CALLBACK }, Categories = new string[] { "@PACKAGE_NAME@" })]
[IntentFilter(new string[] { Constants.INTENT_FROM_GCM_LIBRARY_RETRY }, Categories = new string[] { "@PACKAGE_NAME@" })]

public class Constans : GcmBroadcastReceiverBase<GcmService>
{
    public static string[] SENDER_IDS = { "[專案編號]" };
    public const string HUB_NAME = "[Notification Hub名稱]";
    public const string HUB_LISTEN_CONN = "[包含Listen權限的Notification Hub連線字串]";
}

[Service]
public class GcmService : GcmServiceBase
{
    static NotificationHub hub;
    static string Tag;

    public static void Register(Context Context)
    {
        GcmClient.Register(Context, Constans.SENDER_IDS);
    }

    public static void Unregister(Context Context)
    {
        GcmClient.UnRegister(Context);
    }

    public GcmService() : base(Constans.SENDER_IDS)
    {
    }

    protected override void OnRegistered(Context context, string registrationId)
    {
        hub = new NotificationHub(Constans.HUB_NAME, Constans.HUB_LISTEN_CONN, context);

        if (hub != null)
        {

            try
            {
                hub.UnregisterAll(registrationId);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            // 若是有用到tags,請在這裡加入client端的tags字串
            var tags = new List<string>() { };

            try
            {
                var hubRegistration = hub.Register(registrationId, tags.ToArray());
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }

    protected override void OnUnRegistered(Context context, string registrationId)
    {
        if (hub != null)
            hub.Unregister();
    }

    protected override void OnMessage(Context context, Intent intent)
    {
        Console.WriteLine("Received Notification");

        if (intent != null || intent.Extras != null)
        {

            var keyset = intent.Extras.KeySet();

            foreach (var key in intent.Extras.KeySet())
            {
                string strMessage = intent.Extras.GetString(key);
                Console.WriteLine("Key: {0}, Value: {1}", key, strMessage);
            }
        }

        var msg = new StringBuilder();
        string messageText = intent.Extras.GetString("message");
        if (!string.IsNullOrEmpty(messageText))
        {
            createNotification("New hub message!", messageText);
        }
        else
        {
            createNotification("Unknown message details", msg.ToString());
        }
    }

    protected override bool OnRecoverableError(Context context, string errorId)
    {
        return true;
    }

    protected override void OnError(Context context, string errorId)
    {

    }

    void createNotification(string title, string desc)
    {
        var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
        var uiIntent = new Intent(this, typeof(MainActivity));
        var notification = new Notification(Android.Resource.Drawable.SymActionEmail, title);

        notification.Flags = NotificationFlags.AutoCancel;
        notification.SetLatestEventInfo(this, title, desc, PendingIntent.GetActivity(this, 0, uiIntent, 0));

        notificationManager.Notify(1, notification);
        dialogNotify(title, desc);
    }

    protected void dialogNotify(String title, String message)
    {

        MainActivity.instance.RunOnUiThread(() => {
            AlertDialog.Builder dlg = new AlertDialog.Builder(MainActivity.instance);
            AlertDialog alert = dlg.Create();
            alert.SetTitle(title);
            alert.SetButton("Ok", delegate {
                alert.Dismiss();
            });
            alert.SetMessage(message);
            alert.Show();
        });
    }
}

這段程式碼主要是提供了接收訊息推播的功能服務,請將前面步驟所記下的[專案編號]、[Notification Hub名稱]與[連線字串],更新至Constans的類別中,其他程式碼就先不要去動它

主要的程序如下
OnRegistered的內容,會從GCM中取得這台行動裝置的[registrationId],並將這個[registrationId]註冊進Notification Hub裡
OnMessage會將接收到的訊息,透過顯示alert的方式,呈現在App的畫面中

接著,在根目錄中的MainActivity.cs中,加入下面的內容

using Gcm.Client;
using XamarinNotification.Dorid.Services;

public static MainActivity instance = null;

private void CreateGCM()
{
    try
    {
        // Check to ensure everything's setup right
        GcmClient.CheckDevice(this);
        GcmClient.CheckManifest(this);

        // Register for push notifications
        System.Diagnostics.Debug.WriteLine("Registering...");
        GcmClient.Register(this, Constans.SENDER_IDS);
    }
    catch (Java.Net.MalformedURLException)
    {
        CreateAndShowDialog("There was an error creating the client. Verify the URL.", "Error");
    }
    catch (Exception e)
    {
        CreateAndShowDialog(e.Message, "Error");
    }
}

private void CreateAndShowDialog(String message, String title)
{
    AlertDialog.Builder builder = new AlertDialog.Builder(this);

    builder.SetMessage(message);
    builder.SetTitle(title);
    builder.Create().Show();
}

這段程式碼主要是當App啟動時,向GCM進行註冊的動作,以便接收推播訊息

接著在MainActitivy.cs的OnCreate裡,加上這兩行

instance = this;
this.CreateGCM();

最後,將Xamarin.Dorid的專案屬性中,[Packages name]的欄位,改為小寫開頭

不將Packages name改為小寫開頭,會在實際部署至手機或是模擬器時,出現剖析器發生錯誤的問題而無法安裝,所以Packages name請一定要改為小寫開頭

到這裡,程式碼的部份也完成了,把程式碼編譯完,佈署至實體手機執行看看

請不要部署到模擬器,因為訊息推播會透過Google APIs作接收,模擬器上一般來說都不會安裝Google APIs,若是嘗試安裝到模擬器執行的話,會出現無法執行的錯誤

要發送訊息進行測試的方式,進入到Azure Portal的Notification Hub裡,點選[Test Send]

在[Test Send]的畫面中,選擇推播至Android的平台後,點選[Send],就可以推送訊息作測試了

若是推送成功,會出現passed的通知

手機上也收到剛剛傳送出來的測試訊息了

本文章範例程式已上傳GitHub,連結網址如下:
https://github.com/madukapai/maduka-Xamarin

參考資料
開始使用適用於 Android 應用程式的通知中樞