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

Oreo系统后台接收通知出现重复通知问题排查与解决

问题原因分析

我帮你梳理下为啥会出现两条通知的核心问题:

这是FCM消息类型与后台处理逻辑的冲突导致的:

  • FCM分两种消息类型:通知消息(Notification Message)和数据消息(Data Message)。
  • 当应用在后台时,如果收到的是包含notification字段的消息,FCM系统服务会自动帮你展示一条通知;如果此时你的自定义渠道没被FCM识别到(或者创建时机有问题),系统就会自动生成默认渠道来显示这条通知。
  • 同时,你的PushMessageReceiver又接收到了这条消息的内容,手动调用NotificationHandler再显示了一条自定义渠道的通知,最终就出现了两条重复通知。

另外,你虽然配置了com.google.firebase.messaging.default_notification_channel_id,但如果FCM自动处理通知时,自定义渠道还未完成创建(哪怕概率很低),也会触发系统创建默认渠道。

解决方法

根据你的业务需求,分两种场景处理:

场景1:完全自定义控制所有通知(推荐)

这种方式能彻底避免重复通知,步骤如下:

  1. 调整FCM消息发送结构:只发送数据消息,也就是消息体里只有data字段,不要包含notification字段。示例结构:

    {
      "to": "你的设备token",
      "data": {
        "title": "促销通知",
        "body": "新品上市",
        "messageid": "123",
        // 其他自定义字段...
      }
    }
    

    这样不管应用在前台还是后台,消息都会传递到你的PushMessageReceiver,由你完全控制通知显示,FCM不会自动弹出通知,自然不会有重复或默认渠道的问题。

  2. 优化渠道创建逻辑:在现有创建渠道的方法里增加判断,避免重复创建,确保渠道在FCM需要时已存在:

    @RequiresApi(api = Build.VERSION_CODES.O)
    public static void createChannels(Context context) {
        NotificationManager manager = getManager(context);
        // 先检查渠道是否已存在,避免重复创建
        NotificationChannel existingChannel = manager.getNotificationChannel(ANDROID_CHANNEL_ID);
        if (existingChannel == null) {
            NotificationChannel androidChannel = new NotificationChannel(ANDROID_CHANNEL_ID, ANDROID_CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH);
            androidChannel.enableLights(true);
            androidChannel.enableVibration(true);
            androidChannel.setLightColor(android.app.Notification.DEFAULT_LIGHTS);
            androidChannel.setLockscreenVisibility(android.app.Notification.VISIBILITY_PRIVATE);
            manager.createNotificationChannel(androidChannel);
        }
    }
    

场景2:必须发送包含notification字段的消息

如果业务需求必须发送通知+数据的组合消息,需要避免重复处理:

  1. 判断应用前后台状态:在PushMessageReceiver的onReceive方法里,先判断应用是否在后台,如果是后台就不手动显示通知(因为FCM已经自动处理了):
    @Override
    public void onReceive(Context context, Intent intent) {
        abortBroadcast();
        Logger.d(Logger.TAG, "notification received");
        
        // 判断应用是否在后台,后台时直接返回,避免重复显示
        if (!isAppInForeground(context)) {
            return;
        }
        
        if (intent != null) {
            NotificationHandler.handleNotification(context, formNotificationObject(intent));
        }
    }
    
    // 工具方法:判断应用是否处于前台
    private boolean isAppInForeground(Context context) {
        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
        if (appProcesses == null) {
            return false;
        }
        final String packageName = context.getPackageName();
        for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
            if (appProcess.processName.equals(packageName) && appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                return true;
            }
        }
        return false;
    }
    
  2. 确认渠道ID完全匹配:再次检查Manifest里的default_notification_channel_id值和NotificationHandler里的ANDROID_CHANNEL_ID完全一致(你的代码里都是com.caps,这部分没问题),这样FCM自动显示的通知会使用你的自定义渠道,不会创建默认渠道。

额外建议:迁移到FirebaseMessagingService

现在FCM官方推荐使用FirebaseMessagingService接收消息,比BroadcastReceiver更可靠,也更容易区分前后台逻辑。你可以替换现有Receiver为Service:

  1. 创建Service类:
    public class MyFirebaseMessagingService extends FirebaseMessagingService {
        @Override
        public void onMessageReceived(RemoteMessage remoteMessage) {
            // 处理消息,数据消息或前台时的通知消息都会走到这里
            Notification notification = formNotificationObject(remoteMessage);
            NotificationHandler.handleNotification(this, notification);
        }
        
        private Notification formNotificationObject(RemoteMessage remoteMessage) {
            Notification notification = new Notification();
            // 从remoteMessage.getData()获取自定义字段
            notification.setMessageId(Integer.parseInt(remoteMessage.getData().get("messageid")));
            notification.setTitle(remoteMessage.getData().get("title"));
            notification.setBody(remoteMessage.getData().get("body"));
            // 其他字段处理...
            return notification;
        }
    }
    
  2. 在Manifest里注册Service:
    <service
        android:name=".MyFirebaseMessagingService"
        android:exported="false">
        <intent-filter>
            <action android:name="com.google.firebase.MESSAGING_EVENT" />
        </intent-filter>
    </service>
    

内容的提问来源于stack exchange,提问作者andro-girl

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:40:44