简体   繁体   English

应用程序被杀死时无法处理 FCM 消息

[英]FCM messages unable to be handled when app is killed

I have been reading various tutorials, other SO threads, as well as the official Android Developer and Firebase documentation to no avail.我一直在阅读各种教程、其他 SO 线程以及官方 Android 开发人员和 Firebase 文档,但无济于事。 I've tried nearly everything and I'm running out of steam as well as time as I'm in the process of repairing a notification system that previously worked but no longer works.我已经尝试了几乎所有方法,但由于我正在修复以前有效但不再有效的通知系统,因此我的动力和时间都用完了。

I am using Azure Notification Hubs to distribute notifications to FCM among other Push Notification platforms.我正在使用 Azure 通知中心在其他推送通知平台中向 FCM 分发通知。 My FCM project targets only Android.我的 FCM 项目仅针对 Android。 My app is built in Xamarin.Forms using the latest NuGet package versions of all dependencies (Xamarin.Forms 5.xxx, Firebase.Messaging 122.0, etc.).我的应用程序使用所有依赖项的最新 NuGet 包版本(Xamarin.Forms 5.xxx、Firebase.Messaging 122.0 等)在 Xamarin.Forms 中构建。

Currently, remote messages received while the app is running or backgrounded work flawlessly via a custom Service inheriting and implementing FirebaseMessagingService.目前,应用程序运行或后台接收的远程消息通过继承和实现 FirebaseMessagingService 的自定义服务完美地工作。 Once the app is killed (task switcher -> swipe app away), upon sending further messages I start seeing Logcat messages with the following:一旦应用程序被终止(任务切换器 -> 滑动应用程序),在发送更多消息后,我开始看到包含以下内容的 Logcat 消息:

broadcast intent callback: result=CANCELLED forIntent { act=com.google.android.c2dm.intent.RECEIVE pkg= (has extras) }广播意图回调:result=CANCELLED forIntent { act=com.google.android.c2dm.intent.RECEIVE pkg=(有额外的)}

I understand Google changed the way implicit receivers work in API 26 and above and my understanding is that this action ( com.google.android.c2dm.intent.RECEIVE ) is not included in the exception list, so I am lost as to how the messages can be listened for and handled in the background.我了解 Google 更改了隐式接收器在 API 26 及更高版本中的工作方式,我的理解是此操作( com.google.android.c2dm.intent.RECEIVE )未包含在异常列表中,因此我不知道如何消息可以在后台监听和处理。 I have read in other threads as recently as July 2019 that FCM messages received while an app is killed are supposed to be sent directly to the notification tray.我最近在 2019 年 7 月在其他线程中读到,在应用程序被终止时收到的 FCM 消息应该直接发送到通知托盘。 This cannot be a widespread problem as many applications send notifications while killed, so I'm hoping to get some current information to direct me to a solution.这不是一个普遍的问题,因为许多应用程序在被杀死时发送通知,所以我希望获得一些当前信息来指导我找到解决方案。

Is this intent broadcast being cancelled because of the implicit receiver changes, or am I doing something else wrong?这个意图广播是因为隐式接收器更改而被取消,还是我做错了什么?

I am testing on a OnePlus 7 Pro with Android 10, so I am wondering if maybe it's a battery optimization issue that others have mentioned on devices by OEMs such as Huawei and Xiaomi.我正在使用 Android 10 的 OnePlus 7 Pro 进行测试,所以我想知道这是否是其他人在华为和小米等 OEM 的设备上提到的电池优化问题。

My app is targeting Android API level 29 with min API 21我的应用程序的目标是 Android API 级别 29,最小 API 21

I have enabled Direct Boot Awareness for my main activity as well as the receiver to ensure that the receiver intercepts intents for my app upon boot and before the user has opened the app:我为我的主要活动和接收器启用了直接启动感知,以确保接收器在启动时和用户打开应用程序之前拦截我的应用程序的意图:

<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>

My main activity includes intent filters for being the launch activity:我的主要活动包括作为启动活动的意图过滤器:

      <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>

I request the following permissions:我请求以下权限:

  <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" />

I have defined the following meta-data tags in my manifest <application> tag:我在清单<application>标签中定义了以下meta-data标签:

<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/..." />

These values work perfectly for received notifications while in background, so I know they are correctly configured.这些值非常适合在后台接收到的通知,所以我知道它们配置正确。

Edit 1:编辑1:

I have done more digging and debugging since posting and I do see that, without my custom BroadcastReceiver also listening for the c2dm.intent.RECEIVE action that my app:自发布以来,我做了更多的挖掘和调试,我确实看到了这一点,没有我的自定义 BroadcastReceiver 也监听我的应用程序的 c2dm.intent.RECEIVE 操作:

  • is in fact getting remote messages I send while the app is killed via the Google-provided internal FirebaseInstanceIdReceiver that is part of the documented FCM setup (I removed the NotificationReceiver service mentioned prior)实际上是通过 Google 提供的内部 FirebaseInstanceIdReceiver 终止应用程序时发送的远程消息,该内部 FirebaseInstanceIdReceiver 是记录的 FCM 设置的一部分(我删除了之前提到的 NotificationReceiver 服务)
  • is starting my custom implementation of the FirebaseMessagingService which (see below screenshot)正在开始我的 FirebaseMessagingService 的自定义实现(见下面的截图)
  • is not triggering OnMessageReceived overload in my FirebaseMessagingService (see below screenshot)没有在我的 FirebaseMessagingService 中触发 OnMessageReceived 过载(见下面的截图)

logcat upon receiving FCM remote message while app is killed当应用程序被杀死时收到 FCM 远程消息时的 logcat

*******FirebaseService partial code: *******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);
            }
        }

        ...

I was able to resolve the issue.我能够解决这个问题。 My FirebaseMessagingService implementation had a Dependency Injection call in the constructor which failed when the service was started in the background by the FirebaseIidInstanceReceiver.我的 FirebaseMessagingService 实现在构造函数中有一个依赖注入调用,当 FirebaseIidInstanceReceiver 在后台启动服务时失败。 This caused the service to fail to start and did not generate Android notifications while the app was killed.这导致服务无法启动,并且在应用程序被终止时未生成 Android 通知。

Since I've done a lot of digging and information on this topic is so fragmented and out of date, I'll try to compile what I know results in a working solution here:由于我已经进行了大量挖掘,并且关于该主题的信息非常零散和过时,因此我将尝试编译我所知道的结果,在这里找到一个可行的解决方案:

Follow the steps here , notably setting up your FCM project and downloading the google-services.json file.按照此处的步骤操作,特别是设置您的 FCM 项目并下载google-services.json文件。

Ensure your manifest declares the following permissions:确保您的清单声明以下权限:

<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" />

Add the following within your AndroidManifest <application> tag to listen for message receives:在您的 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>

Optionally define defaults for notification channel, notification icon (must be white color only, allowing transparency), and notification icon color when the notification tray is expanded, also within the <application> manifest tag:可选地定义通知通道、通知图标(必须为白色,允许透明)和通知托盘展开时的通知图标颜色的默认值,也在<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/..." />

Create a custom class inheriting from FirebaseMessagingService .创建一个继承自FirebaseMessagingService的自定义类。 In Xamarin.Forms, you will need the Xamarin.Firebase.Messaging NuGet package for this class.在 Xamarin.Forms 中,您将需要此类的 Xamarin.Firebase.Messaging NuGet 包。 Within your implementation, you should override OnMessageReceived(RemoteMessage) and add your application logic which will handle messages containing the notification property in the foreground and messages with only the data property in both the foreground and background.在您的实现中,您应该覆盖OnMessageReceived(RemoteMessage)并添加您的应用程序逻辑,该逻辑将处理OnMessageReceived(RemoteMessage)包含notification属性的消息以及在前台和后台中仅包含data属性的消息。 Your class should be decorated with the following attributes (note that DirectBootAware is optional; see below):你的类应该用以下属性装饰(注意 DirectBootAware 是可选的;见下文):

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

If you wish to ensure that notifications can be received after a device reboot and before the device is unlocked, you may consider making your application and your FirebaseMessagingService implementation Direct Boot Aware (more here )如果您希望确保在设备重启后和设备解锁之前可以收到通知,您可以考虑让您的应用程序和 FirebaseMessagingService 实现直接启动感知(更多信息请点击此处

In your MainActivity, ensure a Notification Channel is created for devices running Android O or higher, and this method invoked at some point during OnCreate :在您的 MainActivity 中,确保为运行 Android O 或更高版本的设备创建通知通道,并在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); 
    }
}

Add a ProGuard config file ("proguard.cfg") to your Android project to prevent the SDK linker from killing Google Play and Firebase libraries.将 ProGuard 配置文件 ("proguard.cfg") 添加到您的 Android 项目,以防止 SDK 链接器终止 Google Play 和 Firebase 库。 Edit the Properties of this file in Visual Studio and set the Build Action to ProguardConfiguration .在 Visual Studio 中编辑此文件的属性并将 Build Action 设置为ProguardConfiguration Even if the option is missing from the dropdown list, Xamarin will recognize it.即使下拉列表中缺少该选项,Xamarin 也会识别它。 If you are using d8 and r8 instead of dx and ProGuard in your build, Xamarin will still use this config file and conform to the rules you define within.如果您在构建中使用 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.** { *; } 

Hope this helps and if I've missed anything I will update with further details.希望这会有所帮助,如果我错过了什么,我会更新更多细节。

The @AndrewH solution worked for me. @AndrewH 解决方案对我有用。 With one missing detail.缺少一个细节。 The Firebase messaging service will get called also when the app is killed.当应用程序被Firebase messaging service也会被调用。 When the app is killed, only the constructor of the service will get called because internally firebase knows your app is killed.当应用程序被杀死时,只有服务的构造函数会被调用,因为内部 Firebase 知道您的应用程序已被杀死。 So, before you initialize any code that will handle notifications on foreground or any code that interacts with Xamarin forms , you should check if Xamarin forms has been initialized.因此,在初始化将处理前台通知的任何代码或与Xamarin forms交互的任何代码之前,您应该检查Xamarin forms是否已初始化。 For example:例如:

if (Xamarin.Forms.Forms.IsInitialized)
            {
                // Do stuffs.
            }

Otherwise your app will crash when it receives a push notification from killed state.否则你的应用程序会在收到来自终止状态的推送通知时崩溃。

Also you should know that if the app is killed or Xamarin.Forms.Forms.IsInitialized == false , you should not try to execute any code.此外,您应该知道,如果应用程序被Xamarin.Forms.Forms.IsInitialized == falseXamarin.Forms.Forms.IsInitialized == false ,则不应尝试执行任何代码。 Just leave it.就别管了。 Firebase will just show the notification for you. Firebase 只会为您显示通知。 You will just handle the notification when the user click on the notification from the system tray in your MainActivity.OnCreate() .当用户在MainActivity.OnCreate()单击系统托盘中的通知时,您将只处理通知。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 当应用程序被杀死时,来自FCM的数据消息未启用onMessage - Data messages from FCM not enabling onMessagereceived when app is killed 使用 FCM 杀死应用程序时,Android 无法接收通知 - Android unable to receive notification when app is killed using FCM 当应用程序被杀死时,FCM 不工作 - FCM is not working when the app is killed 应用被杀死时,应用未收到FCM通知 - app not receiving FCM notifications when the app is killed 应用程序完全终止时未收到 FCM 通知 - Not getting FCM notification when app is completely killed 应用程序被杀死时 FCM 通知不会触发 - FCM Notification not firing when app is killed 当应用程序处于后台或被杀死时,FCM onMessageReceived() 未被调用 - When app is in background or killed FCM onMessageReceived() not called 当应用程序被杀并且处于优化电池模式时,不会收到FCM消息 OnePlus 5t | OnePlus 6 | 错误广播意图回调:result = CANCELLED - Not receiving FCM messages when app is killed and in Optimised battery mode| OnePlus 5t |OnePlus 6 | Error broadcast intent callback: result=CANCELLED 应用程序被杀/刷出时未收到FCM推送通知 - FCM push notification not received when app is killed/swiped out 当应用被多个 Firebase 项目杀死时未收到 FCM 通知 - FCM notification not received when app is killed with multiple Firebase projects
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM