推送渠道 - iOS 与安卓的推送差异
第一个真正的问题
入职第二周,老王找到我:“iOS 用户投诉说收不到推送,特别是晚上发的消息。”
我查了一下推送日志:
2026-03-31 22:30:15 [ERROR] APNs push failed: Invalid token
2026-03-31 22:30:16 [ERROR] APNs push failed: Invalid token
2026-03-31 22:30:17 [ERROR] APNs push failed: Invalid token
...
“Invalid token?” 我问老王。
“可能是用户卸载了 App,或者 token 过期了。”
“我们用的什么推送服务?”
“极光推送,统一封装了 iOS 和安卓。”
我打开极光的文档,发现一个问题:
极光推送的工作原理:
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 我们的 │────▶│ 极光推送 │────▶│ APNs │
│ 服务器 │ │ 服务器 │ │ (Apple) │
└──────────┘ └──────────┘ └──────────┘
│
▼
┌──────────┐
│ 安卓厂商 │
│ 推送服务 │
└──────────┘
极光推送作为中间层,帮我们对接了各家推送服务。但这也意味着:
- 我们无法直接控制推送参数
- 推送失败时,只能看到极光返回的错误,看不到真正的原因
- 调试困难
iOS 推送:APNs
我决定深入了解一下 APNs。
Apple Push Notification service(APNs)是苹果官方的推送服务。所有 iOS App 的推送,都必须经过 APNs。
APNs 的工作流程:
┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐
│ iOS App │────▶│ APNs │────▶│ iOS 设备 │────▶│ 用户 │
│ (我们的) │ │ (Apple) │ │ │ │ │
└────────────┘ └────────────┘ └────────────┘ └────────────┘
│ ▲
│ │
▼ │
┌────────────┐ ┌────────────┐
│ 我们的 │────▶│ APNs │
│ 服务器 │ │ │
└────────────┘ └────────────┘
- App 在用户设备上注册推送
- APNs 返回一个 device token
- App 把 token 发给我们的服务器
- 我们的服务器用 token 通过 APNs 发送推送
关键概念:Device Token
Device token 是 APNs 分配给每个设备的唯一标识。它不是设备 ID,也不是用户 ID,而是 APNs 内部使用的标识。
Device token 可能变化:
- 用户卸载重装 App
- 用户恢复设备
- 用户升级系统
- Apple 定期更新 token
所以,我们需要定期更新 token,并在推送失败时检测 token 是否失效。
Token 失效处理
我查看了当前的 token 存储方式:
数据设计要点
- 核心是在
user_devices里保存业务事实,而不是把规则散落在应用逻辑里。- 索引服务于高频查询,重点是缩小扫描范围,而不是堆更多字段。
“这张表有个问题,” 我告诉老王,“同一个用户可能有多台设备,但我们没有处理 token 失效的情况。“
APNs 的反馈服务
APNs 提供了一个反馈服务(Feedback Service),会告诉我们哪些 token 已经失效。
我写了一个脚本,定期查询失效 token:
部署后,失效 token 问题减少了 80%。
安卓推送:更复杂的世界
iOS 只有一个 APNs,但安卓呢?
由于国内无法使用 Google 服务(包括 FCM - Firebase Cloud Messaging),各家手机厂商都有自己的推送服务:
| 厂商 | 推送服务 | 特点 |
|---|---|---|
| 小米 | Mi Push | 系统级推送,到达率高 |
| 华为 | HMS Push | 需要华为开发者账号 |
| OPPO | OPPO Push | 需要认证 |
| vivo | vivo Push | 较新的服务 |
| 其他 | 无系统推送 | 需要自建推送通道 |
问题:我们需要对接多家推送服务!
我调研了一下,发现有两种方案:
方案 A:统一推送平台
使用极光、个推等第三方服务,他们封装了各家厂商推送:
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 我们的 │────▶│ 统一推送 │────▶│ 各厂商 │
│ 服务器 │ │ 平台 │ │ 推送 │
└──────────┘ └──────────┘ └──────────┘
优点:
- 一套 API,接入简单
- 维护成本低
缺点:
- 推送到达率受平台影响
- 成本较高(按推送量收费)
- 无法精细控制
方案 B:自建推送系统
直接对接各家厂商推送:
┌──────────┐ ┌──────────┐
│ 我们的 │────▶│ Mi Push │
│ 服务器 │ └──────────┘
└──────────┘ ┌──────────┐
│ HMS Push │
└──────────┘
┌──────────┐
│ OPPO Push│
└──────────┘
优点:
- 完全控制,可优化到达率
- 成本可控
缺点:
- 开发和维护成本高
- 需要对接多家 API
我权衡了一下,决定先用统一推送平台(极光),再逐步自建。
推送到达率
老王问我:“我们的推送到达率是多少?”
我查了一下极光的统计:
上周推送统计:
- 发送总数:10 万条
- 到达数:8.5 万条
- 到达率:85%
按平台分析:
- iOS:92%
- 安卓(小米):88%
- 安卓(华为):90%
- 安卓(其他):65%
“安卓’其他’到达率只有 65%?”
“那些是没有系统推送的机型,” 老王说,“比如 Google Pixel、一加等,需要 App 在前台才能收到推送。”
我分析了一下原因:
到达率影响因素:
-
系统级推送:系统内置推送服务,App 不运行也能收到
- iOS:APNs(100% 系统级)
- 小米、华为、OPPO、vivo:厂商推送(系统级)
-
应用级推送:需要 App 在前台运行
- 其他机型:无系统推送,到达率低
-
用户设置:用户可能关闭了通知权限
- 需要在 App 内引导用户开启
-
网络因素:推送通道不稳定
- 需要重试机制
提高到达率
我决定采取几个措施:
1. 针对不同机型选择不同策略
2. 引导用户开启通知权限
我在 App 启动时检测通知权限:
3. 推送失败自动降级
推送内容设计
产品经理小李找到我:“推送文案要怎么设计?”
我给她展示了几个例子:
不好的推送:
用户 A 评论了你的动态
- 太模糊,用户不知道哪个动态
- 没有吸引力,用户可能不点击
好的推送:
用户 A:你的文章写得真好,特别是第三段关于...
- 包含具体内容
- 吸引用户点击查看完整评论
推送内容限制
不同渠道对推送内容有限制:
| 渠道 | 标题长度 | 内容长度 | 其他限制 |
|---|---|---|---|
| APNs | 最多 178 字符 | 最多 1024 字符 | 不支持自定义声音 |
| Mi Push | 最多 50 字符 | 最多 128 字符 | 需要审核 |
| HMS Push | 最多 40 字符 | 最多 180 字符 | 不支持自定义图标 |
我写了一个内容适配函数:
推送时机选择
老王问:“半夜推送打扰用户的问题,怎么解决?”
我调研了一下用户活跃时间:
用户活跃时间分布(24 小时):
- 0:00-6:00:5%(深夜)
- 6:00-9:00:15%(早晨)
- 9:00-12:00:25%(上午)
- 12:00-14:00:20%(午休)
- 14:00-18:00:25%(下午)
- 18:00-22:00:30%(晚上)
- 22:00-24:00:10%(睡前)
深夜(0:00-6:00)用户活跃率只有 5%,这时候推送不仅打扰用户,还浪费资源。
我设计了一个推送时段控制:
小结
这周我学到了:
- iOS 推送:APNs 是唯一通道,需要处理 token 失效
- 安卓推送:国内多家厂商推送,需要选择合适的策略
- 到达率优化:区分系统级和应用级推送,引导用户开启权限
- 推送内容:不同渠道有限制,需要适配
- 推送时机:避免深夜打扰,支持用户自定义免打扰时段
下周,我要处理站内信的设计了。
