FCM 实战:消息结构、数据与通知
Firebase Cloud Messaging (FCM) 实战:消息结构、数据与通知
什么是 FCM?
Firebase Cloud Messaging (FCM) 是 Google 提供的跨平台消息传递解决方案,让你能免费、可靠地将消息发送到 Android、iOS 和 Web 应用。无论是推送通知、即时消息同步还是后台数据刷新,FCM 都是首选工具。
在 FCM 中,消息分为两种核心类型:通知消息 和 数据消息。理解它们的结构与行为差异,是构建稳定推送逻辑的基础。
FCM 消息的基本结构
一条 FCM 消息本质是一个 JSON 对象,通过 HTTP/2 协议或 Firebase Admin SDK 发送。最外层字段如下:
{
"message": {
"token": "设备注册令牌",
"notification": { ... },
"data": { ... },
"android": { ... },
"apns": { ... },
"webpush": { ... }
}
}
token:目标设备的 FCM 注册令牌(单一设备)。topic:发送到订阅了某个主题的设备(可选,与token二选一)。condition:多主题条件表达式,如"TopicA" in topics && ("TopicB" in topics || "TopicC" in topics)(可选)。notification:通知消息体,由系统自动处理显示。data:数据消息体,由应用自行处理。android/apns/webpush:平台特定配置,可覆盖通知行为、定制铃声、图片等。
基础用法:只需关心
token(或topic)、notification和data,其他字段可按需使用。
通知消息(Notification Message)
通知消息由 FCM SDK 自动将内容展示为系统通知栏中的一条标准通知。你无需编写任何展示代码,适合快速发送提醒。
主要字段
"notification": {
"title": "新消息",
"body": "你收到了一条新评论",
"image": "https://example.com/icon.png"
}
title:通知标题(必填)。body:通知内容文本(必填)。image:在通知中展示的图片 URL(Android 支持大图,iOS 支持富媒体推送)。
行为特点
- 前台/后台:当应用在前台时,系统不会自动弹出通知,需在
onMessageReceived中手动处理;后台时则由系统托盘自动显示。 - 直接使用 FCM 控制台:无需写代码,只需填写标题和正文,即可快速下发。
📌 注意:仅靠通知消息无法携带自定义数据,若点击通知后需跳转到指定页面,务必发送通知+数据组合消息。
数据消息(Data Message)
数据消息完全由客户端代码处理,系统不显示任何通知。适合静默同步数据、刷新界面、触发后台任务等场景。
主要字段
"data": {
"type": "chat",
"conversationId": "abc123",
"sender": "Alice"
}
- 所有键值对都是字符串类型。
- 键名避免使用 FCM 保留字段(如
from、google.开头等)。 - 最大总大小 4KB(适合轻量数据,大体积内容应传递资源 ID 后再请求)。
行为特点
- 始终触发
onMessageReceived:无论应用在前台还是后台,都会回调该方法,由开发者决定如何反应。 - 后台数据消息会唤醒应用短暂处理(Android 10+ 有后台执行限制),适合小量工作。
- 可自行构造本地通知,实现点击跳转等复杂逻辑。
通知 + 数据组合消息
在实际项目中,最常见的做法是同时携带 notification 和 data,让消息既有自动显示的便利,又能携带自定义数据用于点击跳转。
{
"message": {
"token": "your-device-token",
"notification": {
"title": "新消息",
"body": "你收到了一条私信"
},
"data": {
"screen": "chat",
"userId": "U12345"
}
}
}
不同状态下的接收行为
| 应用状态 | 通知显示 | data 获取方式 |
|---|---|---|
| 前台 | 不自动显示,你需要在 onMessageReceived 中自行处理 |
remoteMessage.getData() 直接获取 |
| 后台 | 系统自动显示通知 | 点击通知后通过 intent.getExtras()(Android)或 launchOptions(iOS/Web)获取 |
🔍 关键点:在后台收到组合消息时,
data不会传递给onMessageReceived,而是附加在点击通知打开的 Activity 的Intent中。因此务必在启动 Activity 中处理data负载。
实战案例:通过 Firebase 控制台发送组合消息
- 打开 Firebase 控制台 > 你的项目 > Messaging。
- 点击“发送您的第一条消息”。
- 选择“通知消息”类型,填写通知标题和正文。
- 点击“添加自定义数据”,输入键值对(例如
screen = "chat",userId = "U12345")。 - 目标选择“User segment”或“Topic”,也可以输入设备令牌测试。
- 点击“审阅”后直接发送。
收到的消息会自动弹出通知,点击后你可以在 Activity 中获取 data 并处理跳转。
客户端代码处理(Android 示例)
1. 继承 FirebaseMessagingService 接收消息
public class MyFirebaseMessagingService extends FirebaseMessagingService {
@Override
public void onMessageReceived(@NonNull RemoteMessage remoteMessage) {
// 检查是否包含 data
if (remoteMessage.getData().size() > 0) {
String screen = remoteMessage.getData().get("screen");
// 在前台时可自定义创建通知,或在后台时处理逻辑
// 注意:应用在后台时此方法不会被调用,数据需通过 Launcher Activity 获取
}
}
}
2. 在 Launcher Activity 中获取后台消息数据
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getIntent().getExtras() != null) {
for (String key : getIntent().getExtras().keySet()) {
Object value = getIntent().getExtras().get(key);
Log.d("FCM Data", "Key: " + key + " Value: " + value);
}
String screen = getIntent().getExtras().getString("screen");
// 根据 screen 跳转对应界面
}
}
3. 使用 Firebase Admin SDK 发送(Node.js 示例)
const admin = require("firebase-admin");
admin.initializeApp({
credential: admin.credential.applicationDefault()
});
const message = {
token: "设备令牌",
notification: {
title: "新评论",
body: "有人回复了你的帖子"
},
data: {
postId: "123",
commentId: "456"
}
};
admin.messaging().send(message)
.then(response => console.log("成功:", response))
.catch(error => console.error("失败:", error));
常见问题与最佳实践
❓ 为什么后台收到通知时,onMessageReceived 没有被调用?
这是预期行为。后台通知消息由系统托盘接管,不会回调该方法。只有前台消息或纯数据消息才会触发 onMessageReceived。要获取后台通知里携带的 data,必须从启动 Activity 的 Intent 中读取。
❓ 如何让通知点击后打开指定页面?
一定要在消息中加入 data 字段,并在目标 Activity 中解析这些数据实现跳转。不要依赖 notification 的 click_action(因平台限制很多)。
❓ 数据消息和通知消息选哪个?
- 只需显示通知 → 纯通知消息
- 需要静默同步或自定义显示逻辑 → 数据消息
- 既显示通知又要携带点击逻辑 → 组合消息(推荐)
❓ 消息大小有什么限制?
整个消息负载(包括所有字段)不得超过 4,096 字节。如果 data 中的内容较大,考虑传递资源 ID,让客户端再通过 API 获取完整信息。
❓ 如何避免重复通知或丢失数据?
- 不要在
onMessageReceived中再次发送系统通知(避免前台重复显示)。 - 使用 Firebase 的
collapse_key参数让多条同类消息只保留最新一条。 - 设计幂等的
data处理逻辑,防止网络重试导致重复操作。
总结
FCM 的消息结构清晰且灵活:通知消息让推送变得极其简单,数据消息赋予你完全的控制权,而两者的结合则是实际应用中的黄金组合。掌握前台/后台的接收差异、正确解析 data 负载、遵守大小限制,就能构建出稳定、不打扰用户的推送体验。
从 Firebase 控制台到 Admin SDK,再到客户端接收,整条链路现在你已经完整贯通。快去动手发送你的第一条组合消息吧!