3. FCM 消息类型解析

FCM 定义了三种消息传递模型:通知消息(Notification Message)、数据消息(Data Message)和混合消息(Combined Message)。理解它们的负载结构与进程行为是正确实现后台任务唤醒的关键。本章从负载格式、系统分发机制、进程存活状态下的行为三个维度进行深度解析。

3.1 三种消息的负载结构

消息类型 负载键 系统处理方式 前台/后台行为差异 进程被杀死后
通知消息 notification + 可选 data 系统自动创建通知栏,
不触发 onMessageReceived
前台:直接显示;后台:显示 + 可选轻量处理 系统重新创建应用后仅显示通知
数据消息 仅 data,无 notification 应用必须自行处理,
始终触发 onMessageReceived
前台/后台均触发回调,可启动 Service 若被杀死则无法触发回调(无唤醒能力)
混合消息 notification + data 同时存在 系统显示通知,同时将 data 通过 Intent 附加到启动的 Activity 前台:直接显示 + 回调;后台:显示通知 + 附加 data 用户点击通知后启动 Activity 可获得 data

3.2 典型负载 JSON 示例

以下展示三种消息的完整 Payload 结构,用于服务端发送时的格式参考:

// 通知消息(Notification Only)
{
  "to": "device_",
  "notification": {
    "title": "新消息",
    "body": "您有一条未读通知",
    "sound": "default"
  }
}

// 数据消息(Data Only)
{
  "to": "device_",
  "data": {
    "type": "silent_sync",
    "payload": "{\"task_id\": 123}",
    "priority": "high"
  }
}

// 混合消息(Notification + Data)
{
  "to": "device_",
  "notification": {
    "title": "更新提醒",
    "body": "点击查看详情"
  },
  "data": {
    "deep_link": "/detail/456",
    "badge": "1"
  }
}

关键区分:数据消息的 data 字段是 键值对必须被应用解析;通知消息的 notification 字段由系统 UI 层自动接管,应用层无需编码处理通知栏展示。

3.3 进程行为全景图

下方 SVG 从 FCM 服务器到 Android 设备,展示了三种消息在 前台运行、后台挂起、进程被杀死 三种状态下的系统分发路径与应用响应行为。

FCM 三种消息类型与进程行为关系图 FCM 消息类型与进程行为映射 FCM 服务器 通知消息 数据消息 混合消息 前台:自动显示通知 后台:系统显示 + 唤醒受限 onMessageReceived 回调 可启动 Service 启动Service 系统显示通知 点击通知→获取data 进程被杀?仅有通知消息可恢复

图解重点:绿色路径(数据消息)是唯一能在后台稳定触发 onMessageReceived 的模型,但无法在进程被杀死时唤醒;红色路径(通知消息)由系统保活,适合简单提醒;紫色路径(混合消息)适合“点击通知后获取额外数据”的场景。

3.4 进程行为深度分析

  • 前台进程:三种消息的 onMessageReceived 都正常触发(混合消息仅当应用在前台时触发回调,后台则不触发)。
  • 后台进程(挂起/缓存):通知消息由系统直接处理;数据消息会触发回调(但 Android 12+ 有特殊前台服务限制);混合消息在后台仅显示通知,不触发回调。
  • 进程被杀死:只有通知消息能重新唤醒应用(仅显示通知,不回调);数据消息彻底失效;混合消息在用户点击通知后启动 默认 Activity 并附带 data 键值对。

3.5 实现建议与常见误区

常见错误:很多开发者误以为数据消息可以像 iOS 那样“静默唤醒”被杀死进程。实际上,FCM 数据消息在 Android 8.0+ 上,当应用被杀死后 完全不会传递。必须在应用存活状态才能收到。

若需要实现“被杀死后仍能执行后台任务”,必须结合 通知消息 + 高优先级 FCM 并利用系统广播接收器(如 BOOT_COMPLETED)重新注册。更可靠的方式是使用 WorkManager 周期性任务或 前台服务 保持长连接。

// 最佳实践:在 onMessageReceived 中启动前台服务确保存活
public void onMessageReceived(RemoteMessage message) {
    if (message.getData().size() > 0) {
        Intent intent = new Intent(this, MyForegroundService.class);
        intent.putExtra("payload", message.getData().toString());
        startForegroundService(intent);
    }
}

3.6 性能与稳定性建议

  • 避免在 onMessageReceived 中执行长时间阻塞操作;使用 WorkManager 或 IntentService 异步处理。
  • 混合消息的 data 部分最大为 4KB,通知消息的 notification 字段同样受限,大数据应使用 下载任务 而非直接嵌入。
  • 针对 Android 12+,后台启动前台服务限制更严,建议在通知消息中添加 ticker 或使用 高优先级 FCM 配合 priority: 10。
  • 调试时使用 FCM 控制台或 curl 发送测试消息,并观察 adb logcat -s FCM 输出确认回调是否触发。

章节小结:通知消息适合保活,数据消息适合后台任务,混合消息是折中方案。实际项目中应根据进程唤醒需求选择类型,并辅以前台服务或 WorkManager 保证可靠性。