92、推送服务集成:极光推送、小米推送、华为推送、自定义推送通道
推送服务,说白了就是让App在后台也能跟用户“打招呼”。
我刚开始做推送时,觉得这玩意儿不就是调个SDK嘛。后来踩了坑才发现——不同厂商的推送策略、保活机制、权限要求,简直是八仙过海各显神通。今天咱们就把极光、小米、华为、还有自定义通道,一个一个捋清楚。
为什么需要多通道推送?
Android系统从6.0开始,对后台限制越来越严。到了8.0,后台服务几乎被“阉割”。你想想看,App在后台收不到推送,用户就收不到消息,那体验得多糟糕。
所以行业里搞了个方案:用厂商通道做保底。说白了,就是让手机厂商自己的推送服务来帮你发消息。小米手机用小米推送,华为手机用华为推送,其他手机用极光这类第三方通道。
核心思路: 能走厂商通道就走厂商通道,走不了再走第三方通道。实在不行,自己搞个长连接兜底。
极光推送集成
极光推送(JPush)是国内用得比较多的第三方推送。它最大的好处是——省心。你不需要关心各家厂商的适配,极光帮你做了封装。
集成步骤
- 在极光官网注册应用,获取AppKey
- 在build.gradle里添加依赖
- 在Application里初始化
- 注册广播接收器处理消息
// build.gradle
implementation 'cn.jiguang.sdk:jpush:4.8.0'
implementation 'cn.jiguang.sdk:jcore:3.2.0'
// Application
JPushInterface.setDebugMode(true);
JPushInterface.init(this);
// 广播接收器
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (JPushInterface.ACTION_MESSAGE_RECEIVED.equals(intent.getAction())) {
String message = intent.getStringExtra(JPushInterface.EXTRA_MESSAGE);
// 处理自定义消息
}
}
}
我的经验: 极光的初始化一定要放在Application的onCreate里,而且越早越好。我曾经因为初始化晚了,导致部分华为手机收不到推送,排查了半天才发现是时序问题。
小米推送集成
小米推送,说白了就是给小米手机“开后门”。小米系统对自家推送有最高优先级,App被杀掉也能收到消息。
集成要点
- 需要在小米开放平台注册应用,获取AppId和AppKey
- 小米推送的SDK体积比较小,大概200KB左右
- 支持透传消息和通知栏消息两种模式
// 初始化
MiPushClient.registerPush(this, APP_ID, APP_KEY);
// 注册回调
public class MyPushMessageReceiver extends PushMessageReceiver {
@Override
public void onReceivePassThroughMessage(Context context, MiPushMessage message) {
// 透传消息,不会弹出通知
}
@Override
public void onNotificationMessageClicked(Context context, MiPushMessage message) {
// 用户点击了通知栏
}
}
注意: 小米推送的透传消息,在部分MIUI版本上会被系统拦截。我遇到过用户反馈“收不到透传消息”,后来发现是MIUI 12的省电策略搞的鬼。解决方案是引导用户把App加入“无限制”后台白名单。
华为推送集成
华为推送(HMS Push)是华为手机的“亲儿子”。它的优势在于——华为手机即使杀后台,也能通过系统级通道送达消息。
集成流程
- 在华为开发者联盟注册应用,配置SHA256指纹
- 集成HMS Core SDK
- 实现HmsMessageService
// build.gradle
implementation 'com.huawei.hms:push:6.5.0.300'
// 服务类
public class MyHmsMessageService extends HmsMessageService {
@Override
public void onNewToken(String token) {
// 获取推送令牌,上报给服务端
}
@Override
public void onMessageReceived(RemoteMessage message) {
// 处理收到的消息
String data = message.getData();
// 注意:华为推送的data消息,在App前台时才会回调
}
}
关键点: 华为推送的onMessageReceived在App后台时,只会收到通知栏消息,不会回调透传消息。这一点跟小米推送不一样。我刚开始做的时候没注意,结果后台消息全丢了。
自定义推送通道
有些场景下,第三方和厂商通道都满足不了需求。比如企业内部App、或者需要实时性极高的IM应用。这时候就得自己搞一套推送通道。
实现方案
- WebSocket长连接: 实时性最好,但耗电
- MQTT协议: 轻量级,适合物联网和移动端
- HTTP长轮询: 实现简单,但延迟较高
// 使用OkHttp实现WebSocket
OkHttpClient client = new OkHttpClient.Builder()
.readTimeout(0, TimeUnit.MILLISECONDS)
.build();
Request request = new Request.Builder()
.url("ws://your-server.com/push")
.build();
WebSocket ws = client.newWebSocket(request, new WebSocketListener() {
@Override
public void onMessage(WebSocket webSocket, String text) {
// 收到推送消息
}
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
// 断线重连
reconnect();
}
});
我的建议: 自定义通道只作为兜底方案。能走厂商通道就走厂商通道,毕竟系统级推送的稳定性和省电效果,是第三方SDK比不了的。我曾经在一个项目中全部用自定义WebSocket,结果用户反馈手机发烫、耗电快,后来切回厂商通道才解决。
多通道统一管理
实际项目中,往往是多个通道并存。这时候需要一个统一的管理层,来协调各个通道的优先级和切换逻辑。
优先级策略
| 设备类型 | 首选通道 | 备选通道 | 兜底通道 |
|---|---|---|---|
| 华为手机 | 华为推送 | 极光推送 | 自定义WebSocket |
| 小米手机 | 小米推送 | 极光推送 | 自定义WebSocket |
| 其他手机 | 极光推送 | 自定义WebSocket | — |
避坑指南: 我曾经在切换通道时,没有处理好token的同步问题。用户从华为手机换到小米手机,推送token没更新,结果消息全发到旧设备上了。解决方案是:每次设备启动时,重新获取所有通道的token,并上报给服务端。
推送消息的格式规范
不管用哪个通道,消息格式最好统一。我习惯用JSON,包含以下几个字段:
{
"type": "order_update", // 消息类型
"title": "订单状态变更", // 通知标题
"content": "您的订单已发货", // 通知内容
"payload": { // 业务数据
"order_id": "123456",
"status": "shipped"
},
"extra": { // 扩展字段
"channel": "huawei",
"priority": "high"
}
}
我的习惯: payload里只放关键业务数据,不要塞大字段。我曾经见过有人把整个订单对象塞进去,结果推送消息超过4KB,直接被厂商通道拦截了。嗯,后来改成只传order_id,客户端再根据id去拉详情。
推送的测试与监控
推送集成完了,怎么测?我一般分三步走:
- 前台测试: App在前台时,看能不能收到透传消息
- 后台测试: App切到后台,看通知栏能不能弹出
- 杀进程测试: 彻底杀掉App,看厂商通道能不能送达
监控方面,建议接入推送的送达率统计。极光和厂商通道都提供了数据上报接口,可以统计到“发送成功”、“送达设备”、“用户点击”三个维度的数据。
总结一下: 推送集成没有银弹。华为手机用华为推送,小米手机用小米推送,其他手机用极光推送,实在不行自己搞个WebSocket兜底。记住一个原则——能用系统通道就用系统通道,别跟系统作对。