XAML in Xamarin.Forms 基礎篇 電子書

特別說明

2019/05/28

Xamarin.Android 的遠端推播通知 Remote Push Notification - 使用 Azure Notification Hub

Xamarin.Android 的遠端推播通知 Remote Push Notification - 使用 Azure Notification Hub

本章節將會說明如何透過 Azure Notification Hub 服務,建立起一個具有遠端推播訊息功能的 Android App。
本教學課程示範如何使用 Azure 通知中樞將推播通知傳送至 Xamarin.Android 應用程式。 您會建立可使用 Firebase 雲端通訊 (FCM) 接收推播通知的空白 Xamarin.Android 應用程式。 您會使用通知中樞,將推播通知廣播到所有執行您的應用程式的裝置。 NotificationHubs 應用程式範例中提供完成的程式碼。
在本教學課程中,您會執行下列步驟:
  • 建立 Firebase 專案並啟用 Firebase 雲端通訊
  • 建立 Azure 通知中樞
  • 建立 Xamarin.Android 應用程式,並將其連線至通知中樞
  • 從 Azure 入口網站傳送測試通知

必要條件

  • Azure 訂用帳戶。 如果您沒有 Azure 訂用帳戶,請在開始前建立免費 Azure 帳戶。
  • Visual Studio 搭配 Xamarin (在 Windows 上) 或 Visual Studio for Mac (在 OS X 上)。
  • 有效的 Google 帳戶

建立 Firebase 專案並啟用 Firebase 雲端通訊

建立一個新的 Firebase 專案

  • 打開 Firebase 主控台 網頁,使用您的 Google 帳號,登入到 Firebase 系統內 。在登入首頁內,點選 [新增專案] 按鈕
    Firebase 主控台首頁
  • 在 [新增專案] 對話窗中的 [專案名稱] 欄位,輸入 Xamarin-Push-Notification
  • 勾選 [新增專案] 對話窗最下方的 我接受 ... 檢查盒
  • 最後,點選 [建立專案] 按鈕
    Firebase 建立專案 對話窗
  • 當此 Firebase 專案建立完成之後,點選 [繼續] 按鈕
    Firebase 建立專案完成
  • 現在,將會顯示 Xamarin-Push-Notification 這個專案頁面,找到 [首先請新增應用程式] 文字,點選該文字上方代表 Android 機器人的圖示,[將 Firebase 新增至 Android 應用程式]
    Firebase 專案首頁
  • 此時,將會顯示 [新增 Android 應用程式] 頁面,並且將會有四個步驟要進行,分別是 [註冊應用程式]、[下載設定檔]、[新增 Firebase SDK]、[執行應用程式以驗證是否安裝成功]
  • 在 [註冊應用程式] 步驟中,需要填寫底下兩個欄位資料
  • 在 [Android 套件名稱] 欄位中,輸入 com.vulcan.AzureNHub
把這個 Android 套件名稱 字串記錄下來
該 Android 套件名稱 將會於 Xamarin.Android 專案中用到,這將會用於 Xamarin.Android 的 [Xamarin.Android 專案屬性] > [Android 資訊清單] > [套件名稱] 欄位,因此,請將這個 套件名稱 記錄下來
  • 在 [應用程式暱稱 (選填)] 欄位中,輸入 Azure Notification Hub Lab
  • 點選 [註冊應用程式] 按鈕
    Firebase 新增 Android 應用程式 - 註冊應用程式
  • 在 [下載設定檔] 步驟中,請點選 [下載 google-servic.json] 按鈕,取得 google-servic.json 這個檔案,等下在 Xamarin.Android 專案內會使用的到。
請將這個 google-servic.json 妥善保存在適當的地方
google-servic.json 檔案 將會於 Xamarin.Android 專案中用到
  • 點選右下角的 [繼續] 按鈕,繼續下一個步驟
    Firebase 新增 Android 應用程式 - 下載設定檔
  • 在 [新增 Firebase SDK] 步驟中,點選右下角的 [繼續] 按鈕,繼續下一個步驟
    Firebase 新增 Android 應用程式 - 新增 Firebase SDK
  • 在 [執行應用程式以驗證是否安裝成功] 步驟中,點選右下角的 [略過此步驟] 連結,完成 新增 Android 應用程式 程序。
    Firebase 新增 Android 應用程式 - 執行應用程式以驗證是否安裝成功

Firebase 專案設定

  • 現在將會顯示出 [Xamarin-Push-Notification] 頁面,在該頁面左上方找到 [Project Overview] 文字的右方,有個齒輪圖示
  • 點選該齒輪圖示,在彈出子視窗中,點選 [專案設定]
    Firebase 專案設定
  • 此時,將會看到 [設定] 頁面的 [一般] 標籤,若剛剛忘記取得 google-servic.json 這個檔案,可以在這裡點選 [下載 google-servic.json] 按鈕,來重新取得
    Firebase 設定 - 一般
  • 點選 [Cloud Messaging] 標籤,將會看到有個 伺服器金鑰 欄位。
    Firebase 設定 - Cloud Messaging
把這個 伺服器金鑰 字串記錄下來
請把這個 伺服器金鑰 值記錄下來,等下在 Azure 通知中樞內會用到,因此,請將這個 伺服器金鑰 記錄下來

建立 Azure 通知中樞

  • 打開 Azure 入口網站 網頁,使用您的 Microsoft 帳號,登入到 Azure 系統內 。
    Azure 入口網站
  • 在 Azure 入口網站 左方,點選 [所有服務],當 所有服務 的彈出子畫面出現後,在該子畫面最上方輸入 hub ,將會得到與 hub 有關的 Azure 產品。
    此時,請選擇 [Notification Hubs] 這個項目
    Azure 所有服務
  • 現在將會顯示 [Notification Hubs] 頁面,請點選上方工具列的 [新增] 按鈕,準備新增一個 Notification Hubs 服務
    Azure Notification Hubs
  • 當顯示新 刀鋒 視窗,分別在底下欄位填入適當的欄位值
    • Notification Hub
      這裡需要填入這個通知中樞服務的名稱,請輸入 azure-notification-hub-lab
    • Create a new namespace
      在這裡填入一個通知中樞的命名空間,由於在這裡僅是做練習,因此請在此輸入 azure-notification-hub-lab ,建立一個新的通知中樞命名空間
    • 位置
      在這裡選擇 東南亞
    • Resource Group
      由於在這裡僅是做練習,在這裡點選 [新建] 連結,當顯示文字 資源群組是能夠存放 Azure 解決方案相關資源的容器。 的彈出視窗,在該彈出視窗的 名稱 欄位內,填入 azure-notification-hub-lab,完成後,點選 [確定] 按鈕。
    • 訂用帳戶
      依據登入帳號可以使用訂用帳戶,選擇合適的帳戶項目
    • Pricing tier
      由於在這裡僅是做練習,在這裡就僅選擇 Free
把這個 通知中樞服務的名稱 字串記錄下來
通知中樞服務的名稱 等下在 Xamarin.Android 專案內會用到,因此,請將這個通知中樞服務的名稱 字串記錄下來
  • 最後,在該 刀鋒 視窗下方,點選 [建立] 按鈕
    新建一個通知中樞
  • 當通知中樞建立完成之後,可以點選 [通知] 鈴鐺圖示,然後選取 [前往資源] 按鈕,或重新整理 [通知中樞] 頁面中的清單,然後選取您的通知中樞
    通知中樞完成的提示訊息
  • 當進入到剛剛建立的 [azure-notification-hub-lab] 通知中樞項目頁面,在左方的功能表清單中,選取 [Access Policies]。
    通知中樞 - Access Policies
把這個 DefaultListenSharedAccessSignature 字串記錄下來
這裡存在兩個連接字串,請記下 [DefaultListenSharedAccessSignature] 這個連結字串,因為,等下在 Xamarin.Android 專案中會使用到

設定通知中樞的 GCM 設定

  • 在剛剛的 [azure-notification-hub-lab] 通知中樞項目頁面,從左方功能表清單內,點選 [Google (GCM/FCM)] 這個項目
  • 找出剛剛在 Firebase 專案內的 伺服器金鑰 ,將這個 伺服器金鑰 值填入到 [API Key] 欄位內,然後點選 [Save] 按鈕,儲存這個設定
    通知中樞 - Google (GCM/FCM) - API Key

建立 Xamarin.Android 應用程式,並將其連線至通知中樞

建立 Xamarin.Forms 專案

請依照底下說明步驟,建立一個使用 Prism 開發框架的 Xamarin.Forms 練習專案
  • 啟動 Visual Studio 2017 應用程式
  • 點選功能表 [檔案] > [新增] > [專案]
  • 在 [新增專案] 對話窗左方,點選 [Prism] 項目
  • 在 [新增專案] 對話窗中間,點選 [Prism Blank App (Xamarin.Forms)],
  • 在 [新增專案] 對話窗下方的 [名稱] 欄位,輸入 XFAzureNHub
  • 點選 [新增專案] 對話窗右下方的 [確定] 按鈕
  • 當 [PRISM PROJECT WIZARD] 對話窗出現之後,請確定有勾選 [ANDROID] 與 [iOS] 這兩個項目,而 [UWP] 這個項目則不要勾選
  • 確定 [Container] 下拉選單欄位,選擇的是 [Unity]
  • 最後,點選 [CREATE PROJECT] 按鈕
  • 現在,請等候 Xamarin.Forms 專案建立完成

套用 Firebase 設定到 Xamarin.Android 專案

  • 展開 [XFAzureNHub.Android] 專案,使用滑鼠雙擊 [Properties] 節點
  • 點選 [Android 資訊清單] 標籤頁次,將剛剛在 Firebase 專案中設定的套件名稱,也就是 com.vulcan.AzureNHub ,填入到 [套件名稱] 欄位內。
    Xamarin.Android 的套件名稱修正
需要設定與 Firebase 專案上相同的 套件名稱
若 Xamarin.Android 專案屬性中的套件名稱與 Firebase 專案內的套件名稱兩者不一致,將會造成這個 Xamarin.Android 專案無法正常接收到來自遠端的推播通知。

安裝相關 NuGet 套件

  • 滑鼠右擊 [XFAzureNHub.Android] 專案的 [參考] 節點,選取 [管理 NuGet 套件] 選項
  • 切換到 [瀏覽] 標籤頁次
  • 搜尋 [Xamarin.GooglePlayServices.Base] 套件,安裝到 [XFAzureNHub.Android] 專案
    安裝 Xamarin.GooglePlayServices.Base 套件
  • 搜尋 [Xamarin.Firebase.Messaging] 套件,安裝到 [XFAzureNHub.Android] 專案
    安裝 Xamarin.Firebase.Messaging 套件
  • 搜尋 [Xamarin.Azure.NotificationHubs.Android] 套件,安裝到 [XFAzureNHub.Android] 專案
    安裝 Xamarin.Azure.NotificationHubs.Android 套件

新增 Google Services JSON 檔案

接下來要來處理一個 Firebase 很重要的檔案,也就是剛剛從 Google Firebase 主控台下載下來的 google-services.json 檔案
  • 找到剛剛下載下來的 google-services.json,把它拖拉到方案總管的 [XFAzureNHub.Android] 專案內
  • 點選 google-services.json 檔案,並且在 [屬性] 窗格中,將 [建置動作] 設定為 GoogleServicesJson。
    設定 google-services.json 建置動作為 GoogleServicesJson
看不到 GoogleServicesJson 選項之處置做法
如果您未看到 GoogleServicesJson 選項,請關閉 Visual Studio 再加以重新啟動,並重新開啟專案,然後重複上述步驟,就會看到了 GoogleServicesJson 選項。

開始設計 Firebase 推播通知的相關程式碼

  • 展開 [XFAzureNHub.Android] 專案內的 [Properties] 節點,打開 [AndroidManifest.xml] 檔案
    設定 google-services.json 建置動作為 GoogleServicesJson
  • 將下列 <receiver> 元素插入 <application> 元素中
 
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" android:exported="false" />
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND">
    <intent-filter>
    <action android:name="com.google.android.c2dm.intent.RECEIVE" />
    <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
    <category android:name="${applicationId}" />
    </intent-filter>
</receiver>
  • 底下是修正後的 [AndroidManifest.xml] 檔案內容
 
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.vulcan.AzureNHub" android:installLocation="auto">
    <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="27" />
    <uses-permission android:name="android.permission.INTERNET" />
    <application android:label="XFAzureNHub.Android" android:icon="@mipmap/ic_launcher">
    <receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" android:exported="false" />
    <receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND">
      <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
        <category android:name="${applicationId}" />
      </intent-filter>
    </receiver>
  </application>
</manifes>

建立 Constants.cs 類別

  • 在 [XFAzureNHub.Android] 專案內,建立一個 Constants.cs 類別,並定義類別中的下列常數值。
    其中,[ListenConnectionString] 這個常數,需要到 Azure 通知中樞中,找到 [DefaultListenSharedAccessSignature] 這個欄位值來填入進來,這個欄位值可以從 [azure-notification-hub-lab] 這個通知中樞設定頁面,在左方功能清單的最下方,找到 [Manage] > [Access Policies] 連結,點擊之後,就會在右方視窗中看到 [DefaultListenSharedAccessSignature] 欄位。
    在 Azure Notification Hub 的 DefaultListenSharedAccessSignature 值
    另外一個 [NotificationHubName] 常數,就是這個 Azure 通知中樞的名稱,也就是 [azure-notification-hub-lab]
    在 Azure Notification Hub 的該中樞的名稱
 Constants.cs
public static class Constants
{
    public const string ListenConnectionString = "請在這裡填入 Azure Notification Hub 的 DefaultListenSharedAccessSignature 值";
    public const string NotificationHubName = "這裡填入 Azure Notificaion Hub 的名稱";
    public const string ChannelName = "azure-notification-hub-lab";
    public const string CHANNEL_ID = "location_notification";
    public const string NotificationTitle = "Azure 通知中樞訊息";
}

修正 MainActivity 類別

  • 打開 [MainActivity.cs] 檔案,在最前面加入底下的命名空間參考
 MainActivity.cs
using Android.Util;
在這個 [MainActivity] 類別,加入底下的常數欄位宣告
 MainActivity.cs
public const string TAG = "MainActivity";
  • 將下列程式碼新增到 base.OnCreate(savedInstanceState) 之後的 OnCreate
 MainActivity.cs
if (Intent.Extras != null)
{
    foreach (var key in Intent.Extras.KeySet())
    {
        if(key!=null)
        {
            var value = Intent.Extras.GetString(key);
            Log.Debug(TAG, "Key: {0} Value: {1}", key, value);
        }
    }
}

建立 MyFirebaseIIDService 類別

  • 在 [XFAzureNHub.Android] 專案內,建立一個 MyFirebaseIIDService.cs 類別
  • 使用底下程式碼替換掉檔案內容
 MyFirebaseIIDService.cs
using System.Collections.Generic;

using Android.App;
using Android.Content;
using Android.Util;
using WindowsAzure.Messaging;
using Firebase.Iid;

namespace XFAzureNHub.Droid
{
    [Service]
    [IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
    public class MyFirebaseIIDService : FirebaseInstanceIdService
    {
        const string TAG = "MyFirebaseIIDService";
        NotificationHub hub;

        public override void OnTokenRefresh()
        {
            var refreshedToken = FirebaseInstanceId.Instance.Token;
            Log.Debug(TAG, "FCM token: " + refreshedToken);
            SendRegistrationToServer(refreshedToken);
        }

        void SendRegistrationToServer(string token)
        {
            // Register with Notification Hubs
            hub = new NotificationHub(Constants.NotificationHubName,
                                        Constants.ListenConnectionString, this);

            var tags = new List<string>() { };
            var regID = hub.Register(token, tags.ToArray()).RegistrationId;

            Log.Debug(TAG, $"Successful registration of ID {regID}");
        }
    }
}

建立 MyFirebaseMessagingService 類別

  • 在 [XFAzureNHub.Android] 專案內,建立一個 MyFirebaseMessagingService.cs 類別
  • 使用底下程式碼替換掉檔案內容
 MyFirebaseMessagingService.cs
using System;
using System.Linq;

using Android.App;
using Android.Content;
using Android.Media;
using Android.OS;
using Android.Support.V4.App;
using Android.Util;
using Firebase.Messaging;

namespace XFAzureNHub.Droid
{
    [Service]
    [IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
    public class MyFirebaseMessagingService : FirebaseMessagingService
    {
        const string TAG = "MyFirebaseMsgService";
        public override void OnMessageReceived(RemoteMessage message)
        {
            Log.Debug(TAG, "From: " + message.From);
            if (message.GetNotification() != null)
            {
                //These is how most messages will be received
                Log.Debug(TAG, "Notification Message Body: " + message.GetNotification().Body);
                createNotification(Constants.NotificationTitle, message.GetNotification().Body);
            }
            else
            {
                //Only used for debugging payloads sent from the Azure portal
                createNotification(Constants.NotificationTitle, message.Data.Values.First());
            }
        }

        void createNotification(string title, string desc)
        {
            var intent = new Intent(this, typeof(MainActivity));
            intent.AddFlags(ActivityFlags.ClearTop);
            var pendingIntent = PendingIntent.GetActivity(this, RandomGenerator(), intent, PendingIntentFlags.OneShot);

            NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
                .SetSmallIcon(Resource.Drawable.ic_launcher)
                .SetContentTitle(title)
                .SetContentText(desc)
                .SetSound(RingtoneManager.GetDefaultUri(RingtoneType.Notification))
                .SetAutoCancel(true)
                .SetContentIntent(pendingIntent);

            NotificationManager notificationManager;
            notificationManager = (NotificationManager)GetSystemService(Context.NotificationService);

            if (Build.VERSION.SdkInt < BuildVersionCodes.O)
            {
                notificationManager.Notify(RandomGenerator(), notificationBuilder.Build());
            }
            else
            {
                notificationBuilder.SetChannelId(Constants.CHANNEL_ID);
                var name = Constants.ChannelName;
                var description = desc;
                var channel = new NotificationChannel(Constants.CHANNEL_ID, name, NotificationImportance.Default)
                {
                    Description = description, 
                };

                notificationManager.CreateNotificationChannel(channel);
                notificationManager.Notify(RandomGenerator(), notificationBuilder.Build());
            }
        }
        private int RandomGenerator()
        {
            return new Random().Next(int.MinValue, int.MaxValue);
        }
    }
}

開始進行測試

  • 滑鼠右擊專案 [XFAzureNHub.Android],點選 [設定為起始專案] 選項
  • 在 Azure 通知中樞 (azure-notification-hub-lab) 的網頁上,點選 [Test Send] 按鈕
    Azure 通知中樞 (azure-notification-hub-lab)
  • 切換 [Platform] 欄位值為 [Android]
  • 點選下方的 [Send] 按鈕
    準備進行 Android 平台的通知測試
  • 當最下方的 [Result] 列表出現新的紀錄,則表示剛剛的通知推播已經傳送到遠端裝置上了
    推播通知傳送結果
  • 從模擬器上就可以看到這個遠端推播訊息通知
    接收到推播通知
選擇模擬器或者實體裝置
當要執行這個練習專案,並且可以接收到來自於 Azure 通知中樞的遠端推播內容,需要在目的模擬器或者實體裝置上有安裝 Google Play Service 這個軟體,否則,是無法接收到來自於遠端推播通知訊息。
因此,在 Android Device Manager 對話窗中,請針對要進行除錯的模擬器,使用滑鼠右擊該裝置項目,再彈出對話窗中,選擇 [編輯] 選項
Android Device Manager
當該模擬器裝置的屬性對話窗出現之後,在其裝置屬性對話窗的左下方,將會看到有個 [Google Play Store] 選項,請勾選它;若無法勾選這個選項,請將 處理器 這個下拉選單,選擇 x86 這個項目即可。
Android Device Manager




1 則留言:

  1. If you add splash activity for splash screen as your main launcher then push notification will not work. To get around this issue I assign splash theme to my MainActivity and change the theme to Maintheme using base.SetTheme(resid: Resource.Style.MainTheme); before base.OnCreate(savedInstanceState); call. my 2 cents.

    回覆刪除