You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin FCM推送前台/杀进程不显示、后台Data计数异常问题

问题对应修复方案

核心问题根因

  • 后台打开通知时Data计数异常递增:事件重复绑定+消息未做去重,多场景触发下同一消息会被多次处理
  • 进程被杀/前台状态下通知不弹出:缺少SDK前置初始化、未适配高版本Android通知权限与通知渠道、默认配置下前台不自动弹窗

分步修复操作

  1. 解决事件重复绑定问题
    所有推送事件绑定逻辑执行前,先解绑同名委托,避免应用重建、后台切前台时构造函数重复执行导致的多次订阅。如果需要做消息计数,给每一条推送下发唯一message_id字段,客户端处理前先校验本地是否已经处理过该ID,已处理的消息直接跳过,杜绝重复计数。

  2. 解决前台不弹通知问题
    推送插件默认不会在应用前台时自动弹出通知栏提醒,需要在收到消息的回调里判断当前应用状态,处于前台时主动调用本地通知渲染方法弹出提醒。

  3. 解决进程被杀不弹通知问题
    仅在共享项目的App构造函数中初始化逻辑是不够的,需要在Android项目的Application类的OnCreate生命周期中提前完成推送SDK初始化,保证进程冷启动时SDK已经完成推送服务注册,能正常接收系统推送。同时需要完成两项高版本适配:

    • 为Android 8.0及以上系统创建默认通知渠道,未创建渠道的通知会被系统直接拦截
    • 在应用启动时针对Android 13及以上系统动态申请POST_NOTIFICATIONS权限,未获得权限的应用无法弹出任何通知

修正后核心代码

共享项目 App.xaml.cs

using System;
using Xamarin.Forms;
using Plugin.FirebasePushNotification;
using Xamarin.Forms.Xaml;
using Xamarin.Essentials;

namespace FCMTest
{
    public partial class App : Application
    {
        public App()
        {
            InitializeComponent();
            MainPage = new MainPage();

            // 绑定前先解绑,防止重复订阅
            CrossFirebasePushNotification.Current.OnTokenRefresh -= Current_OnTokenRefresh;
            CrossFirebasePushNotification.Current.OnNotificationReceived -= Current_OnNotificationReceived;
            CrossFirebasePushNotification.Current.OnNotificationOpened -= Current_OnNotificationOpened;
            
            CrossFirebasePushNotification.Current.Subscribe("all");
            CrossFirebasePushNotification.Current.OnTokenRefresh += Current_OnTokenRefresh;
            CrossFirebasePushNotification.Current.OnNotificationReceived += Current_OnNotificationReceived;
            CrossFirebasePushNotification.Current.OnNotificationOpened += Current_OnNotificationOpened;
        }

        private void Current_OnTokenRefresh(object source, FirebasePushNotificationTokenEventArgs e)
        {
            System.Diagnostics.Debug.WriteLine($"Token from function OnTokenRefresh: {e.Token}");
        }

        private void Current_OnNotificationReceived(object source, FirebasePushNotificationDataEventArgs d)
        {
            System.Diagnostics.Debug.WriteLine("Received");
            // 消息去重逻辑,可替换为自己的本地存储校验
            if (d.Data.ContainsKey("message_id") && Preferences.ContainsKey($"processed_{d.Data["message_id"]}"))
            {
                return;
            }
            if (d.Data.ContainsKey("message_id"))
            {
                Preferences.Set($"processed_{d.Data["message_id"]}", true);
            }
            
            foreach (var data in d.Data)
            {
                System.Diagnostics.Debug.WriteLine($"{data.Key} : {data.Value}");
            }

            // 前台状态手动弹出通知
            if (IsAppForeground())
            {
                var title = d.Data.ContainsKey("title") ? d.Data["title"].ToString() : "Test from code";
                var body = d.Data.ContainsKey("body") ? d.Data["body"].ToString() : "Here is your test!";
                CrossFirebasePushNotification.Current.PresentLocalNotification(title, body, d.Data);
            }
        }

        private void Current_OnNotificationOpened(object source, FirebasePushNotificationResponseEventArgs d)
        {
            System.Diagnostics.Debug.WriteLine("Opened");
            // 同样加去重逻辑,避免打开事件重复触发计数
            if (d.Data.ContainsKey("message_id") && Preferences.ContainsKey($"opened_{d.Data["message_id"]}"))
            {
                return;
            }
            if (d.Data.ContainsKey("message_id"))
            {
                Preferences.Set($"opened_{d.Data["message_id"]}", true);
            }
            
            foreach (var data in d.Data)
            {
                System.Diagnostics.Debug.WriteLine($"{data.Key} : {data.Value}");
            }
        }

        private bool IsAppForeground()
        {
            try
            {
                return Platform.CurrentActivity != null;
            }
            catch
            {
                return false;
            }
        }
        
        protected override void OnStart()
        {
            // Android13+ 动态申请通知权限
            if (DeviceInfo.Platform == DevicePlatform.Android && DeviceInfo.Version.Major >= 13)
            {
                Permissions.RequestAsync<Permissions.PostNotifications>();
            }
        }

        protected override void OnSleep()
        {
        }

        protected override void OnResume()
        {
        }
    }
}

Android项目 MainApplication.cs(需新增)

using System;
using Android.App;
using Android.Runtime;
using Plugin.FirebasePushNotification;

namespace FCMTest.Droid
{
    [Application]
    public class MainApplication : Application
    {
        protected MainApplication(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
        {
        }

        public override void OnCreate()
        {
            base.OnCreate();
            
            // 初始化SDK并创建默认通知渠道
            FirebasePushNotificationManager.Initialize(this, new PushNotificationChannel[]
            {
                new PushNotificationChannel("fcm_default_channel", "推送通知", NotificationImportance.Default)
                {
                    LockscreenVisibility = NotificationVisibility.Public
                }
            }, true);

            // 处理进程被杀状态下点击通知拉起应用的场景
            FirebasePushNotificationManager.ProcessIntent(Intent);
        }
    }
}

服务端发送调整

原有发送逻辑无需大改,只需要在Data字段中补充唯一message_id字段,配合客户端做消息去重即可:

var message = new Message()
{
    Data = new Dictionary<string, string>()
    {
        {"myData", "One more try to say Succeded!"},
        {"message_id", Guid.NewGuid().ToString()} // 新增唯一消息ID
    },
    Token = registrationToken,
    Notification = new Notification()
    {
        Title = "Test from code",
        Body = "Here is your test!"
    }
};

内容的提问来源于stack exchange,提问作者Joseph Dagher

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 23:45:55