实现 Android 离线推送
更新时间: 2025/01/20 13:27:38
为了提高消息送达率,网易云信引入手机系统厂商推送。手机系统级别的厂商推送(如小米、华为、vivo、OPPO、魅族等)的优势在于其拥有稳定的系统级长连接,可以做到随时接收推送。
适用场景
您可以通过集成各移动端设备厂商的原生推送 SDK,与 NIM SDK 搭配使用,实现离线推送功能。
实现离线推送消息功能后,当出现以下用户行为时,会触发离线推送,使用手机厂商系统级推送告知用户有消息需要接收:
- 当应用被切换到后台,并且 App 资源被系统回收时。
- 用户登录账号后,主动关闭 App 时。
- 网络不稳定等,导致 NIM SDK 无法与网易云信服务器保持正常连接时。
技术原理
网易云信 IM 实现离线推送的技术原理如下:
sequenceDiagram
autoNumber
actor 接收方
participant 厂商 as 厂商推送服务端
participant 网易云信 as 网易云信推送服务端
actor 发送方
接收方->>厂商: 申请推送 Token
厂商-->>接收方: 返回推送 Token
接收方->>网易云信: 传入推送 Token
发送方->>网易云信: 发送消息或自定义系统通知
网易云信->>网易云信: 判断是否需要发送离线推送消息
网易云信->>厂商: 转发离线推送消息
厂商->>接收方: 投递离线推送消息
前提条件
在实现离线推送功能之前,请确保:
-
开发环境满足如下要求:
- Android 5.0 及以上版本。
- 自 v6.9.0 起,改用 AndroidX 支持库,Target API 改为 28,不再支持 support 库。
-
已 注册 IM 账号,获取 IM 账号和 Token。
第一步:集成平台离线推送服务
请参考具体第三方厂商的推送集成文档,集成需要使用到的第三方厂商离线推送 SDK,接入各厂商的离线推送服务。
目前支持以下第三方推送厂商:
第三方推送厂商 | 当前兼容版本 | 集成指南 |
---|---|---|
小米 | 5.6.2 | 集成小米推送 |
华为 | 6.9.0.300 | 集成华为推送 |
荣耀 | 7.0.41.301 | 集成荣耀推送 |
OPPO | 3.1.0 | 集成 OPPO 推送 |
vivo | 3.0.0.4_484 | 集成 vivo 推送 |
魅族 | 4.2.3 | 集成魅族推送 |
谷歌 FCM | firebase-bom:28.4.2 具体版本: firebase-messaging:23.0.0 ,firebase-analytics: 20.0.0 |
集成谷歌推送 |
第二步:集成 NIM SDK
在 集成 NIM SDK 时,需要添加第三方厂商推送辅助包。
-
若自动集成,则需要在 dependencies 中添加相关依赖。
dependencies { compile fileTree(dir: 'libs', include: '*.jar') // 添加依赖。注意,版本号必须一致。 // 基础功能 (必需) implementation "com.netease.nimlib:basesdk:${LATEST_VERSION}" // 通过网易云信来集成第三方厂商推送需要 implementation "com.netease.nimlib:push:${LATEST_VERSION}" }
-
若手动集成需要将 下载的
nim-push-x.x.x.jar
文件拷贝到您的项目路径的app/libs
目录下。然后在该.jar
文件上右键添加为依赖。
第三步:初始化 NIM SDK
初始化 NIM SDK 时,完成厂商的推送证书配置,并选择推送渠道。
-
需要将推送证书的信息配置到初始化参数
SDKOptions.mixPushConfig
中。小米Java
MixPushConfig config = new MixPushConfig(); // 传入从小米推送平台获取到的 AppId 与 AppKey config.xmAppId = "xxxx"; config.xmAppKey = "xxxx"; // 传入网易云信控制台上小米推送对应的证书名 config.xmCertificateName = "xxxx"; ... options.mixPushConfig = config;
华为Java
MixPushConfig config = new MixPushConfig(); // 传入华为推送的 App ID config.hwAppId = "xxxx"; // 传入网易云信控制台上华为推送证书名 config.hwCertificateName = "xxxx"; ... options.mixPushConfig = config;
荣耀Java
MixPushConfig config = new MixPushConfig(); // 传入荣耀推送证书名,荣耀推送的 appId 请在 AndroidManifest.xml 文件中配置 config.honorCertificateName = "xxxx"; ... options.mixPushConfig = config;
vivoJava
MixPushConfig config = new MixPushConfig(); // 传入网易云信控制台上配置的 vivo 推送证书名,vivo 推送的 appId appKey 请在 AndroidManifest.xml 文件中配置 config.vivoCertificateName = "xxxx"; ... options.mixPushConfig = config;
OPPOJava
MixPushConfig config = new MixPushConfig(); config.oppoAppId = "xxxx"; config.oppoAppKey = "xxxxxx"; // 注意区分 AppSercet 与 MasterSecret config.oppoAppSercet = "xxxxxxx"; // 传入网易云信控制台上配置的 oppo 推送证书名 config.oppoCertificateName = "xxxx"; ... options.mixPushConfig = config;
魅族Java
MixPushConfig config = new MixPushConfig(); config.mzAppId = "xxx"; config.mzAppKey = "xxxx"; config.mzCertificateName = "xxxx"; ... options.mixPushConfig = config;
谷歌Java
//传入网易云信控制台上配置的谷歌推送证书名,谷歌推送的 AppSecret 请在 AndroidManifest.xml 文件中配置 MixPushConfig config = new MixPushConfig(); config.fcmCertificateName = "xxxx"; ... options.mixPushConfig = config;
推送证书名长度不超过 32 个字符,否则登录时会报错 500。
-
选择推送渠道。
-
如果
SDKOptions.mixPushConfig.autoSelectPushType
为 false(默认),则 SDK 直接选择服务端推荐的推送渠道。 -
如果配置
SDKOptions.mixPushConfig.autoSelectPushType
为 true,则 SDK 根据实际的 token 的获取情况确定推送渠道,此时服务端推荐的推送渠道为最高优先级。需要确定推送渠道时,先进行本地支持性判断,然后将向所有可能支持的推送厂商申请 token,并在成功拿到的 token 中选择优先级更高的渠道。
SDK 默认 Google FCM 推送的优先级最低,即如果同时接入了 FCM 和其他厂商推送,则会优先走其他厂商推送通道。但是如果您的应用用户主要分布在海外,在 网易云信控制台 已设置 FCM 推送优先 (控制台:应用详情 > 更多 > 证书管理),那么同时接入 FCM 和其他厂商推送 SDK 的场景下,优先走 FCM 推送通道。
-
-
在
Application#onCreate
中启用华为、荣耀、OPPO 厂商的推送服务,即对华为、荣耀、OPPO 推送服务进行初始化。其他厂商推送服务无需额外初始化,可忽略该步骤。华为Java
public class NimApplication extends Application { ... @Override public void onCreate() { ... if (NIMUtil.isMainProcess(this)) { ... // 在此处添加以下代码 com.huawei.hms.support.common.ActivityMgr.INST.init(this); ... } } }
荣耀Java
public class NimApplication extends Application { ... @Override public void onCreate() { ... if (NIMUtil.isMainProcess(this)) { ... // 在此处添加以下代码 HonorPushClient.getInstance().init(getApplicationContext(), true); ... } } }
OPPOJava
public class NimApplication extends Application { ... @Override public void onCreate() { ... if (NIMUtil.isMainProcess(this)) { ... // 在此处添加以下代码 com.heytap.msp.push.HeytapPushManager.init(this, true); ... } } }
若您正确集成第三方推送(已传相关推送
token
至网易云信服务器),SDK 日志会打印以下相应记录。其中pushType
和type
表示推送类型(5 小米、6 华为、7 魅族、8 谷歌 FCM、9 vivo、10 OPPO、0 不支持),tokenName
表示推送证书名称,token
表示推送 token。Java
[ui]mix_push: after login, mix push state=MixPushState{pushType=5, hasPushed=0, lastDeviceId=''} [ui]mix_push: commit mix push token:type 5 tokenName PUSH_CER_NAME token y7ssE..................sLxnU
-
(可选)初始化之后,无论是否登录,您都可以调用
MixPushService
的registerPush
方法获取推送 Token。(Token 会通过MixPushServiceObserve
的observeMixPushToken
回调得到。)如果您的应用用户主要分布在海外,需要优先走 FCM 推送通道,需要提前在 网易云信控制台 已设置 FCM 推送优先 (控制台:应用详情 > 更多 > 证书管理),那么调用该接口时,会优先获取 FCM 推送的 Token。
第四步:测试离线推送
消息发送方
发送消息或自定义通知给接收方(离线),具体的收发流程可参考 消息收发 和 自定义通知收发。
这里以发送文本消息为例,通过调用 sendMessage
实现。发送的消息默认需要推送,如需设置推送文案,推送角标,推送文案前缀等,请参考 配置消息的推送属性。
Java// 该账号为示例
String account = "testAccount";
// 以单聊类型为例
SessionTypeEnum sessionType = SessionTypeEnum.P2P;
String text = "this is an example";
// 创建一个文本消息
IMMessage textMessage = MessageBuilder.createTextMessage(account, sessionType, text);
// 发送给对方
NIMClient.getService(MsgService.class).sendMessage(textMessage, false).setCallback(new RequestCallback<Void>() {
@Override
public void onSuccess(Void param) {
}
@Override
public void onFailed(int code) {
}
@Override
public void onException(Throwable exception) {
}
});
消息接收方
接收方将会在登录后接收到离线推送。
第五步:(可选)关闭或开启离线推送
NIM SDK 的第三方推送服务功能默认开启,无需单独调用开启服务接口。若后续不想使用第三方推送服务,可调用 enable
方法进行关闭。
JavaNIMClient.getService(MixPushService.class).enable(false).setCallback(...)
相关参考
-
第三方厂商推送通道存在流控,具体请参考 厂商推送通道限制说明
-
若您想直接使用 SDK 中注册的 Channel 来作为第三方厂商(小米、OPPO)的申请 Channel,可参考 SDK 中的 Channel 信息
常见问题
触发离线推送的条件是什么?
Android 切换到后台并且等 App 被系统回收时,或者用户主动关闭 App,才能触发推送条件。因此,若要测试 Android 推送问题,请登录后关闭 App,确保满足推送条件。
哪些场景下不会触发推送?
IM 账号未登录/已登出/被踢出,不会触发推送。App 在前台,也不会触发推送。用户登录 IM 账号,并且未主动登出或者没有被踢出,可能触发推送。