4. FCM 后台通知点击路由解析
后台通知点击的 Intent 数据链路
当应用处于后台时,FCM 通知由系统托盘展示,点击后系统会启动应用主入口 Activity,并把通知携带的 data 以 Intent extras 形式注入。与前台 onMessageReceived 手动构建 PendingIntent 不同,后台点击的 Intent 由 FCM SDK 与系统共同组装,路由解析必须从 getIntent().getExtras() 中读取。
关键约定:后台通知的 data 键值会原样进入 Intent extras,但 google.message_id、from、collapse_key 等由 SDK 附加;自定义业务字段(如 type、target_id)需在服务端 payload 的 data 中显式声明。冷启动与热启动的差异在于:冷启动时 Activity 首次创建,Intent 即携带数据;热启动时 Activity 已存在,新 Intent 通过 onNewIntent 送达,必须调用 setIntent(intent) 更新,否则后续读取会拿到旧数据。
路由解析实现
在 MainActivity 中统一处理冷启动与热启动,核心是提取 extras 后交给路由分发器。以下代码可直接放入项目运行,RouteDispatcher 根据 type 字段决定跳转目标。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 冷启动:首次创建时解析
handleNotificationIntent(intent)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
// 热启动:必须更新 Intent,否则 getIntent() 仍是旧数据
setIntent(intent)
handleNotificationIntent(intent)
}
private fun handleNotificationIntent(intent: Intent?) {
val extras = intent?.extras ?: return
val type = extras.getString("type") ?: return
val targetId = extras.getString("target_id")
when (type) {
"order_detail" -> {
startActivity(Intent(this, OrderDetailActivity::class.java).apply {
putExtra("order_id", targetId)
})
}
"promotion" -> {
startActivity(Intent(this, PromotionActivity::class.java).apply {
putExtra("promo_id", targetId)
})
}
else -> {
// 未知类型,留在主页
}
}
}
}
热启动陷阱:若 onNewIntent 中不调用 setIntent(intent),后续任何 getIntent() 调用都会返回启动时的旧 Intent,导致第二次点击通知时路由到错误页面或丢失数据。这是后台通知路由最常见的 bug。
冷启动与热启动的验证方法
用 adb 模拟两种场景,观察 Logcat 中路由日志确认行为正确:
- 冷启动验证:先
adb shell am force-stop 包名杀掉进程,再通过 FCM 控制台发送带type=order_detail的通知,点击后应进入OrderDetailActivity且order_id=A1024。 - 热启动验证:保持应用在后台(按 Home 键),发送
type=promotion的通知,点击后应进入PromotionActivity;再发送type=order_detail的通知,点击后应正确切换到OrderDetailActivity,验证setIntent生效。
调试技巧:在 handleNotificationIntent 入口打印 Log.d("Route", "extras=$extras"),可直观看到冷/热启动时 extras 的差异。若热启动时 extras 为空或为旧值,检查 onNewIntent 是否被正确覆写且调用了 setIntent。