3. FCM 前台通知展示与点击跳转
前台消息为什么必须自己发通知
FCM 的消息分两类:通知消息(notification)和数据消息(data)。当应用处于前台时,通知消息不会自动进入系统托盘,而是把整条 RemoteMessage 交给 onMessageReceived();只有应用在后台(或被杀)时,带 notification 字段的消息才由系统直接展示。因此“前台也能看到通知”这件事,必须由我们在 onMessageReceived() 里手动构建 NotificationCompat 并发出。
点击跳转的关键是 PendingIntent:它把“点击通知”这个动作延迟绑定到一个 Intent 上,由系统在用户点击时以我们的应用身份触发。要跳转到指定 Activity,需要显式 Intent + FLAG_IMMUTABLE(Android 12+ 强制要求可变性声明)+ 唯一 requestCode,避免多条通知互相覆盖。
实现步骤
- 在
AndroidManifest.xml中注册继承FirebaseMessagingService的服务,并声明目标 Activity。 - 重写
onMessageReceived(),从message.data取标题、正文和跳转参数。 - 创建通知渠道(Android 8.0+ 必须),再构建
NotificationCompat.Builder。 - 用显式 Intent 构造
PendingIntent,设置FLAG_IMMUTABLE与唯一 requestCode。 - 调用
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 时必需)。