Appearance
移动端推送通道、长连接与长效保活边界
回到总览:异步后台任务、保活、系统能力
相关模块:Android Foreground Service 深挖(服务类型、通知约束与高版本后台限制) · Doze 深度休眠模式与国内厂商后台限制
一句话定义
移动端推送通道与长效保活是指在 Android 8.0+ 与国内厂商 ROM 严格限制后台常驻进程的背景下,通过将自研 TCP 长连接与厂商系统级推送通道(FCM / 小米 / 华为 / OPPO / vivo Push)相结合,实现高到达率消息下发与合规保活的技术架构。
代码索引
计划补齐的实验:
labs/android/host/topics/background-tasks/push-channel-demo/— FCM 与厂商推送通道回调集成、Socket 心跳包保活与死锁测试
为什么需要
- 为什么自建 TCP 长连接(Socket)在应用退后台一段时间后无法再收到任何推送消息?
- 一句话答:应用退后台后,设备进入 Doze 模式或被厂商 ROM 强制挂起 CPU/切断网络,自建 Socket 长连接无法继续维护心跳包,链路随之断开;只有由系统底层服务维持的厂商推送通道(如 FCM 或小米 Push 服务)才能突破限制收到消息。
- 为什么 10 年 Android 工程师架构设计时要明确“推送通道替代自建保活”?
- 一句话答:Android 原生黑产级别的多进程拉起与双进程保活在 Android 8.0+ / 12+ 已被系统与厂商完全堵死;放弃黑产保活、全面接入厂商推送通道是保证线上消息到达率的唯一合规解法。
底层机制
1. 混合推送通道分发架构 (Hybrid Push Architecture)
text
[App 后台 / 灭屏]
│
├── App 运行在前台/刚退后台 ───► [自建 TCP 长连接 (Socket)] ───► (低延迟,高自定义)
│
└── App 彻底退后台 / 进程被杀死 ──► [厂商系统级 Push 通道] ───► (高到达率,系统级透传)
├── 小米 Push (MiPush)
├── 华为 Push (HMS)
├── OPPO / vivo Push
└── 谷歌 FCM (海外)2. 自建 Socket 心跳包 (Heartbeat) 与智能心跳策略
在 App 处于前台或短时间退后台时,自建 Socket 是性价比最高的通信方式:
- 固定心跳:每隔 4.5 分钟(防止运营商 NAT 端口映射超时切断)发送一次心跳包。
- 智能心跳 (Smart Heartbeat):根据当前网络环境(Wi-Fi / 5G / 4G)动态探测 NAT 超时时间,在维持连接的前提下最大程度减少 CPU 唤醒次数与电量消耗。
Android / Flutter / Web / Backend 对照
| 维度 | Android 宿主 | Flutter (Dart) | iOS 宿主 |
|---|---|---|---|
| 系统级推送 | 厂商 Push (小米/华为/vivo) / FCM | 依赖 firebase_messaging / 极光插件 | APNs (Apple Push Notification) |
| 透传消息处理 | FirebaseMessagingService | FirebaseMessaging.onBackgroundMessage | UNNotificationServiceExtension |
| 进程杀死后 | 厂商通道弹系统通知,点击唤醒 App | 点击系统通知唤醒 FlutterEngine | APNs 弹通知,点击唤醒 App |
常见场景
1. 厂商推送 Token 注册与服务端绑定
java
// 华为 / 小米 Push 注册示例
public void initVendorPush(Context context) {
if (RomUtils.isXiaomi()) {
MiPushClient.registerPush(context, APP_ID, APP_KEY);
} else if (RomUtils.isHuawei()) {
HMSAgent.connect(activity, () -> HMSAgent.Push.getToken(this::onTokenReceived));
}
}
// 拿到各厂商的 RegId / Token 后,统一上报给自己后台服务器绑定 userId
private void onTokenReceived(String token) {
ApiServer.bindUserPushToken(userId, token, RomUtils.getDeviceBrand());
}常见误配、事故后果与排障
1. 事故:Android 8.0+ 上忽略 NotificationChannel 导致透传消息无法展示
- 误配原因:在 Android 8.0 (API 26) 以上设备上,收到厂商透传消息后弹本地通知,未创建
NotificationChannel。 - 后果:日志提示
Failed to post notification...,通知被系统静默丢弃,用户完全感知不到推送。 - 排障与修法:在发送通知前,必须创建并注册
NotificationChannel(设置合理的 Importance 等级)。
与相近概念对比
| 通道类型 | 延迟时间 | 到达率 (进程被杀后) | 依赖服务 |
|---|---|---|---|
| 自建 Socket 长连接 | 极低 (毫秒级) | 0% (进程被杀即断开) | 自己的 Server 节点 |
| 厂商系统级 Push | 低 (秒级) | 99%+ | 小米/华为/vivo 等系统服务 |
| FCM (Firebase) | 低 (海外) | 国内 0% / 海外 99% | Google Play Services |
对应实验
计划补齐的实验:
labs/android/host/topics/background-tasks/push-channel-demo/— FCM 与厂商推送通道回调集成、Socket 心跳包保活与死锁测试
复习检查题
为什么自建的 TCP Socket 长连接心跳时间通常不能超过 5 分钟?
答:因为移动运营商的网络网关(GGSN / NAT)维护着内部的 TCP 映射表。为了节省网络资源,如果一个 TCP 链路在一定时间内(通常为 4~5 分钟)没有任何数据包收发,运营商 NAT 映射就会静默超时并切断该连接,此时 Client 侧虽然Socket 状态看似连接,实际上已无法收到任何数据。
在 Android 8.0+ 设备上,为什么厂商推送通道(如小米 MiPush)在应用进程被杀死后依然能弹出推送通知?
答:因为厂商推送 SDK 的客户端不是运行在你的 App 进程中,而是集成在 Android 操作系统本身的底层常驻系统服务(如小米的
com.xiaomi.xmsf)中。系统服务由手机厂商的 ROM 保证永久存活,当接收到推送后直接由系统服务弹出 Notification,用户点击通知时才去唤醒或启动目标 App。
速记
- 通道分工:前台走自建 Socket,后台/死进程靠厂商系统 Push。
- NAT 防切断:Socket 心跳包间隔控制在 4.5 分钟以内。
- 8.0 渠道:弹 Notification 必须绑定
NotificationChannel,否则静默丢弃。