如何兼容 FCM/APNs Token 直连推送
适用场景
如果您的 App 符合以下情况,本文适用于您:
- App 已直接对接 Google FCM 和 iOS APNs,自身持有 FCM Token 与 APNs Token,按原生推送协议自主推送;
- 历史版本 App 未集成 EngageLab AppPush SDK,大量存量用户没有 EngageLab 的
registration_id,无法通过 创建推送 API 触达; - 希望在不强制升级 App 的前提下,先把这些存量用户纳入 EngageLab 推送体系,再逐步迁移到 SDK。
方案概览
EngageLab 为这一场景提供 设备注册 API:服务端直接提交已持有的 FCM Token 或 APNs Device Token,EngageLab 为其生成唯一关联的 registration_id。之后即可像 SDK 注册用户一样,通过创建推送 API 按 registration_id 下发消息,并使用标签、别名、统计等基于 registration_id 的能力。
flowchart LR
token["您已持有的<br/>FCM / APNs Token"]
register["设备注册 API<br/>/v4/devices/token/registration_id"]
regId["registration_id"]
push["创建推送 API<br/>/v4/push"]
device["用户设备收到通知"]
token --> register --> regId --> push --> device接入步骤:
- 服务端调用设备注册 API,按平台批量提交 Token(单次 1~500 个),保存返回的
registration_id与 Token 的映射; - 推送时在创建推送 API 的
to.registration_id中填入目标用户的registration_id; - Token 发生变化(FCM
onNewToken、APNs 重新注册)时,重新调用设备注册 API 获取新的registration_id。
接入后一个常见问题是:用户在手机上点击通知后,App 会打开首页,还是能直接进入订单详情等目标页面?下文以 Android FCM 为例说明。
以下「通知点击跳转」部分仅覆盖 Android 原生 FCM 通知 场景。iOS(APNs Token 注册)的点击跳转不在本文讨论范围内。
通知点击跳转:结论
可以实现目标页跳转,但不是注册了 registration_id 之后就自动具备。
- 默认情况下,点击通知只会打开 App;
- 具体进入哪个页面,由您的 App 自身的点击处理逻辑决定;
- 若要跳转到目标页,需要服务端在推送请求中指定
intent.url,并由您的 App 提供能够响应该 action 的 Activity。
设备注册 API 只负责建立 FCM Token 与 registration_id 的映射关系,不会给您的 App 增加任何页面跳转能力;这类用户也不会走依赖 EngageLab SDK 的通知点击中转逻辑。因此这是一种过渡期方案,长期仍建议迁移到 EngageLab AppPush SDK,详见文末「长期建议」章节。
默认行为:不配置点击动作
当推送请求中没有配置 notification.android.intent 时,EngageLab 下发给 FCM 的通知不携带 click_action。此时的点击表现完全遵循 FCM 的默认行为:
- App 在后台或已被杀掉时,通知由 FCM 自动展示,点击后打开 App 的启动 Activity(即
AndroidManifest.xml中声明为MAIN/LAUNCHER的 Activity); - 最终用户看到的是首页、登录页,还是恢复到之前停留的页面,取决于您的 App 的启动逻辑和当前任务栈状态。
因此「默认打开 App」并不等价于「一定回到首页」。详见 Firebase 官方说明:在 Android 应用中接收消息。
实现目标页跳转:intent.url → click_action
映射链路
EngageLab 服务端会把创建推送 API 中 notification.android.intent.url 的值,映射为 FCM 通知的 android.notification.click_action 字段,再由 Android 系统在您的 App 中查找能够匹配该 action 的 Activity。
flowchart LR
pushApi["创建推送 API<br/>notification.android.intent.url"]
fcm["FCM 消息<br/>android.notification.click_action"]
activity["您的 App<br/>匹配该 action 的 Activity"]
target["业务目标页<br/>(如订单详情)"]
pushApi --> fcm --> activity --> targetclick_action 的官方定义见 FCM AndroidNotification。
步骤一:服务端在推送请求中指定 intent.url
按 registration_id 推送时,在 notification.android 中增加 intent.url,并通过 extras 携带业务参数:
curl -X POST https://pushapi-sgp.engagelab.com/v4/push \
-u "appKey:masterSecret" \
-H "Content-Type: application/json" \
-d '{
"from": "push",
"to": {
"registration_id": ["13065ffa4e1a6cc91c3"]
},
"body": {
"platform": "android",
"notification": {
"android": {
"title": "订单已发货",
"alert": "您的订单 20260911001 已发货,点击查看物流",
"intent": {
"url": "intent:#Intent;action=com.example.app.ORDER_DETAIL;component=com.example.app/com.example.app.OrderDetailActivity;end"
},
"extras": {
"order_id": "20260911001",
"page": "order_detail"
}
}
}
},
"request_id": "order-shipped-20260911001"
}'
关于 intent.url 的取值,需要注意:
- 它是 Android Intent URI 格式(
intent:#Intent;...;end),不是随意填写的网页地址; action是您的 App 自定义的动作名称,component是包名/Activity 全类名;- 同一
url中action与component可以只填其一,但建议同时填写,以便精确命中目标 Activity; - 更多取值类型(打开首页、Deeplink 等)见创建推送 API 中 android 通知字段 的
intent说明。
步骤二:您的 App 提供匹配的 Activity
在 AndroidManifest.xml 中,为目标 Activity 声明与 intent.url 中 action 一致的 intent-filter:
<activity
android:name=".OrderDetailActivity"
android:exported="true">
<intent-filter>
<action android:name="com.example.app.ORDER_DETAIL" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
FCM 展示通知并被点击后,会以该 action 启动对应 Activity,并把通知中的 extras 键值放入 Intent 的 extras。目标 Activity 中读取参数并完成业务路由:
class OrderDetailActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val orderId = intent.getStringExtra("order_id")
if (orderId.isNullOrEmpty()) {
// 缺少参数时回退到订单列表或首页
startActivity(Intent(this, MainActivity::class.java))
finish()
return
}
showOrderDetail(orderId)
}
}
若您的 App 已经有统一的路由中转页(例如所有通知点击都先进入 PushRouterActivity,再根据参数分发到具体页面),可以把 intent.url 的 component 指向该中转页,由中转页读取 extras 后再跳转到订单详情等业务页面,无需为每个业务页面单独声明 action。
注意事项
- 是否需要集成 EngageLab SDK: 不需要。上述链路只依赖 FCM 的
click_action机制和您的 App 自身的 Activity 声明。 - 已有 click_action 处理可复用: 如果您的 App 之前直接使用 FCM 推送时已经实现了
click_action处理逻辑,只需把原来的 action 值填入intent.url即可复用。 - 没有 click_action 处理需要补充: 若您的 App 目前只依赖默认打开行为,需要按「步骤二」增加 Activity 声明和参数解析逻辑,否则即使服务端配置了
intent.url,系统也找不到可响应的 Activity。 - 业务参数通过 extras 传递:
intent.url用于决定「打开哪个页面」,页面内展示什么内容建议通过extras传递(如order_id),由目标 Activity 自行解析。 - App 在前台时的表现: App 在前台时,FCM 不会自动展示通知,而是回调到您的 App 的
onMessageReceived,是否展示通知以及点击后如何跳转均由您的 App 自行实现。 - 验证方法: 联调时可先用
intent:#Intent;action=android.intent.action.MAIN;end确认通道链路正常,再替换为业务 action 验证目标页跳转。
长期建议:尽快迁移到 EngageLab AppPush SDK
设备注册 API 定位于兼容过渡方案,用于让历史版本 App 的存量用户在未集成 SDK 的情况下也能被触达,并生成关联的 registration_id,为后续接入 EngageLab AppPush 的其他能力做准备。它解决的是「能不能推到」的问题,而不是替代 SDK。
我们建议您把它作为迁移策略的一部分,而不是长期方案:
- 新版本 App 集成 EngageLab AppPush SDK: 新用户通过 SDK 标准注册获得
registration_id,直接享有完整的通知展示与点击处理能力。 - 旧版本 App 用直连 Token 过渡: 存量用户通过设备注册 API 注册的
registration_id继续推送,两套体系可以共存,不需要强制合并。 - 随版本更新逐步收敛: 随着用户升级到集成 SDK 的版本,直连 Token 用户会自然减少,最终统一到 SDK 体系。
相比本文介绍的直连方式,集成 SDK 后:
- 通知点击跳转由 SDK 统一处理,
intent.url支持的三种类型(指定 Activity、应用首页、Deeplink)无需您在 App 侧逐个声明 action 与解析逻辑; - 前台通知展示、通知栏样式(
builder_id、style、channel_id等)、角标、消息折叠等字段才能完整生效; - SDK 会自动上报设备信息与点击等行为,控制台的送达、点击统计更完整。
集成方式请参考 Android SDK 集成指南 和 iOS SDK 集成指南。










