android - 应用程序被终止时无法处理 FCM 消息

标签 android firebase xamarin.forms firebase-cloud-messaging

我一直在阅读各种教程、其他 SO 线程以及官方 Android 开发人员和 Firebase 文档,但无济于事。我几乎尝试了所有方法,但我已经耗尽了精力和时间,因为我正在修复以前可以工作但现在不再工作的通知系统。

我正在使用 Azure 通知中心将通知分发到其他推送通知平台中的 FCM。我的 FCM 项目仅针对 Android。我的应用程序是使用所有依赖项的最新 NuGet 包版本(Xamarin.Forms 5.x.x.x、Firebase.Messaging 122.0 等)在 Xamarin.Forms 中构建的。

目前,应用运行时或后台接收的远程消息可以通过继承和实现 FirebaseMessagingService 的自定义服务完美地工作。一旦应用程序被终止(任务切换器 -> 滑动应用程序),在发送更多消息后,我开始看到包含以下内容的 Logcat 消息:

broadcast intent callback: result=CANCELLED forIntent { act=com.google.android.c2dm.intent.RECEIVE pkg= (has extras) }

我了解 Google 改变了 API 26 及更高版本中隐式接收器的工作方式,我的理解是此操作 ( com.google.android.c2dm.intent.RECEIVE ) 不包含在异常(exception)列表中,因此我不知道如何监听消息并在后台处理。我最近在 2019 年 7 月在其他线程中读到,在应用程序被终止时收到的 FCM 消息应该直接发送到通知托盘。这不会是一个普遍存在的问题,因为许多应用程序在被终止时会发送通知,因此我希望获得一些当前信息来指导我找到解决方案。

此 Intent 广播是否由于隐式接收器更改而被取消,或者我是否做错了什么?

我正在运行 Android 10 的 OnePlus 7 Pro 上进行测试,所以我想知道这是否是其他人在华为和小米等 OEM 设备上提到的电池优化问题。

我的应用的目标是 Android API 级别 29,最低 API 21

我为我的主要 Activity 和接收器启用了直接启动感知,以确保接收器在启动时和用户打开应用程序之前拦截我的应用程序的 Intent :

<receiver android:directBootAware="true" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND" android:name=".NotificationReceiver">
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <category android:name="<package>" />
    </intent-filter>
</receiver>

我的主要 Activity 包括作为启动 Activity 的 Intent 过滤器:

      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
      <intent-filter>
        <action android:name=".MainActivity" />
        <category android:name="android.intent.category.DEFAULT" />
      </intent-filter>

我请求以下权限:

  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.CAMERA" />
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
  <uses-permission android:name="android.permission.WAKE_LOCK" />
  <uses-permission android:name="android.permission.GET_ACCOUNTS" />

我定义了以下meta-data我的 list 中的标签 <application>标签:

<meta-data
    android:name="com.google.firebase.messaging.default_notification_channel_id"
    android:value="@string/..."/>
<meta-data
    android:name="com.google.firebase.messaging.default_notification_icon"
    android:resource="@drawable/..." />
<meta-data
    android:name="com.google.firebase.messaging.default_notification_color"
    android:resource="@color/..." />

这些值非常适合在后台收到的通知,因此我知道它们配置正确。

编辑1:

自从发布以来,我做了更多的挖掘和调试,我确实看到了这一点,没有我的自定义 BroadcastReceiver 也监听我的应用程序的 c2dm.intent.RECEIVE 操作:

  • 实际上是通过 Google 提供的内部 FirebaseInstanceIdReceiver 获取我在应用被终止时发送的远程消息,该内部 FirebaseInstanceIdReceiver 是记录的 FCM 设置的一部分(我删除了前面提到的 NotificationReceiver 服务)
  • 正在开始 FirebaseMessagingService 的自定义实现(请参见下面的屏幕截图)
  • 未在我的 FirebaseMessagingService 中触发 OnMessageReceived 过载(请参见下面的屏幕截图)

logcat upon receiving FCM remote message while app is killed

*****FirebaseService 部分代码:

    [Service(DirectBootAware = true, Exported = true, Enabled = true)]
    [IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
    [IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
    public class MyFirebaseService : FirebaseMessagingService
    {
        public MyFirebaseService()
        {
            
        }

        public override ComponentName StartService(Intent service)
        {
            Log.Info("GCM", $"MyFirebaseService started from intent {service}");

            return base.StartService(service);
        }

        public override void OnMessageReceived(RemoteMessage message)
        {
            var notification = message.GetNotification();
            Log.Info("GCM", $"Received remote message from FCM. Has notification: {notification != null}, has data: {message.Data != null}");

            if (notification != null)
            {
                message.Data.TryGetValue("route", out string route);
                SendNotification(notification.Title, notification.Body, route);
            }
            else
            {
                ParseDataNotification(message);
            }
        }

        ...

最佳答案

我能够解决这个问题。我的 FirebaseMessagingService 实现在构造函数中进行了依赖注入(inject)调用,当 FirebaseIidInstanceReceiver 在后台启动服务时,该调用失败。这导致服务无法启动,并且在应用程序被终止时没有生成 Android 通知。

由于我已经进行了大量的挖掘,并且有关该主题的信息如此分散且过时,因此我将尝试在此处编译我所知道的结果以形成可行的解决方案:

按照步骤 here 操作,特别是设置您的 FCM 项目并下载 google-services.json文件。

确保您的 list 声明了以下权限:

<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET" />

在 AndroidManifest 中添加以下内容 <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>

可以选择定义通知 channel 、通知图标(必须仅为白色,允许透明)以及通知托盘展开时的通知图标颜色的默认值,也在 <application> 内。 list 标记:

<meta-data
    android:name="com.google.firebase.messaging.default_notification_channel_id"
    android:value="@string/..."/>
<meta-data
    android:name="com.google.firebase.messaging.default_notification_icon"
    android:resource="@drawable/..." />
<meta-data
    android:name="com.google.firebase.messaging.default_notification_color"
    android:resource="@color/..." />

创建一个继承自 FirebaseMessagingService 的自定义类。在 Xamarin.Forms 中,您将需要 Xamarin.Firebase.Messaging此类的 NuGet 包。在您的实现中,您应该覆盖 OnMessageReceived(RemoteMessage)并添加您的应用程序逻辑,该逻辑将处理包含 notification 的消息前台属性和仅包含 data 的消息前景和背景的属性。您的类应使用以下属性进行修饰(请注意,DirectBootAware 是可选的;请参见下文):

[Service(DirectBootAware = true, Exported = true, Enabled = true)]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
[IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]

如果您希望确保在设备重新启动后和设备解锁之前可以收到通知,您可以考虑使您的应用程序和 FirebaseMessagingService 实现Direct Boot Aware(更多 here )

在您的 MainActivity 中,确保为运行 Android O 或更高版本的设备创建通知 channel ,并在 OnCreate 期间的某个时刻调用此方法。 :

private 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 channelId = GetString(Resource.String./*res id here*/);
    var notificationManager = (NotificationManager)GetSystemService(NotificationService);

    // Don't re-create the notification channel if we already created it
    if (notificationManager.GetNotificationChannel(channelId) == null)
    {
        var channel = new NotificationChannel(channelId,
            "<display name>",
            NotificationImportance.Default);

        notificationManager.CreateNotificationChannel(channel); 
    }
}

将 ProGuard 配置文件(“proguard.cfg”)添加到您的 Android 项目中,以防止 SDK 链接器终止 Google Play 和 Firebase 库。在 Visual Studio 中编辑此文件的属性并将生成操作设置为 ProguardConfiguration 。即使下拉列表中缺少该选项,Xamarin 也会识别它。如果您在构建中使用 d8 和 r8 而不是 dx 和 ProGuard,Xamarin 仍将使用此配置文件并符合您在其中定义的规则。

# Keep commands are required to prevent the linker from killing dependencies not directly referenced in code
# See: https://forums.xamarin.com/discussion/95107/firebaseinstanceidreceiver-classnotfoundexception-when-receiving-notifications

-dontwarn com.google.android.gms.**
-keep class com.google.android.gms.** { *; }
-keep class com.google.firebase.** { *; } 

希望这对您有所帮助,如果我错过了任何内容,我将更新更多详细信息。

关于android - 应用程序被终止时无法处理 FCM 消息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68074280/

相关文章:

ios - Swift Firebase 在 child 身上获得值(value)

angular - 从 FirebaseListObservables 数组到字符串数组

ios - 使用 Cocoapods 添加 Firebase 时的 Apple Mach-O 链接器错误组

c# - 如何在xamarin表单中实现圆形布局

android - 预定的警报管理器不工作 Android

android-sdk\docs\reference 丢失

ios - ITMS-90809:不推荐使用的API使用情况(UIWebView)

c# - 如何使用 Rg.Plugins.Popup 将 XAML 和模板合并到一个 C# 模板中?

android - ContactsContract API android 中的问题

android - 从 Fragment 开始一个 Activity