使用 Firebase Cloud Messaging 发送远程通知

本演练逐步详解了如何使用 Firebase Cloud Messaging 在 Xamarin.Android 应用程序中实现远程通知(也称为推送通知)。 它说明了如何实现与 Firebase Cloud Messaging (FCM) 通信所需的各种类,提供了如何配置 Android 清单以访问 FCM 的示例,并演示了使用 Firebase 控制台的下游消息传递。

FCM 通知概述

在本演练中,将创建一个名为 FCMClient 的基础应用,以阐述 FCM 消息传送的基本知识。 FCMClient 检查 Google Play Services 是否存在,从 FCM 接收注册令牌,显示从 Firebase 控制台发送的远程通知,以及订阅主题消息:

应用的屏幕截图示例

将探讨以下主题领域:

  1. 后台通知

  2. 主题消息

  3. 前台通知

在本演练中,会逐步将功能添加到 FCMClient 并在设备或模拟器上运行,以了解它如何与 FCM 交互。 你将使用日志记录来见证 FCM 服务器的实时应用事务,并观察如何从输入到 Firebase 控制台通知 GUI 的 FCM 消息生成通知。

要求

最好是能够熟悉 Firebase Cloud Messaging 可以发送的不同类型的消息。 消息的有效负载将确定客户端应用如何接收和处理消息。

在继续本演练之前,必须获取必要的凭据才能使用 Google 的 FCM 服务器;Firebase Cloud Messaging 中解释了此过程。 具体而言,必须下载 google-services.json 文件,才能与本演练中提供的示例代码一起使用。 如果尚未在 Firebase 控制台中创建项目(或者尚未下载 google-services.json 文件),请参阅 Firebase Cloud Messaging

若要运行示例应用,需要与 Firebase 兼容的 Android 测试设备或模拟器。 Firebase Cloud Messaging 支持在 Android 4.0 或更高版本上运行的客户端,并且这些设备还必须安装 Google Play Store 应用(需要 Google Play Services 9.2.1 或更高版本)。 如果设备上尚未安装 Google Play Store 应用,请访问 Google Play 网站下载并安装。 或者,可以将 Android SDK 模拟器与已安装的 Google Play Services 配合使用,而不是测试设备(如果使用 Android SDK 模拟器,则无需安装 Google Play Store)。

启动应用项目

首先,创建一个名为 FCMClient 的新的空 Xamarin.Android 项目。 如果不熟悉创建 Xamarin.Android 项目,请参阅你好,Android。 创建新应用后,下一步是设置包名称并安装(将用于与 FCM 通信的)多个 NuGet 包。

设置包名称

Firebase Cloud Messaging 中,为已启用 FCM 的应用指定了包名称。 此包名称还充当与 API 密钥关联的应用程序 ID。 将应用配置为使用此包名称:

  1. 打开 FCMClient 项目的属性。

  2. 在“Android 清单”页中,设置包名称。

在以下示例中,包名称设置为 com.xamarin.fcmexample

设置包名称

在更新“Android 清单”时,还应检查以确保启用 Internet 权限。

重要

如果此包名称与在 Firebase 控制台中输入的包名称不完全匹配,则客户端应用将无法从 FCM 接收注册令牌。

添加 Xamarin Google Play Services 基础包

由于 Firebase Cloud Messaging 依赖于 Google Play Services,因此必须将 Xamarin Google Play Services - 基础 NuGet 包添加到 Xamarin.Android 项目。 需要版本 29.0.0.2 或更高版本。

  1. 在 Visual Studio 中,右键单击“引用 > 管理 NuGet 包...”。

  2. 单击“浏览”选项卡,搜索 Xamarin.GooglePlayServices.Base

  3. 将此包安装到 FCMClient 项目中:

    安装 Google Play Services 基础映像

如果在安装 NuGet 期间收到错误,请关闭 FCMClient 项目,再次打开它,然后重试 NuGet 安装。

安装 Xamarin.GooglePlayServices.Base 时,还会安装所有必要的依赖项。 编辑 MainActivity.cs 并添加以下 using 语句:

using Android.Gms.Common;

此语句使 Xamarin.GooglePlayServices.Base 中的 GoogleApiAvailability 类可用于 FCMClient 代码。 GoogleApiAvailability 用于检查 Google Play Services 是否存在。

添加 Xamarin Firebase Messaging 包

若要从 FCM 接收消息,必须将 Xamarin Firebase - Messaging NuGet 包添加到应用项目中。 如果没有此包,Android 应用程序将无法从 FCM 服务器接收消息。

  1. 在 Visual Studio 中,右键单击“引用 > 管理 NuGet 包...”。

  2. 搜索 Xamarin.Firebase.Messaging

  3. 将此包安装到 FCMClient 项目中:

    安装 Xamarin Firebase Messaging

安装 Xamarin.Firebase.Messaging 时,还会安装所有必要的依赖项。

接下来,编辑 MainActivity.cs 并添加以下 using 语句:

using Firebase.Messaging;
using Firebase.Iid;
using Android.Util;

前两个语句使 Xamarin.Firebase.Messaging NuGet 包中的类型可供 FCMClient 代码使用。 Android.Util 添加了日志记录功能,用于观察 FMS 的事务。

添加 Google Services JSON 文件

下一步是将 google-services.json 文件添加到项目的根目录:

  1. google-services.json 复制到项目文件夹。

  2. google-services.json 添加到应用项目(单击“解决方案资源管理器”中的“显示所有文件”,右键单击 google-services.json,然后选择“包括在项目中”)。

  3. 在“解决方案资源管理器”窗口中选择“google-services.json”。

  4. 在“属性”窗格中,将“生成操作”设置为“GoogleServicesJson”:

    将“生成操作”设置为“GoogleServicesJson”

    注意

    如果未显示 GoogleServicesJson 生成操作,请保存并关闭解决方案,然后重新打开它。

google-services.json 添加到项目(并且设置了 GoogleServicesJson 生成操作)时,生成过程将提取客户端 ID 和 API 密钥,然后将这些凭据添加到 obj/Debug/android/AndroidManifest.xml 中合并/生成的 AndroidManifest.xml。 此合并过程会自动添加连接到 FCM 服务器所需的任何权限和其他 FCM 元素。

检查 Google Play Services 并创建通知通道

Google 建议 Android 应用在访问 Google Play Services 功能之前检查 Google Play Services APK 是否存在(有关详细信息,请参阅检查 Google Play Services)。

将首先创建应用 UI 的初始布局。 编辑 Resources/layout/Main.axml,并将其内容替换为以下 XML:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="10dp">
    <TextView
        android:text=" "
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/msgText"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:padding="10dp" />
</LinearLayout>

TextView 用于显示指示是否安装了 Google Play Services 的消息。 将更改保存到 Main.axml

编辑 MainActivity.cs 并将以下实例变量添加到 MainActivity 类:

public class MainActivity : AppCompatActivity
{
    static readonly string TAG = "MainActivity";

    internal static readonly string CHANNEL_ID = "my_notification_channel";
    internal static readonly int NOTIFICATION_ID = 100;

    TextView msgText;

变量 CHANNEL_IDNOTIFICATION_ID 将用于本演练稍后将添加到 MainActivity 的方法 CreateNotificationChannel 中。

在以下示例中,OnCreate 方法将在应用尝试使用 FCM 服务之前验证 Google Play Services 是否可用。 将下列方法添加到 MainActivity 类:

public bool IsPlayServicesAvailable ()
{
    int resultCode = GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable (this);
    if (resultCode != ConnectionResult.Success)
    {
        if (GoogleApiAvailability.Instance.IsUserResolvableError (resultCode))
            msgText.Text = GoogleApiAvailability.Instance.GetErrorString (resultCode);
        else
        {
            msgText.Text = "This device is not supported";
            Finish ();
        }
        return false;
    }
    else
    {
        msgText.Text = "Google Play Services is available.";
        return true;
    }
}

此代码会检查设备,查看是否安装了 Google Play Services APK。 如果未安装,则会在指示用户从 Google Play Store 下载 APK(或在设备的系统设置中启用 APK)的 TextBox 中显示一则消息。

在 Android 8.0(API 级别 26)或更高版本上运行的应用必须创建一个通知通道用于发布通知。 将以下方法添加到 MainActivity 类,此类将创建通知通道(如有必要):

void CreateNotificationChannel()
{
    if (Build.VERSION.SdkInt < BuildVersionCodes.O)
    {
        // Notification channels are new in API 26 (and not a part of the
        // support library). There is no need to create a notification
        // channel on older versions of Android.
        return;
    }

    var channel = new NotificationChannel(CHANNEL_ID,
                                          "FCM Notifications",
                                          NotificationImportance.Default)
                  {

                      Description = "Firebase Cloud Messages appear in this channel"
                  };

    var notificationManager = (NotificationManager)GetSystemService(Android.Content.Context.NotificationService);
    notificationManager.CreateNotificationChannel(channel);
}

OnCreate 方法替换为以下代码:

protected override void OnCreate (Bundle bundle)
{
    base.OnCreate (bundle);
    SetContentView (Resource.Layout.Main);
    msgText = FindViewById<TextView> (Resource.Id.msgText);

    IsPlayServicesAvailable ();

    CreateNotificationChannel();
}

IsPlayServicesAvailableOnCreate 的末尾调用,以便每次启动应用时运行 Google Play Services 检查。 将调用方法 CreateNotificationChannel,来确保运行 Android 8 或更高版本的设备存在通知通道。 如果你的应用有 OnResume 方法,它也应从 OnResume 调用 IsPlayServicesAvailable。 完全重新生成并运行应用。 如果全部配置正确,应会看到类似于以下屏幕截图的屏幕:

应用指示 Google Play Services 可用

如果未收到此结果,请验证是否已在设备上安装 Google Play Services APK(有关详细信息,请参阅设置 Google Play Services)。 另请验证是否已将 Xamarin.Google.Play.Services.Base 包添加到 FCMClient 项目,如前所述。

添加实例 ID 接收方

下一步是添加一个服务,用于将 FirebaseInstanceIdService 扩展为处理 Firebase 注册令牌的创建、轮换和更新。 FCM 需要 FirebaseInstanceIdService 服务才能将消息发送到设备。 将 FirebaseInstanceIdService 服务添加到客户端应用时,应用将自动接收 FCM 消息,并在应用处于后台时将其显示为通知。

在 Android 清单中声明接收方

编辑 AndroidManifest.xml 并将以下 <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>

此 XML 执行以下操作:

  • 声明一个 FirebaseInstanceIdReceiver 实现,该实现为每个应用实例提供唯一标识符。 此接收方还会对操作进行身份验证和授权。

  • 声明用于安全地启动服务的内部 FirebaseInstanceIdInternalReceiver 实现。

  • 应用 ID 存储在已添加到项目google-services.json 文件中。 Xamarin.Android Firebase 绑定会将令牌 ${applicationId} 替换为应用 ID;客户端应用不需要其他代码来提供应用 ID。

FirebaseInstanceIdReceiver 是一个 WakefulBroadcastReceiver,接收 FirebaseInstanceIdFirebaseMessaging 事件并将其传递到派生自 FirebaseInstanceIdService 的类。

实现 Firebase 实例 ID 服务

向 FCM 注册应用程序的工作由你提供的自定义 FirebaseInstanceIdService 服务处理。 FirebaseInstanceIdService 执行下列步骤:

  1. 使用实例 ID API 生成安全令牌,以授权客户端应用访问 FCM 和应用服务器。 反过来,应用会从 FCM 获取注册令牌

  2. 如果应用服务器需要注册令牌,则将注册令牌转发到应用服务器。

添加名为 MyFirebaseIIDService.cs 的新文件,并将其模板代码替换为以下内容:

using System;
using Android.App;
using Firebase.Iid;
using Android.Util;

namespace FCMClient
{
    [Service]
    [IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
    public class MyFirebaseIIDService : FirebaseInstanceIdService
    {
        const string TAG = "MyFirebaseIIDService";
        public override void OnTokenRefresh()
        {
            var refreshedToken = FirebaseInstanceId.Instance.Token;
            Log.Debug(TAG, "Refreshed token: " + refreshedToken);
            SendRegistrationToServer(refreshedToken);
        }
        void SendRegistrationToServer(string token)
        {
            // Add custom implementation, as needed.
        }
    }
}

此服务实现在最初创建或更改注册令牌时调用的 OnTokenRefresh 方法。 OnTokenRefresh 运行时,它将从 FirebaseInstanceId.Instance.Token 属性(由 FCM 异步更新)中检索最新的令牌。 在此示例中,将记录刷新的令牌,以便可以在输出窗口中查看它:

var refreshedToken = FirebaseInstanceId.Instance.Token;
Log.Debug(TAG, "Refreshed token: " + refreshedToken);

OnTokenRefresh 不常调用:在以下情况下,它用于更新令牌:

  • 安装或卸载应用时。

  • 用户删除应用数据时。

  • 应用擦除实例 ID 时。

  • 令牌的安全遭到损害时。

根据 Google 的实例 ID 文档,FCM 实例 ID 服务将请求应用定期刷新其令牌(通常每 6 个月刷新一次)。

OnTokenRefresh 还调用 SendRegistrationToAppServer 将用户的注册令牌与应用程序维护的服务器端帐户(如果有)相关联:

void SendRegistrationToAppServer (string token)
{
    // Add custom implementation here as needed.
}

由于此实现取决于应用服务器的设计,因此此示例中提供了一个空的方法正文。 如果应用服务器需要 FCM 注册信息,请修改 SendRegistrationToAppServer 以将用户的 FCM 实例 ID 令牌与应用维护的任何服务器端帐户相关联。 (请注意,令牌对客户端应用不透明。)

将令牌发送到应用服务器时,SendRegistrationToAppServer 应保留一个布尔值,以指示令牌是否已发送到服务器。 如果此布尔值为 false,SendRegistrationToAppServer 会向应用服务器发送令牌,否则说明令牌已在上一次调用中发送到应用服务器。 在某些情况下(如此 FCMClient 示例),应用服务器不需要令牌;因此,此示例不需要此方法。

实现客户端应用代码

现在,接收方服务已准备就绪,可以编写客户端应用代码来利用这些服务。 在以下部分中,会向 UI 添加一个按钮来记录注册令牌(也称为实例 ID 令牌),并在从通知中启动应用时向 MainActivity 添加更多代码来查看 Intent 信息:

已添加到应用屏幕的“日志令牌”按钮

记录令牌

此步骤中添加的代码仅用于演示目的,生产客户端应用无需记录注册令牌。 编辑 Resources/layout/Main.axml,并在 TextView 元素后面立即添加以下 Button 声明:

<Button
  android:id="@+id/logTokenButton"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_gravity="center_horizontal"
  android:text="Log Token" />

将以下代码添加到 MainActivity.OnCreate 方法的末尾:

var logTokenButton = FindViewById<Button>(Resource.Id.logTokenButton);
logTokenButton.Click += delegate {
    Log.Debug(TAG, "InstanceID token: " + FirebaseInstanceId.Instance.Token);
};

当点击“记录令牌”按钮时,此代码会将当前令牌记录到输出窗口。

处理通知意图

当用户点击从 FCMClient 发出的通知时,通知消息随附的任何数据都在 Intent 附加内容中提供。 编辑 MainActivity.cs 并将以下代码添加到 OnCreate 方法顶部(调用 IsPlayServicesAvailable 之前):

if (Intent.Extras != null)
{
    foreach (var key in Intent.Extras.KeySet())
    {
        var value = Intent.Extras.GetString(key);
        Log.Debug(TAG, "Key: {0} Value: {1}", key, value);
    }
}

当用户点击通知消息时,将触发应用的启动器 Intent,因此此代码会将 Intent 中的任何随附数据记录到输出窗口。 如果必须触发其他 Intent,则通知消息的 click_action 字段必须设置为该 Intent(未指定 click_action 时使用启动器 Intent)。

后台通知

生成并运行 FCMClient 应用。 将显示“记录令牌”按钮:

系统会显示“记录令牌”按钮

点击“记录令牌”按钮。 IDE 输出窗口中应显示如下所示的消息:

“输出”窗口中显示的实例 ID 令牌

标有令牌的长字符串是要粘贴到 Firebase 控制台中的实例 ID 令牌,请选择此字符串并将其复制到剪贴板。 如果未看到实例 ID 令牌,请将以下行添加到 OnCreate 方法顶部,以验证是否已正确分析 google-services.json

Log.Debug(TAG, "google app id: " + GetString(Resource.String.google_app_id));

记录到输出窗口的 google_app_id 值应与 google-services.json 中记录的 mobilesdk_app_id 值匹配。 处理 google-services.json 时由 msbuild 生成 Resource.String.google_app_id

发送邮件

登录到 Firebase 控制台,选择项目,单击“通知”,然后单击“发送第一条消息”:

“发送第一条消息”按钮

在“编写消息”页上,输入消息文本并选择“单个设备”。 从 IDE 输出窗口中复制实例 ID 令牌,并将其粘贴到 Firebase 控制台的“FCM 注册令牌”字段中:

撰写消息对话框

在 Android 设备(或模拟器)上,通过点击“Android 概述”按钮和触摸主屏幕来使应用在后台运行。 设备准备就绪后,在 Firebase 控制台中单击“发送消息”:

“发送消息”按钮

显示“审阅消息”对话框时,单击“发送”。 通知图标应显示在设备(或模拟器)的通知区域中:

系统会显示通知图标

打开通知图标以查看消息。 通知消息应准确键入到 Firebase 控制台的“消息文本”字段中:

系统会在设备上显示通知消息

点击通知图标以启动 FCMClient 应用。 发送到 FCMClientIntent 附加内容列在 IDE 输出窗口中:

来自键、消息 ID 和折叠键的意图附加列表

在此示例中,from 键设置为应用的 Firebase 项目编号(本示例中为 41590732),collapse_key 设置为其包名称 (com.xamarin.fcmexample)。 如果未收到消息,请尝试删除设备(或模拟器)上的 FCMClient 应用,并重复上述步骤。

注意

如果强制关闭应用,FCM 将停止传送通知。 Android 可防止后台服务广播无意或不必要地启动已停止应用程序的组件。 (有关此行为的详细信息,请参阅对已停止的应用程序启动控制。)因此,每次从调式会话运行和停止应用时,都需要手动卸载该应用,这会强制 FCM 生成新令牌,以便继续接收消息。

添加自定义默认通知图标

在前面的示例中,通知图标设置为应用程序图标。 以下 XML 为通知配置自定义默认图标。 Android 为未显式设置通知图标的所有通知消息显示此自定义默认图标。

若要添加自定义默认通知图标,请将图标添加到 Resources/drawable 目录,编辑 AndroidManifest.xml,并将以下 <meta-data> 元素插入到 <application> 部分中:

<meta-data
    android:name="com.google.firebase.messaging.default_notification_icon"
    android:resource="@drawable/ic_stat_ic_notification" />

在此示例中,位于 Resources/drawable/ic_stat_ic_notification.png 的通知图标将用作自定义默认通知图标。 如果未在 AndroidManifest.xml 中配置自定义默认图标,并且通知有效负载中未设置任何图标,则 Android 会将应用程序图标用作通知图标(如上面的通知图标屏幕截图所示)。

处理主题消息

到目前为止编写的代码将处理注册令牌,并向应用添加远程通知功能。 下一个示例会添加用于侦听主题消息并将其作为远程通知转发给用户的代码。 主题消息是发送到订阅特定主题的一个或多个设备的 FCM 消息。 有关主题消息的详细信息,请参阅主题消息传送

订阅主题

编辑 Resources/layout/Main.axml,并在上一个 Button 元素后面立即添加以下 Button 声明:

<Button
  android:id="@+id/subscribeButton"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_gravity="center_horizontal"
  android:layout_marginTop="20dp"
  android:text="Subscribe to Notifications" />

此 XML 向布局添加“订阅通知”按钮。 编辑 MainActivity.cs,并将以下代码添加到 OnCreate 方法的末尾:

var subscribeButton = FindViewById<Button>(Resource.Id.subscribeButton);
subscribeButton.Click += delegate {
    FirebaseMessaging.Instance.SubscribeToTopic("news");
    Log.Debug(TAG, "Subscribed to remote notifications");
};

此代码会在布局中找到“订阅通知”按钮,并将其单击处理程序分配给调用 FirebaseMessaging.Instance.SubscribeToTopic 的代码(传入已订阅的主题新闻)。 当用户点击“订阅”按钮时,应用将订阅新闻主题。 在下一部分中,将从 Firebase 控制台通知 GUI 发送新闻主题消息。

发送主题消息

卸载应用,重新生成,然后再次运行它。 单击“订阅通知”按钮:

“订阅通知”按钮

如果应用已成功订阅,则应在 IDE 输出窗口中看到主题同步成功

“输出”窗口显示主题同步成功消息

使用以下步骤发送主题消息:

  1. 在 Firebase 控制台中,单击“新建消息”。

  2. 在“编写消息”页上,输入消息文本并选择“主题”。

  3. 在“主题”下拉菜单中,选择内置主题新闻

    选择新闻主题

  4. 在 Android 设备(或模拟器)上,通过点击“Android 概述”按钮和触摸主屏幕来使应用在后台运行。

  5. 设备准备就绪后,在 Firebase 控制台中单击“发送消息”。

  6. 检查 IDE 输出窗口以查看日志输出中的 /topics/news

    显示来自 /topic/news 的消息

当在输出窗口中看到此消息时,通知图标还应显示在 Android 设备的通知区域中。 打开通知图标以查看主题消息:

系统以通知形式显示主题消息

如果未收到消息,请尝试删除设备(或模拟器)上的 FCMClient 应用,并重复上述步骤。

前台通知

若要在前台应用中接收通知,必须实现 FirebaseMessagingService。 接收数据有效负载和发送上游消息也需要此服务。 以下示例演示了如何实现扩展 FirebaseMessagingService 的服务,生成的应用能够在前台运行时处理远程通知。

实现 FirebaseMessagingService

FirebaseMessagingService 服务负责从 Firebase 接收和处理消息。 每个应用必须对此类型划分子类并重写 OnMessageReceived 以处理传入消息。 当应用位于前台时,OnMessageReceived 回调将始终处理消息。

注意

应用只有 10 秒的时间来处理传入的 Firebase 云消息。 应使用 Android 作业计划程序Firebase 作业调度程序等库为后台执行计划超过此时间的任何工作。

添加名为 MyFirebaseMessagingService.cs 的新文件,并将其模板代码替换为以下内容:

using System;
using Android.App;
using Android.Content;
using Android.Media;
using Android.Util;
using Firebase.Messaging;

namespace FCMClient
{
    [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);
            Log.Debug(TAG, "Notification Message Body: " + message.GetNotification().Body);
        }
    }
}

请注意,必须声明 MESSAGING_EVENT 意图筛选器,才能将新的 FCM 消息定向到 MyFirebaseMessagingService

[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]

当客户端应用从 FCM 接收消息时,OnMessageReceived 会通过调用其 GetNotification 方法从传入的 RemoteMessage 对象中提取消息内容。 接下来,它会记录消息内容,以便可以在 IDE 输出窗口中查看它:

var body = message.GetNotification().Body;
Log.Debug(TAG, "Notification Message Body: " + body);

注意

如果在 FirebaseMessagingService 中设置断点,根据 FCM 传递消息的方式,调试会话可能会也可能不会命中这些断点。

发送另一条消息

卸载应用,重新生成,再次运行它,然后按照以下步骤发送另一条消息:

  1. 在 Firebase 控制台中,单击“新建消息”。

  2. 在“编写消息”页上,输入消息文本并选择“单个设备”。

  3. 从 IDE 输出窗口中复制令牌字符串,并如之前一样将其粘贴到 Firebase 控制台的“FCM 注册令牌”字段中。

  4. 确保应用在前台运行,然后单击 Firebase 控制台中的“发送消息”:

    从控制台发送另一条消息

  5. 显示“审阅消息”对话框时,单击“发送”。

  6. 传入消息记录到 IDE 输出窗口:

    输出窗口显示的消息正文

添加本地通知发送方

在此剩余示例中,传入的 FCM 消息将转换为应用在前台运行时启动的本地通知。 编辑 MyFirebaseMessageService.cs 并添加以下 using 语句:

using FCMClient;
using System.Collections.Generic;

将以下方法添加到 MyFirebaseMessagingService

void SendNotification(string messageBody, IDictionary<string, string> data)
{
    var intent = new Intent(this, typeof(MainActivity));
    intent.AddFlags(ActivityFlags.ClearTop);
    foreach (var key in data.Keys)
    {
        intent.PutExtra(key, data[key]);
    }

    var pendingIntent = PendingIntent.GetActivity(this,
                                                  MainActivity.NOTIFICATION_ID,
                                                  intent,
                                                  PendingIntentFlags.OneShot);

    var notificationBuilder = new  NotificationCompat.Builder(this, MainActivity.CHANNEL_ID)
                              .SetSmallIcon(Resource.Drawable.ic_stat_ic_notification)
                              .SetContentTitle("FCM Message")
                              .SetContentText(messageBody)
                              .SetAutoCancel(true)
                              .SetContentIntent(pendingIntent);

    var notificationManager = NotificationManagerCompat.From(this);
    notificationManager.Notify(MainActivity.NOTIFICATION_ID, notificationBuilder.Build());
}

为了区分此通知与后台通知,此代码使用不同于应用程序图标的图标标记通知。 将文件 ic_stat_ic_notification.png 添加到 Resources/drawable,并将其包含在 FCMClient 项目中。

SendNotification 方法使用 NotificationCompat.Builder 创建通知,使用 NotificationManagerCompat 启动通知。 通知保留一个 PendingIntent,允许用户打开应用并查看传入到 messageBody 的字符串的内容。 有关 NotificationCompat.Builder 的详细信息,请参阅本地通知

OnMessageReceived 方法的末尾调用 SendNotification 方法:

public override void OnMessageReceived(RemoteMessage message)
{
    Log.Debug(TAG, "From: " + message.From);

    var body = message.GetNotification().Body;
    Log.Debug(TAG, "Notification Message Body: " + body);
    SendNotification(body, message.Data);
}

做出这些更改后,每当应用处于前台时收到通知时,SendNotification 都会运行,通知将显示在通知区域中。

当应用处于后台时,消息的有效负载将确定消息的处理方式:

  • 通知 – 消息将发送到系统托盘。 那里将显示本地通知。 当用户点击通知时,应用将启动。
  • 数据 – 消息将由 OnMessageReceived 处理。
  • 两者 – 同时包含通知和数据有效负载的消息将传送到系统托盘。 应用启动时,数据有效负载将显示在用于启动应用的 IntentExtras 中。

在此示例中,如果应用在后台运行,则在消息具有数据有效负载时 SendNotification 将运行。 否则,将启动后台通知(如本演练中之前所示)。

发送最后一条消息

卸载应用,重新生成,再次运行它,然后使用以下步骤发送最后一条消息:

  1. 在 Firebase 控制台中,单击“新建消息”。

  2. 在“编写消息”页上,输入消息文本并选择“单个设备”。

  3. 从 IDE 输出窗口中复制令牌字符串,并如之前一样将其粘贴到 Firebase 控制台的“FCM 注册令牌”字段中。

  4. 确保应用在前台运行,然后单击 Firebase 控制台中的“发送消息”:

    发送前台消息

这一次,在输出窗口中记录的消息也会打包在新通知中,应用在前台运行时通知图标显示在通知托盘中:

前台消息的通知图标

打开通知时,应会看到从 Firebase 控制台通知 GUI 发送的最后一条消息:

带有前台图标的前台通知

断开与 FCM 的连接

若要取消订阅主题,请对 FirebaseMessaging 类调用 UnsubscribeFromTopic 方法。 例如,若要取消订阅之前订阅的新闻主题,可以使用以下处理程序代码将“取消订阅”按钮添加到布局:

var unSubscribeButton = FindViewById<Button>(Resource.Id.unsubscribeButton);
unSubscribeButton.Click += delegate {
    FirebaseMessaging.Instance.UnsubscribeFromTopic("news");
    Log.Debug(TAG, "Unsubscribed from remote notifications");
};

若要从 FCM 中注销设备,请通过对 FirebaseInstanceId 类调用 DeleteInstanceId 方法来删除实例 ID。 例如:

FirebaseInstanceId.Instance.DeleteInstanceId();

此方法调用将删除实例 ID 及其关联的数据。 因此,定期向设备发送 FCM 数据会停止。

疑难解答

下面介绍了将 Firebase Cloud Messaging 与 Xamarin.Android 配合使用时可能出现的问题和解决方法。

未初始化 FirebaseApp

在某些情况下,你可能会看到此错误消息:

Java.Lang.IllegalStateException: Default FirebaseApp is not initialized in this process
Make sure to call FirebaseApp.initializeApp(Context) first.

这是一个已知问题,可以通过清理解决方案和重新生成项目(“生成 > 清理解决方案”、“生成 > 重新生成解决方案”)来解决此问题。

总结

本演练详细介绍了在 Xamarin.Android 应用程序中实现 Firebase Cloud Messaging 远程通知的步骤。 其中介绍了如何安装 FCM 通信所需的包,并介绍了如何配置 Android 清单以访问 FCM 服务器。 提供的演示代码阐述了如何检查 Google Play Services 是否存在。 它演示了如何实现与 FCM 协商注册令牌的实例 ID 侦听器服务,并介绍了此代码在应用在后台运行时如何创建后台通知。 它介绍了如何订阅主题消息,并提供了消息侦听器服务的示例实现,该服务用于在应用在前台运行时接收和显示远程通知。