3. FCM 前台通知展示与点击跳转

FCM 前台通知展示与点击跳转 学习卡片

前台消息为什么必须自己发通知

FCM 的消息分两类:通知消息(notification)和数据消息(data)。当应用处于前台时,通知消息不会自动进入系统托盘,而是把整条 RemoteMessage 交给 onMessageReceived();只有应用在后台(或被杀)时,带 notification 字段的消息才由系统直接展示。因此“前台也能看到通知”这件事,必须由我们在 onMessageReceived() 里手动构建 NotificationCompat 并发出。

点击跳转的关键是 PendingIntent:它把“点击通知”这个动作延迟绑定到一个 Intent 上,由系统在用户点击时以我们的应用身份触发。要跳转到指定 Activity,需要显式 Intent + FLAG_IMMUTABLE(Android 12+ 强制要求可变性声明)+ 唯一 requestCode,避免多条通知互相覆盖。

FCM 下行消息 data + notification 应用在前台? AppLifecycleState 否 系统托盘自动展示 (非本例范围) 是 onMessageReceived(RemoteMessage) 解析 data: title / body / deeplink NotificationCompat.Builder • setSmallIcon / setContentTitle / setContentText • setAutoCancel(true) setPriority(HIGH) • setContentIntent(pendingIntent) PendingIntent.getActivity Intent → TargetActivity(显式) FLAG_IMMUTABLE | FLAG_UPDATE_CURRENT requestCode = 唯一值(防覆盖) NotificationManagerCompat.notify id = 1001(与 requestCode 对应) 用户点击 → 启动 TargetActivity Intent extras 携带 deeplink 参数

实现步骤

  1. 在 AndroidManifest.xml 中注册继承 FirebaseMessagingService 的服务,并声明目标 Activity。
  2. 重写 onMessageReceived(),从 message.data 取标题、正文和跳转参数。
  3. 创建通知渠道(Android 8.0+ 必须),再构建 NotificationCompat.Builder。
  4. 用显式 Intent 构造 PendingIntent,设置 FLAG_IMMUTABLE 与唯一 requestCode。
  5. 调用 NotificationManagerCompat.notify() 发出通知。

关键代码

// AndroidManifest.xml
<service
    android:name=".MyFirebaseMessagingService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>

<activity android:name=".TargetActivity" />
// MyFirebaseMessagingService.kt
class MyFirebaseMessagingService : FirebaseMessagingService() {

    override fun onMessageReceived(message: RemoteMessage) {
        val title = message.data["title"] ?: "默认标题"
        val body  = message.data["body"]  ?: "默认内容"
        val deeplink = message.data["deeplink"] ?: ""

        createChannelIfNeeded()

        // 显式 Intent 指向目标 Activity
        val intent = Intent(this, TargetActivity::class.java).apply {
            flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
            putExtra("deeplink", deeplink)
        }

        val pendingIntent = PendingIntent.getActivity(
            this,
            1001,                       // requestCode,唯一值
            intent,
            PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
        )

        val notification = NotificationCompat.Builder(this, CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_notification)
            .setContentTitle(title)
            .setContentText(body)
            .setAutoCancel(true)
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setContentIntent(pendingIntent)
            .build()

        NotificationManagerCompat.from(this).notify(1001, notification)
    }

    private fun createChannelIfNeeded() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                CHANNEL_ID, "推送通知", NotificationManager.IMPORTANCE_HIGH
            )
            getSystemService(NotificationManager::class.java)
                .createNotificationChannel(channel)
        }
    }

    companion object { const val CHANNEL_ID = "fcm_channel" }
}
// TargetActivity.kt —— 接收跳转参数
class TargetActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val deeplink = intent.getStringExtra("deeplink")
        // 根据 deeplink 加载对应页面
    }
}

验证方法

  • 用 Firebase 控制台或 HTTP v1 API 发送带 data 字段的消息,保持应用在前台。
  • 观察通知是否出现在系统托盘,标题与正文是否与 data 一致。
  • 点击通知,确认跳转到 TargetActivity,并在日志中打印 deeplink 值。
  • 连续发送两条不同 deeplink 的消息,确认点击各自通知跳转到对应页面(验证 requestCode 唯一性)。
Android 12(API 31)起,PendingIntent 必须显式声明 FLAG_IMMUTABLE 或 FLAG_MUTABLE,否则会抛出 IllegalArgumentException。前台通知场景使用 FLAG_IMMUTABLE 即可。
若通知点击后无反应,优先检查:① 目标 Activity 是否在 Manifest 中注册;② requestCode 是否与其他通知冲突;③ 是否遗漏 FLAG_ACTIVITY_NEW_TASK(从 Service 启动 Activity 时必需)。