即时通信 IM 的终端用户需要随时都能够得知最新的消息,而由于移动端设备的性能与电量有限,当 App 处于后台时,为了避免维持长连接而导致的过多资源消耗,即时通信 IM 推荐您使用各厂商提供的系统级推送通道来进行消息通知,系统级的推送通道相比第三方推送拥有更稳定的系统级长连接,可以做到随时接受推送消息,且资源消耗大幅降低。
注意:
- 在没有主动退出登录的情况下,应用退后台、手机锁屏、或者应用进程被用户主动杀掉三种场景下,如果想继续接收到 IM 消息提醒,可以接入即时通信 IM 离线推送。
- 如果应用主动调用 logout 退出登录,或者多端登录被踢下线,即使接入了 IM 离线推送,也收不到离线推送消息。
TUIKitDemo 已经按照如下步骤接入了离线推送功能,文档中已有源码指引链接可供参考。
离线推送功能依赖厂商原始通道,您需要将自己的应用注册到各个厂商的推送平台,得到 AppID 和 AppKey 等参数。目前支持的手机厂商有:Google FCM ,小米、华为、OPPO、VIVO、魅族 。
登录腾讯云 即时通信 IM 控制台 ,添加各个厂商推送证书,并将您在步骤一中获取的各厂商的 AppId、AppKey、AppSecret 等参数配置给 IM 控制台的推送证书,以 Google 为例:
厂商推送平台 | IM 控制台配置 |
---|---|
![]() |
![]() |
收到离线推送后,通知栏会显示推送信息如图所示,单击通知栏会打开应用并进入配置的跳转界面。请您参见下面的步骤,配置单击通知消息后跳转的 Activity。
控制台配置
各个厂商的跳转界面配置方式有所不同,具体如下:
厂商 | 单击后后续动作 | 应用内指定界面 |
---|---|---|
小米 | 打开应用内指定页面 | intent://`您配置的 hostname`/`您配置的 path`#Intent;scheme=`您配置的协议,也就是你定义的 scheme`;launchFlags=0x4000000;component=`您应用跳转界面的完整类名`;end TUIKitDemo 配置的是:intent://com.tencent.qcloud/detail#Intent;scheme=pushscheme;launchFlags=0x4000000;component=com.tencent.qcloud.tim.tuikit/com.tencent.qcloud.tim.demo.main.MainActivity;end |
华为 | 打开应用内指定页面 | intent://`您配置的 hostname`/`您配置的 path`#Intent;scheme=`您配置的协议,也就是你定义的 scheme`;launchFlags=0x4000000;component=`您应用跳转界面的完整类名`;end TUIKitDemo 配置的是:intent://com.tencent.qcloud/detail#Intent;scheme=pushscheme;launchFlags=0x4000000;component=com.tencent.qcloud.tim.tuikit/com.tencent.qcloud.tim.demo.main.MainActivity;end |
魅族 | 打开应用内指定页面 | 您应用跳转界面的完整类名 TUIKitDemo 配置的是:com.tencent.qcloud.tim.demo.main.MainActivity |
OPPO | 打开应用内指定页面 | 您应用跳转界面的完整类名 TUIKitDemo 配置的是:activity:com.tencent.qcloud.tim.demo.main.MainActivity |
vivo | 打开应用内指定页面 | intent://`您配置的 hostname`/`您配置的 path`#Intent;scheme=`您配置的协议,也就是你定义的 scheme`;launchFlags=0x4000000;component=`您应用跳转界面的完整类名`;end TUIKitDemo 配置的是:intent://com.tencent.qcloud/detail#Intent;scheme=pushscheme;launchFlags=0x4000000;component=com.tencent.qcloud.tim.tuikit/com.tencent.qcloud.tim.demo.main.MainActivity;end |
Google FCM | 不需要配置 | 默认会跳转至应用的 Launcher 界面 |
清单文件配置
在 清单文件 AndroidManifest.xml 中完成跳转界面的相关配置,需要注意的是,该配置必须与您在 IM 控制台推送证书的单击后续动作配置保持一致。
<!-- TUIKitDemo 配置的跳转界面是 MainActivity,所以这里填 com.tencent.qcloud.tim.demo.main.MainActivity。集成到您的应用后,需要替换您的应用界面完整类名 -->
<activity
android:name="您应用跳转界面的完整类名"
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize|stateHidden">
<!-- 离线推送打开应用内页面 -->
<intent-filter>
<action android:name="android.intent.a和Google FCmo配置的是:pushscheme://com.tencent.qcloud/detail -->
<data
android:host="您配置的 hostname"
android:path="您配置的 path"
android:scheme="您配置的协议,也就是你定义的 scheme" />
</intent-filter>
</activity>
推送证书 ID 如下:
填充的参数如下:
public class PrivateConstants {
// 在腾讯云控制台上传第三方推送证书后分配的证书ID
public static final long GOOGLE_FCM_PUSH_BUZID = 15518;
}
<!-- 注意:TUIKitDemo 的 applicationId 是 com.tencent.qcloud.tim.tuikit,这里的 “xxxx” 需要替换您的应用的 applicationId。 -->
<!-- ********海外google云消息传递start******** -->
<service
android:name="xxxx.GoogleFCMMsgService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<!-- ********海外google云消息传递end******** -->
下载配置文件添加到工程根目录。
在项目级 build.gradle 文件中 buildscript -> dependencies 下添加以下配置:
dependencies {
...
classpath 'com.google.gms:google-services:4.2.0'
}
在应用级 build.gradle 文件中添加下方配置。
apply plugin: 'com.google.gms.google-services'
单击项目右上角 Sync Now 同步项目。
dependencies {
......
// 主包
implementation 'com.tencent.tpns:tpns:1.3.1.1-release'
// Google FCM
implementation "com.tencent.tpns:fcm:1.3.1.1-release"
// google 云消息传递
implementation ('com.google.firebase:firebase-messaging:19.0.1')
// 小米
implementation "com.tencent.tpns:xiaomi:1.3.1.1-release"
// 魅族
implementation "com.tencent.tpns:meizu:1.3.1.1-release"
// OPPO
implementation "com.tencent.tpns:oppo:1.3.1.1-release"
// vivo
implementation "com.tencent.tpns:vivo:1.3.2.0-release"
// 华为
implementation 'com.tencent.tpns:huawei:1.3.1.1-release'
implementation 'com.huawei.hms:push:5.0.2.300'
}
添加推送类
引入厂商推送类,各个厂商推送方式有区别,可以参见拷贝 TUIOfflinePush 代码路径 下如下文件:
推送服务注册
应合规要求,在用户同意隐私协议登录成功后,分别初始化注册各个厂商推送服务,并在注册结果回调处保存注册成功后的 token,并调用 setOfflinePushConfig 接口上报推送 token 至后台。部分厂商在注册后,调用一些接口也会返回 token,可以再次同步更新下,具体参见以下代码。
public void init() {
...
if (BrandUtil.isBrandXiaoMi()) {
// 小米离线推送
MiPushClient.registerPush(this, PrivateConstants.XM_PUSH_APPID, PrivateConstants.XM_PUSH_APPKEY);
} else if (BrandUtil.isBrandHuawei()) {
// 华为离线推送,设置是否接收Push通知栏消息调用示例
HmsMessaging.getInstance(this).turnOnPush().addOnCompleteListener(new com.huawei.hmf.tasks.OnCompleteListener<Void>() {
@Override
public void onComplete(com.huawei.hmf.tasks.Task<Void> task) {
if (task.isSuccessful()) {
DemoLog.i(TAG, "huawei turnOnPush Complete");
} else {
DemoLog.e(TAG, "huawei turnOnPush failed: ret=" + task.getException().getMessage());
}
}
});
new Thread() {
@Override
public void run() {
try {
// read from agconnect-services.json
String appId = AGConnectServicesConfig.fromContext(MainActivity.this).getString("client/app_id");
String token = HmsInstanceId.getInstance(MainActivity.this).getToken(appId, "HCM");
DemoLog.i(TAG, "huawei get token:" + token);
if(!TextUtils.isEmpty(token)) {
// 该 token 需要保存并调用 setOfflinePushConfig 接口上报 IMSDK
String token = (String) o;
}
} catch (ApiException e) {
DemoLog.e(TAG, "huawei get token failed, " + e);
}
}
}.start();
} else if (MzSystemUtils.isBrandMeizu(this)) {
// 魅族离线推送
PushManager.register(this, PrivateConstants.MZ_PUSH_APPID, PrivateConstants.MZ_PUSH_APPKEY);
} else if (BrandUtil.isBrandVivo()) {
// vivo离线推送
PushClient.getInstance(getApplicationContext()).initialize();
DemoLog.i(TAG, "vivo support push: " + PushClient.getInstance(getApplicationContext()).isSupport());
PushClient.getInstance(getApplicationContext()).turnOnPush(new IPushActionListener() {
@Override
public void onStateChanged(int state) {
if (state == 0) {
String regId = PushClient.getInstance(getApplicationContext()).getRegId();
DemoLog.i(TAG, "vivopush open vivo push success regId = " + regId);
// // 该 token 需要保存并调用 setOfflinePushConfig 接口上报 IMSDK
String token = (String) o;
} else {
// 根据vivo推送文档说明,state = 101 表示该vivo机型或者版本不支持vivo推送,链接:https://dev.vivo.com.cn/documentCenter/doc/156
DemoLog.i(TAG, "vivopush open vivo push fail state = " + state);
}
}
});
} else if (HeytapPushManager.isSupportPush()) {
// oppo离线推送
OPPOPushImpl oppo = new OPPOPushImpl();
oppo.createNotificationChannel(this);
// oppo接入文档要求,应用必须要调用init(...)接口,才能执行后续操作。
HeytapPushManager.init(this, false);
HeytapPushManager.register(this, PrivateConstants.OPPO_PUSH_APPKEY, PrivateConstants.OPPO_PUSH_APPSECRET, oppo);
} else if (BrandUtil.isGoogleServiceSupport()) {
FirebaseInstanceId.getInstance().getInstanceId()
.addOnCompleteListener(new com.google.android.gms.tasks.OnCompleteListener<InstanceIdResult>() {
@Override
public void onComplete(Task<InstanceIdResult> task) {
if (!task.isSuccessful()) {
DemoLog.w(TAG, "getInstanceId failed exception = " + task.getException());
return;
}
// Get new Instance ID token
String token = task.getResult().getToken();
DemoLog.i(TAG, "google fcm getToken = " + token);
// 该 token 需要保存并调用 setOfflinePushConfig 接口上报 IMSDK
String token = (String) o;
}
});
}
}
以 Google FCM 为例,在注册结果回调处保存注册成功后的 token,并调用 setOfflinePushConfig 接口上报给后台。
public class GoogleFCMMsgService extends FirebaseMessagingService {
...
@Override
public void onNewToken(String token) {
DemoLog.i(TAG, "onNewToken token=" + token);
// 该 token 需要保存并调用 setOfflinePushConfig 接口上报 IMSDK
String pushToken = token;
}
...
}
V2TIMOfflinePushConfig v2TIMOfflinePushConfig = null;
// 需要设置 businessID 为对应厂商的证书 ID,isTPNSToken 为 false,上报注册厂商推送服务获取的 token。
v2TIMOfflinePushConfig = new V2TIMOfflinePushConfig(0, token, true);
V2TIMManager.getOfflinePushManager().setOfflinePushConfig(v2TIMOfflinePushConfig, new V2TIMCallback() {
@Override
public void onError(int code, String desc) {
DemoLog.d(TAG, "setOfflinePushToken err code = " + code);
}
@Override
public void onSuccess() {
DemoLog.d(TAG, "setOfflinePushToken success");
}
});
如果您的应用退到后台,收到新消息时需要在手机通知栏进行展示,请您调用 IMSDK 的 doBackground() 接口,将应用的状态同步给 IM 后台;当应用回到前台时,请您调用 IMSDK 的 doForeground() 接口,将应用的状态同步给 IM 后台。监听 App 前后台切换的方案推荐您参见 TUIOfflinePushService 的 initActivityLifecycle() 相关逻辑。
// 应用切到后台时
V2TIMManager.getOfflinePushManager().doBackground(totalCount, new V2TIMCallback() {
@Override
public void onError(int code, String desc) {
DemoLog.e(TAG, "doBackground err = " + code + ", desc = " + desc);
}
@Override
public void onSuccess() {
DemoLog.i(TAG, "doBackground success");
}
});
// 应用切回前台时
V2TIMManager.getOfflinePushManager().doForeground(new V2TIMCallback() {
@Override
public void onError(int code, String desc) {
DemoLog.e(TAG, "doForeground err = " + code + ", desc = " + desc);
}
@Override
public void onSuccess() {
DemoLog.i(TAG, "doForeground success");
}
});
调用 sendMessage 发送消息时,您可以通过 V2TIMOfflinePushInfo 设置离线推送参数,可以参见 ChatProvider 的 sendMessage() 方法:
OfflineMessageContainerBean containerBean = new OfflineMessageContainerBean();
OfflineMessageBean entity = new OfflineMessageBean();
entity.content = message.getExtra().toString();
entity.sender = message.getFromUser();
entity.nickname = chatInfo.getChatName();
entity.faceUrl = TUIChatConfigs.getConfigs().getGeneralConfig().getUserFaceUrl();
containerBean.entity = entity;
V2TIMOfflinePushInfo v2TIMOfflinePushInfo = new V2TIMOfflinePushInfo();
v2TIMOfflinePushInfo.setExt(new Gson().toJson(containerBean).getBytes());
// OPPO必须设置ChannelID才可以收到推送消息,这个channelID需要和控制台一致
v2TIMOfflinePushInfo.setAndroidOPPOChannelID("tuikit");
final V2TIMMessage v2TIMMessage = message.getTimMessage();
String msgID = V2TIMManager.getMessageManager().sendMessage(v2TIMMessage, isGroup ? null : userID, isGroup ? groupID : null,
V2TIMMessage.V2TIM_PRIORITY_DEFAULT, false, v2TIMOfflinePushInfo, new V2TIMSendCallback<V2TIMMessage>() {
@Override
public void onProgress(int progress) {
}
@Override
public void onError(int code, String desc) {
TUIChatUtils.callbackOnError(callBack, TAG, code, desc);
}
@Override
public void onSuccess(V2TIMMessage v2TIMMessage) {
TUIChatLog.v(TAG, "sendMessage onSuccess:" + v2TIMMessage.getMsgID());
message.setMsgTime(v2TIMMessage.getTimestamp());
TUIChatUtils.callbackOnSuccess(callBack, message);
}
});
当手机收到离线推送消息时,会在系统通知栏里展示收到的推送消息。单击通知栏的消息时,会自动跳转到您在步骤四配置的界面,您可以在该界面通过调用 getIntent().getExtras() 获取您在 步骤6 中配置的离线推送参数。示例代码可以参见 TUIKitDemo 的 handleOfflinePush() 方法。
private void handleOfflinePush() {
// 根据登录状态,判断是否需要重新登录 IM
// 1. 如果登录状态为 V2TIMManager.V2TIM_STATUS_LOGOUT,跳转到登录界面,重新登录 IM
if (V2TIMManager.getInstance().getLoginStatus() == V2TIMManager.V2TIM_STATUS_LOGOUT) {
Intent intent = new Intent(MainActivity.this, SplashActivity.class);
if (getIntent() != null) {
intent.putExtras(getIntent());
}
startActivity(intent);
finish();
return;
}
// 2. 否则,说明 App 只是处于退后台的状态,直接解析离线推送参数
final OfflineMessageBean bean = OfflineMessageDispatcher.parseOfflineMessage(getIntent());
if (bean != null) {
setIntent(null);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (manager != null) {
manager.cancelAll();
}
if (bean.action == OfflineMessageBean.REDIRECT_ACTION_CHAT) {
if (TextUtils.isEmpty(bean.sender)) {
return;
}
TUIUtils.startChat(bean.sender, bean.nickname, bean.chatType);
}
}
}
注意:
- FCM 单击通知栏的消息会默认跳转至应用的默认 Launcher 界面,该界面可以通过调用 getIntent().getExtras() 获取您在 步骤6 中配置的离线推送参数,此处进行解析和自定义跳转。
以上完成后,当您的应用退到后台或者进程被杀掉时,消息会进行离线推送通知栏展示,可单击通知栏跳转到设定的应用界面,完成实现离线推送功能。
加入腾讯云即时通信 IM 技术交流群,您将获得:
Telegram交流群:点击加入
Disacord交流群:点击加入
从 SDK 6.1.2155 版本开始,支持设置自定义铃音,支持机型有华为、小米、FCM 和 APNS。方法参见:V2TIMOfflinePushInfo 接口 setAndroidSound() 和 setIOSSound()。
OPPO 手机收不到推送一般有以下几种情况:
自定义消息的离线推送和普通消息不太一样,自定义消息的内容我们无法解析,不能确定推送的内容,所以默认不推送,如果您有推送需求,需要您在 sendMessage 的时候设置 offlinePushInfo 的 desc 字段,推送的时候会默认展示 desc 信息。
离线推送的直观表现就是通知栏提示,所以同其他通知一样受设备通知相关设置的影响,以华为为例:
单击离线推送消息的通知栏,跳转到指定界面,原理是后台根据您在控制台配置的各个厂商的跳转方式和界面参数,根据厂商接口规则,传递给厂商服务器,单击时候进行对应界面启动跳转。对应界面启动还依赖清单文件的配置,必须和控制台配置的相对应,才能正确启动和跳转。
1、首先需要重点排查下控制台和清单文件相关配置是否对应且正确,可参见 TUIKitDemo 的配置,注意部分厂商提供接口方式存在差异。
2、如果跳转到了配置的界面,需要再看下配置界面内离线消息的解析和界面重定向是否正常。
1、国内厂商都有消息分类机制,不同类型也会有不同的推送策略。如果想要推送及时可靠,需要按照厂商规则设置自己应用的推送类型为高优先级的系统消息类型或者重要消息类型。反之,离线推送消息会受厂商推送消息分类影响,与预期会有差异。
2、另外,一些厂商对于应用每天的推送数量也是有限制的,可以在厂商控制台查看应用每日限制的推送数量。
如果离线推送消息出现推送不及时或者偶尔收不到情况,需要考虑下这里:
本页内容是否解决了您的问题?