Android应用处于后台时,如何通过FCM消息触发onMessageReceived方法?
Android应用处于后台时,如何通过FCM消息触发onMessageReceived方法?
嘿,我刚看完你的需求,正好碰到过类似的问题!其实核心问题出在你现在发送的**组合消息(notification + data)**的处理逻辑上——FCM对这种消息的后台处理规则和你想要的不一样,咱们一步步解决:
先搞清楚FCM两种消息的核心差异
FCM消息分两类,它们在不同应用状态下的行为完全不同:
- 纯通知消息:后台/应用关闭时,系统直接弹通知,不会触发
onMessageReceived;只有应用在前台时,才会走到这个方法。 - 纯数据消息:不管应用是前台、后台还是完全关闭,都会触发
onMessageReceived,所有逻辑都得你自己写(包括弹通知)。 - 组合消息:后台/关闭时,系统弹通知,
data字段会附在通知的跳转Intent里,但不会触发onMessageReceived;只有前台时才会触发方法,拿到完整的消息内容。
你的需求是后台时既要弹通知又要触发onMessageReceived,那组合消息肯定满足不了,得改成纯数据消息,把原本notification里的内容都放到data字段里,然后在onMessageReceived里自己控制逻辑。
第一步:修改FCM消息发送结构
把原本的notification字段删掉,把标题、内容、渠道ID这些都塞进data里,比如:
message = { 'message': { 'token': app_token, 'android': { 'priority': 'HIGH', # 高优先级保证消息及时送达 'data': { 'title': msg_title, 'body': msg_body, 'color': ColorTable[msg_level], 'channel_id': ChannelTable[msg_level], 'value1': 'dummy' } } }}
第二步:在Android端处理不同场景
在你的FirebaseMessagingService的onMessageReceived方法里,先判断应用当前的状态,再分别处理:
1. 先写个工具类判断应用是否在前台
这个工具类能帮我们准确识别应用状态:
class AppForegroundChecker private constructor(private val context: Context) { private val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager fun isAppInForeground(): Boolean { val appProcesses = activityManager.runningAppProcesses ?: return false for (processInfo in appProcesses) { if (processInfo.processName == context.packageName && processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { return true } } return false } companion object { @Volatile private var INSTANCE: AppForegroundChecker? = null fun getInstance(context: Context): AppForegroundChecker { return INSTANCE ?: synchronized(this) { val instance = AppForegroundChecker(context.applicationContext) INSTANCE = instance instance } } } }
2. 在onMessageReceived里处理逻辑
override fun onMessageReceived(remoteMessage: RemoteMessage) { super.onMessageReceived(remoteMessage) // 从data里取出消息内容 val msgTitle = remoteMessage.data["title"] val msgBody = remoteMessage.data["body"] val channelId = remoteMessage.data["channel_id"] val colorHex = remoteMessage.data["color"] // 判断应用是否在前台 val isForeground = AppForegroundChecker.getInstance(this).isAppInForeground() if (isForeground) { // 前台状态:只处理UI日志,比如用LiveData通知界面更新 Log.d("FCM", "前台收到消息:$msgBody") // 这里可以通过LiveData、EventBus等方式把消息传给UI组件显示 } else { // 后台/应用关闭状态:先弹系统通知,再处理日志(比如存到本地数据库,等用户打开应用再同步到UI) // 构建跳转Intent,点击通知打开应用主界面 val launchIntent = packageManager.getLaunchIntentForPackage(packageName)?.apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK } val pendingIntent = launchIntent?.let { PendingIntent.getActivity( this, 0, it, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) } // 构建通知 val notificationBuilder = NotificationCompat.Builder(this, channelId ?: "default_channel") .setSmallIcon(R.drawable.ic_notification) // 替换成你的通知图标 .setContentTitle(msgTitle) .setContentText(msgBody) .setColor(Color.parseColor(colorHex)) .setPriority(NotificationCompat.PRIORITY_HIGH) .setContentIntent(pendingIntent) .setAutoCancel(true) // 处理Android 8.0+的通知渠道 val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( channelId ?: "default_channel", "消息通知渠道", NotificationManager.IMPORTANCE_HIGH ) notificationManager.createNotificationChannel(channel) } // 弹出通知 notificationManager.notify((Math.random() * 1000).toInt(), notificationBuilder.build()) // 记录日志,比如存到本地数据库 Log.d("FCM", "后台收到消息:$msgBody") } }
关键注意点
- 一定要设置消息优先级为
HIGH,这样即使应用完全关闭,消息也能及时触发onMessageReceived。 - 后台更新UI不能直接操作View,建议把消息存到本地数据库(比如Room),然后用LiveData监听数据库变化,等应用回到前台时自动更新UI。
- 通知渠道必须提前创建,否则Android 8.0及以上版本不会显示通知。
这样调整后,就能完全满足你的三个需求了:
- 应用完全关闭:触发
onMessageReceived,弹出系统通知; - 应用后台:触发
onMessageReceived,弹出通知同时处理日志; - 应用前台:只触发
onMessageReceived处理日志。
备注:内容来源于stack exchange,提问作者Marus Gradinaru
相关产品推荐
相关产品推荐

