十年前,推送通知的架构很简单——你的服务器把 JSON 打包,丢给 APNs 或 FCM 的端点,手机亮一下,用户点开。传输层只管投递,不关心内容。但 Jacques Corby-Tuech 最近的分析指出:这条管道正在被重新定义。Apple 和 Google 不再只做"邮差",它们正在成为品牌与用户之间的主动中介——筛选、重组、甚至用 AI 概括你发出的每一条消息。
这个转变和电子邮件的遭遇如出一辙:SMTP 本是开放协议,但 Gmail 的优先级收件箱、智能分类、Promotions 标签,已经让发件人失去了对呈现方式的控制。推送通知正在走同一条路。
从传输层到中介层:发生了什么
传统推送流程是线性的:
你的业务服务器 → APNs / FCM → 用户设备 → 通知中心展示
你控制标题、正文、图标、点击行为。平台只负责加密投递和送达确认。
但近两年,两端都在加"中间逻辑":
- Apple:iOS 16 引入 Live Activities;iOS 18 加入通知摘要(AI 将多条通知合并成一句话);通知分组规则由系统自动决定;App 的推送需要通过"重要通知"审核才能突破默认静默。
- Google:Android 13 起要求运行时通知权限(POST_NOTIFICATIONS);FCM 控制台提供"主题订阅"和 A/B 测试分发能力;Pixel 设备上的通知摘要同样由 Gemini 驱动。
平台不再只是"把你的 JSON 送到设备"。它在读你的内容、改你的呈现、替用户做过滤决策。
中介化的三个具体维度
1. 内容被 AI 重写
iOS 18 的通知摘要功能会把你精心写的"您的订单已发货,预计周三送达"压缩成"2 条订单更新"。你花了时间设计的文案,用户看到的可能是 AI 的一句话概括。品牌语调、行动引导(CTA)、紧急程度——全部被抹平。
2. 送达权被平台审核
Apple 的"Critical Alerts"(重要通知) entitlement 需要申请并审核。没有它,你的紧急通知在专注模式下会被静默。Google 的 POST_NOTIFICATIONS 权限让用户可以一键关掉所有推送——而你的 App 没有任何二次触达通道。
3. 分组与排序由系统决定
通知分组规则从开发者手中转移到操作系统。Android 的通知渠道(Notification Channels)和 iOS 的分组线程(threadIdentifier)给了开发者一些配置空间,但最终排序、折叠、摘要逻辑由平台算法决定。你的"限时优惠"可能被塞进一个叫"促销"的折叠组,用户永远不展开。
实际影响:一条推送的完整生命周期
下面用代码展示当前推送的完整发送流程,你可以看到平台在哪些环节插入了中介逻辑:
FCM 示例:发送一条带分类的推送
# fcm_push_demo.py
# 运行前:pip install firebase-admin
# 需要下载 Firebase 项目的服务账号 JSON 文件
import firebase_admin
from firebase_admin import credentials, messaging
# 1. 初始化——平台已经在这里介入:你的项目必须通过 Firebase 注册
cred = credentials.Certificate("service-account-key.json")
firebase_admin.initialize_app(cred)
# 2. 构造消息——注意 notification 和 data 的区别
# notification 字段的内容会被系统自动展示,受摘要/分组规则控制
# data 字段由你的 App 自行处理,但不会在通知中心直接显示
message = messaging.Message(
notification=messaging.Notification(
title="您的订单已发货",
body="预计周三送达,点击查看物流详情",
),
data={
# 这些数据你的 App 可以在后台读取,但用户在通知中心看不到原文
"order_id": "ORD-20250618-001",
"tracking_url": "https://track.example.com/ORD-20250618-001",
"urgency": "high", # 自定义字段,但平台不认——它有自己的优先级算法
},
android=messaging.AndroidConfig(
# 3. Android 通知渠道——你必须预先在 App 中注册渠道
# 用户可以在系统设置中关闭某个渠道,你的推送就永远送达不了
notification_channel_id="order_updates",
priority="high",
),
apns=messaging.APNSConfig(
payload=messaging.APNSPayload(
aps=messaging.Aps(
# 4. iOS threadIdentifier 决定分组,但摘要逻辑由系统 AI 控制
thread_id="order_updates",
# 5. 如果要突破专注模式,需要 Critical Alerts entitlement
# 普通推送在专注模式下会被静默
# sound="default", # 普通声音
),
),
),
token="<目标设备的 FCM 注册令牌>",
)
# 6. 发送——FCM 可能因为用户关闭权限而返回错误
# 你无法知道通知最终是否被用户看到
try:
response = messaging.send(message)
print(f"发送成功,消息 ID: {response}")
except messaging.UnregisteredError:
print("设备令牌已失效——用户可能卸载了 App 或关闭了通知权限")
except messaging.SenderIdMismatchError:
print("发送者 ID 不匹配——你的服务账号不属于目标项目")
关键观察点标注在注释里。每一步,平台都在做决策:渠道是否开启、分组如何折叠、摘要怎么压缩、专注模式下是否放行。开发者能控制的范围正在缩小。
用 curl 直接调用 APNs:看到更裸的传输层
如果你绕过 FCM,直接调用 APNs 的 HTTP/2 端点,传输层的本质更清晰——但中介逻辑依然在设备端生效:
# 直接调用 APNs(需要 .p8 token 签名,此处仅展示请求结构)
# 实际运行需要:openssl 生成 JWT,curl 支持 HTTP/2
# 生成 JWT token(简化示例,生产环境应使用库)
JWT_HEADER=$(echo -n '{"alg":"ES256","kid":"YOUR_KEY_ID"}' | base64)
JWT_CLAIM=$(echo -n '{"iss":"YOUR_TEAM_ID","iat":'$(( $(date +%s) ))'}' | base64)
# 发送推送
curl -v \
--http2 \
-H "authorization: bearer $JWT_HEADER.$JWT_CLAIM.$JWT_SIGNATURE" \
-H "apns-topic: com.yourapp.bundleid" \
-H "apns-push-type: alert" \
-d '{"aps":{"alert":{"title":"订单已发货","body":"预计周三送达"},"thread-id":"order_updates"}}' \
https://api.push.apple.com/3/device/<DEVICE_TOKEN>
APNs 的 API 本身是纯粹的传输——你发 JSON,它投递。但到达设备后,iOS 的通知引擎会接管:分组、摘要、专注模式过滤。中介化不在传输协议里,而在操作系统的通知框架里。
开发者可以做什么:一份应对清单
中介化是不可逆的趋势。与其抱怨平台"抢了控制权",不如调整策略:
| 维度 | 当前风险 | 应对思路 |
|---|---|---|
| AI 摘要抹平文案 | 品牌语调消失 | 把关键信息放在 data 字段,App 内展示完整内容;通知只做"钩子" |
| 通知权限被用户一键关闭 | 失去唯一触达通道 | 建立 App 内消息中心 + 邮件 + SMS 的多通道冗余 |
| 专注模式静默普通推送 | 紧急消息无法送达 | 申请 Critical Alerts entitlement(Apple)或使用 full-screen intent(Android) |
| 分组折叠降低可见性 | 营销推送被永久折叠 | 减少低价值推送频率,让每条通知都值得单独展示 |
| 渠道管理权在用户 | 单个渠道被关闭 | 在 App 设置中引导用户管理渠道偏好,而非让用户去系统设置里盲关 |
最小可行策略:把推送当成"钩子"而非"完整消息"
# 改造后的推送策略:通知只做提醒,完整内容在 App 内展示
message = messaging.Message(
notification=messaging.Notification(
# 简短标题——即使被 AI 概括,核心信息"有新消息"仍然保留
title="订单更新",
body="查看详情", # 不在通知正文放关键信息,因为可能被摘要吞掉
),
data={
# 完整内容在这里,App 打开后自行渲染
"type": "order_shipped",
"order_id": "ORD-20250618-001",
"estimated_delivery": "2025-06-21",
"tracking_url": "https://track.example.com/ORD-20250618-001",
"full_message": "您的订单 ORD-20250618-001 已发货,预计6月21日送达。点击查看实时物流。",
},
android=messaging.AndroidConfig(
notification_channel_id="order_updates",
),
apns=messaging.APNSConfig(
payload=messaging.APNSPayload(
aps=messaging.Aps(
thread_id="order_updates",
mutable_content=1, # 允许 App 修改通知内容(iOS 10+)
),
),
),
token="<DEVICE_TOKEN>",
)
核心思路:通知正文只承担"来 App 里看"的钩子功能,所有品牌化内容、CTA、详细信息都放在 data 字段中由 App 自行展示。这样即使 AI 把通知摘要成"3 条更新",用户点进 App 后仍然能看到你设计的完整体验。
写在最后
推送通知的中介化和电子邮件的中介化有一个关键区别:邮件至少还有 SMTP 这个开放协议做底层,你可以自建服务器、绕过 Gmail。但推送通知的传输层本身就是封闭的——APNs 和 FCM 是唯一通道,没有替代方案。
这意味着开发者的议价能力比邮件时代更弱。平台的每一个新功能——AI 摘要、分组算法、权限模型——都是不可选择、不可绕过的。你能做的不是对抗中介,而是在中介的规则下设计最优的信息架构:推送做钩子,App 内做完整体验,多通道冗余保送达。
下一次你在写推送文案时,先问自己一个问题:如果这条通知被 AI 压缩成两个字,用户还能知道该做什么吗? 如果答案是不行,你的信息架构就需要重新设计。