收到FCM推送时唤醒Android屏幕及相关功能实现与问题排查
问题解答
一、推送唤醒屏幕(后台/杀死状态生效)
现有代码的问题
你的wakeApp()方法在屏幕熄灭数分钟后失效,核心原因有两个:
- FCM消息类型限制:如果推送是带
notification字段的通知消息,APP后台或被杀死时,系统会直接展示通知,不会触发onMessageReceived();只有纯data字段的数据消息,才能在任何状态下触发该方法。 - WakeLock使用不当:
FULL_WAKE_LOCK已在API 17废弃,且你acquire()后立刻release(),系统可能还没完成唤醒操作就释放了锁,导致唤醒失败;同时Android 12+对WakeLock的权限和参数有更严格限制。
正确实现方案
强制使用FCM数据消息
发送推送时仅包含data字段,示例Payload:{ "data": { "title": "新通知", "body": "测试唤醒消息", "need_wake": "true" }, "to": "设备令牌" }这样无论APP处于前台、后台还是被杀死,都会触发
onMessageReceived()。修改唤醒逻辑
- 先在
AndroidManifest.xml添加必要权限:<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> <!-- 可选,用于解锁锁屏 --> - 替换唤醒代码:
private fun wakeScreen() { val pm = applicationContext.getSystemService(POWER_SERVICE) as PowerManager if (!pm.isInteractive) { val wakeLockTag = "$packageName:WAKELOCK" // 根据系统版本选择合适的WakeLock参数 val wakeLockFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { PowerManager.SCREEN_BRIGHT_WAKE_LOCK or PowerManager.ACQUIRE_CAUSES_WAKEUP } else { PowerManager.SCREEN_DIM_WAKE_LOCK or PowerManager.ACQUIRE_CAUSES_WAKEUP or PowerManager.ON_AFTER_RELEASE } val wakeLock = pm.newWakeLock(wakeLockFlags, wakeLockTag) wakeLock.acquire(10000L) // 持有10秒,确保屏幕完成唤醒 // 适配Android版本解锁锁屏 val keyguardManager = applicationContext.getSystemService(KEYGUARD_SERVICE) as KeyguardManager if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // Android 8+ 需通过Activity请求解锁 val intent = Intent(this, TransparentWakeActivity::class.java) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) startActivity(intent) } else { val keyguardLock = keyguardManager.newKeyguardLock(wakeLockTag) keyguardLock.disableKeyguard() } } } - 注:Android 10+ 无法直接禁用锁屏,需创建一个透明Activity调用
keyguardManager.requestDismissKeyguard()完成解锁。
- 先在
适配国产ROM后台限制
小米、华为、OPPO等品牌的ROM有严格的后台杀进程机制,需引导用户给APP开启「后台运行权限」「自启动权限」,否则APP被杀死后可能无法接收FCM消息。
二、静音模式下通知发声
要实现静音模式下通知仍发声,核心是创建绕过免打扰的高优先级通知渠道:
创建通知渠道(Android 8+)
private fun createHighPriorityChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channelId = "urgent_notification_channel" val channel = NotificationChannel(channelId, "紧急通知", NotificationManager.IMPORTANCE_HIGH).apply { description = "静音模式下仍可发声的通知" // 设置铃声并指定音频属性 setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_NOTIFICATION) .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .build()) enableVibration(true) // 关键:绕过免打扰(静音)模式 setBypassDnd(true) } val notificationManager = getSystemService(NotificationManager::class.java) notificationManager.createNotificationChannel(channel) } }构建高优先级通知
在onMessageReceived()中调用:private fun showUrgentNotification(title: String, body: String) { val intent = packageManager.getLaunchIntentForPackage(packageName) val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE) val notificationBuilder = NotificationCompat.Builder(this, "urgent_notification_channel") .setSmallIcon(R.drawable.ic_notification) .setContentTitle(title) .setContentText(body) .setPriority(NotificationCompat.PRIORITY_HIGH) .setContentIntent(pendingIntent) .setAutoCancel(true) .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)) // 兼容Android 7及以下,设置最高优先级绕过静音 .setPriority(NotificationCompat.PRIORITY_MAX) NotificationManagerCompat.from(this).notify(System.currentTimeMillis().toInt(), notificationBuilder.build()) }
总结
- 唤醒屏幕:必须用FCM数据消息触发
onMessageReceived(),使用适配系统版本的WakeLock参数并持有足够时间,同时引导用户开启ROM后台权限。 - 静音发声:创建带
setBypassDnd(true)的高优先级通知渠道,构建通知时指定对应优先级。
内容的提问来源于stack exchange,提问作者Itamar Stern
相关产品推荐
相关产品推荐

