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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.16 08:23:13