Android FCM推送通知弹出时无声音的问题排查咨询
核心问题根源
Android 8.0+ 通知机制适配失效
当前OreoNotification类的实现,把声音配置写在了Notification.Builder里,但从API 26(Android 8.0)开始,通知的声音、震动、播放优先级权限完全绑定在通知渠道(Notification Channel)上:渠道一旦创建,所有相关配置会被系统固化,单独给Notification实例设置的sound、vibrate参数都会被直接忽略。
你当前创建渠道的代码中,完全缺失渠道级别的声音配置,自然会导致通知静默。
更易踩坑的细节:通知渠道创建后无法通过代码修改配置,如果之前安装过对应CHANNEL_ID的旧版本APP,就算后续改了渠道逻辑,只要不更换渠道ID,旧的静默配置依然会生效。FCM消息触发逻辑被系统截断
如果后端发送的FCM消息同时携带notification节点和data节点的混合内容,当APP处于后台、进程被杀死的状态时,FCM SDK会直接读取notification节点的内容,通过系统默认渠道自动生成通知,完全不会执行你自定义的MyFirebaseMessaging类中的通知构建逻辑,这时候你写的所有声音配置都不会生效。系统适配的隐性限制
部分国产定制ROM(小米、OPPO、vivo等)对通知的校验更严格:如果渠道重要等级设置为IMPORTANCE_DEFAULT、没有搭配对应音频属性,就算用系统默认渠道,也可能被ROM归为静默通知拦截声音;低版本(API<26)通知如果没设置高优先级,也可能被系统归为静默通知不播声音。
另外Android 12以上对PendingIntent的可变性有强制要求,如果没指定对应flag,通知会被系统拦截,连带声音也不会触发。
可落地的修复步骤
重构8.0+版本的通知渠道配置
- 更换全新的
CHANNEL_ID(比如把原来的"some_id"改为"selfcial_message_channel_v2"),避免旧渠道的固化配置影响新逻辑。 - 在
createChannel()方法中完成渠道级的声音配置,删除无效的Notification级声音设置:@TargetApi(Build.VERSION_CODES.O) private void createChannel() { Uri soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); // 构建标准音频属性,适配全机型的声音播放规则 AudioAttributes audioAttributes = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_NOTIFICATION) .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .build(); NotificationChannel channel = new NotificationChannel( CHANNEL_ID, CHANNEL_NAME, // 提升渠道重要等级为高优先级,确保通知能正常弹窗、播声音 NotificationManager.IMPORTANCE_HIGH ); channel.enableLights(true); channel.enableVibration(true); // 渠道级绑定声音配置,这才是8.0以上系统实际生效的声音参数 channel.setSound(soundUri, audioAttributes); channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); getManager().createNotificationChannel(channel); } - 删掉
getOreoNotification()方法中无效的.setSound(soundUri)配置,避免混淆判断逻辑。
- 更换全新的
调整FCM消息的发送结构
通知后端发送纯data结构的FCM消息,即请求体中仅保留data字段,不携带notification字段。这样无论APP在前台、后台还是进程被杀死,消息都会被投递到你自定义的MyFirebaseMessaging的onMessageReceived方法中,完全走你自定义的通知构建流程,不会被系统自动截断用默认渠道生成静默通知。
如果必须保留notification节点,需要在节点中显式指定sound参数为对应的提示音路径,确保系统自动生成通知时能正常播放声音。补充低版本和高版本的兼容配置
- 给API<26分支的
NotificationCompat.Builder补充高优先级配置,避免被系统归为静默通知:NotificationCompat.Builder builder = new NotificationCompat.Builder(this) .setSmallIcon(R.drawable.ic_notification) .setContentTitle(title) .setContentText(body) .setAutoCancel(true) .setSound(soundUri) // 新增高优先级配置 .setPriority(NotificationCompat.PRIORITY_HIGH) .setContentIntent(pendingIntent); - 给所有
PendingIntent调用补充Android 12要求的可变性flag,避免通知被系统拦截:PendingIntent pendingIntent = PendingIntent.getActivity( this, j, intent, // 补充FLAG_IMMUTABLE,适配Android 12+的规则 PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE );
- 给API<26分支的
最后做真机排查:确认APP的系统通知开关、对应通知渠道的“允许播放提示音”“允许振动”开关处于打开状态,部分国产ROM默认会把第三方APP的通知渠道设为静默,需要手动确认开关状态。
内容的提问来源于stack exchange,提问作者Achisyg

