一、简介
即时通信 IM 的终端用户需要随时都能够得知最新的消息,而由于移动端设备的性能与电量有限,当 App 处于后台时,为了避免维持长连接而导致的过多资源消耗,即时通信 IM 推荐您使用各厂商提供的系统级推送通道来进行消息通知,系统级的推送通道相比第三方推送拥有更稳定的系统级长连接,可以做到随时接受推送消息,且资源消耗大幅降低。
本篇主要介绍:在tuikit上如何快速跑通华为离线推送,实现如下功能点,顺便记录一下,在集成过程中,容易踩到的坑点
1、退后台/杀进程,接收离线消息 2、离线消息透传ext字段 3、通知栏点击,打开应用内界面 4、app角标计数刷新
二、官网集成
所有的集成流程,在IM官网已经有介绍,细心的同学参考官网流程,即可跑通代码。
本篇直接从tuikit demo入手,快速运行集成运行华为离线推送。
准备工作:
1、下载tuikit demo; 2、申请好华为离线推送账号; 3、参考IM官网,完成im控制台的一些简单配置; 4、将配置后的各个id,替换到tuikit demo中去 5、搞定
离线推送效果如下图
三、集成操作
1、下载tuikit demo,并运行起来
tuikit已经将华为离线推送必要的库都集成好了,我们只需要替换掉tuikit中的几个配置参数,就能运行起来。
需要替换的几个配置参数,下面会逐一指明。
如果您已经熟悉了tuikit,需要自行集成,可以参考华为官网集成文档
2、申请好华为离线推送账号
如下图,申请好了账号,下载agconnect-services.json文件,替换到tuikit demo中去
如果您申请的包名不是com.tencent.qcloud.tim.tuikit,而是自己的app包名,可以修改demo的包名appliationId。
华为离线推送账号,有些项目申请时需要企业资质,由华为审核,需要几个工作日,记得提前申请。
3、im控制台一些简单配置
如下图,将华为开发者控制台上,申请到的离线推送账号信息,填到IM控制台,会生成一个buzid证书。
将buzid、appid填入demo中
4、将离线推送账号配置参数,写入demo中
如上已经配置齐活了,一共3点,替换agconnect-services.json文件、写入buzid、写入appid。
5、搞定
要运行起来,必需使用release包,签名文件的sha256指纹证书,要与华为控制台配置的sha256证书一致。如果不一致,代码中运行到注册华为离线推送时,会报6003错误。
代码语言:javascript复制HmsInstanceId.getInstance(MainActivity.this).getToken(appId, "HCM");
四、离线推送流程
如下图,“发送方”发一条消息给“接收方”,消息投递流程。
首先集成:终端app集成华为pushsdk、集成imsdk,调用pushsdk申请token,调用imsdk上报token到im服务端
1、消息到了IM服务端,IM服务端会判断接收方userid的在线状态;
2、如果接收方是login登入状态、而且应用在前台,这种后台标记状态是Online,消息就直接发到接收端imsdk。这就是在线消息。
3、如果接收方是login登入状态、但是应用退后台、进程还活着,这种后台标记也是Online,消息会同时发给接收端imsdk、发给华为离线推送后台,华为离线推送后台,会再给手机推送这条消息。
4、如果接收方是login登入状态,但是应用退后台、进程被杀死了,这种后台标记是PushOnline,消息不会再发给接收端imsdk了,只会发送给华为离线推送后台,华为离线推送后台再转推这条消息,弹出系统通知栏。这就是离线消息。
5、如果接收方已经logout登出了,这种后台标记是Offline,消息不会向下投递了,会存在漫游服务器中,接收端在有效期内,login登入了,再通过拉漫游接口获取这些消息。
- 敲黑板!
- 敲黑板!!!
- 敲黑板!!!!!!!
第2点与第3点,后台标记同样是Online状态,推送策略却不一样,这是受imsdk终端api触发的:终端退后台时,调用了doBackground接口,后台就会推在线消息的同时,也给推离线消息。
代码语言:javascript复制//APP 检测到应用退后台时可以调用此接口,可以用作桌面应用角标的初始化未读数量。
//从5.0.1版本开始,如果配置了离线推送,会收到厂商的离线推送通道下发的通知栏消息。
V2TIMManager.getOfflinePushManager().doBackground(param)
五、离线推送,透传ext字段
注意: 由于华为推送的兼容性问题,透传内容只能在部分 EUI10 的设备上收到。
步骤1:发送端设置自定义内容
在发消息前设置每条消息的通知栏自定义内容。
下面是 Android 端简单示例,也可以参考 TUIKit 中 ChatManagerKit.java 类的 sendMessage() 方法中对应的逻辑:
代码语言:javascript复制OfflineMessageContainerBean containerBean = new OfflineMessageContainerBean();
OfflineMessageBean entity = new OfflineMessageBean();
entity.content = message.getExtra().toString();
entity.sender = message.getFromUser();
entity.nickname = TUIKitConfigs.getConfigs().getGeneralConfig().getUserNickname();
entity.faceUrl = TUIKitConfigs.getConfigs().getGeneralConfig().getUserFaceUrl();
containerBean.entity = entity;
V2TIMOfflinePushInfo v2TIMOfflinePushInfo = new V2TIMOfflinePushInfo();
v2TIMOfflinePushInfo.setExt(new Gson().toJson(containerBean).getBytes());
V2TIMManager.getMessageManager().sendMessage(v2TIMMessage, userID, null,
V2TIMMessage.V2TIM_PRIORITY_DEFAULT, false, v2TIMOfflinePushInfo, V2TIMSendCallback)
- 服务端示例请参见 OfflinePushInfo 的格式示例
步骤2:接收端获取自定义内容
- 若 添加证书 时设置【点击通知后】的操作为【打开应用】或【打开应用内指定界面】,当点击通知栏的消息时,客户端可以在相应的
Activity
中获取自定义内容,可以参考 OfflineMessageDispatcher.java 类的 parseOfflineMessage(Intent intent) 方法实现。
Bundle bundle = getIntent().getExtras();
String value = bundle.getString("ext");
Log.i(TAG, "push custom data ext: " ext);
//输出push custom data ext:
{
"entity":{
"action":1,
"chatType":1,
"content":"这是一条含有自定义ext字段的消息",
"faceUrl":"",
"nickname":"",
"sendTime":0,
"sender":"chaoli01",
"version":1
}
}
六、通知栏点击动作
参考IM官网配置
这里给出示例,演示点击打开应用内界面:
代码语言:javascript复制<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.tencent.qcloud.tim.demo">
......
<activity
android:name="com.tencent.qcloud.tim.demo.chat.ChatActivity"
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize|stateHidden">
<!-- 华为离线推送打开应用内页面 -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data
android:host="com.tencent.qcloud.tim"
android:path="/detail"
android:scheme="pushscheme" />
</intent-filter>
</activity>
......
</manifest>
七、app角标刷新
- 手机系统控制角标
厂商推送离线消息到手机,手机系统会触发刷新通知栏、app角标,在手机系统设置里面打开/关闭角标功能,就可以了。
一条离线消息,对应一条通知栏信息,对应一个角标数字 1
- 代码控制角标
我们先看看华为离线推送文档,对于终端代码控制角标的描述。
当点击通知栏消息,唤起应用,才能在app内通过代码控制角标,主要是需要处理消除角标,tuikit demo中有监听消息已读上报,已读成功了就调用华为api,消除app角标,代码如下:
代码语言:javascript复制public class DemoApplication extends Application {
@Override
public void onCreate() {
DemoLog.i(TAG, "onCreate:DemoApplication");
super.onCreate();
registerActivityLifecycleCallbacks(new StatisticActivityLifecycleCallback());
}
class StatisticActivityLifecycleCallback implements ActivityLifecycleCallbacks {
private int foregroundActivities = 0;
private boolean isChangingConfiguration;
private IMEventListener mIMEventListener = new IMEventListener() {
@Override
public void onNewMessage(V2TIMMessage msg) {
String version = TIMManager.getInstance().getVersion();
int i = Integer.parseInt(version.substring(0,1));
//imsdk 5.0以上的版本,当app退后台时调用doBackground,im后台就会同时推送在线消息和离线消息。
//当sdk版本大于等于5.0时,默认退后台不触发在线消息的自定义通知栏。
if(i<4){
MessageNotification notification = MessageNotification.getInstance();
notification.notify(msg);
}
}
};
private ConversationManagerKit.MessageUnreadWatcher mUnreadWatcher = new ConversationManagerKit.MessageUnreadWatcher() {
@Override
public void updateUnread(int count) {
// 华为离线推送角标
HUAWEIHmsMessageService.updateBadge(DemoApplication.this, count);
}
};
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
DemoLog.i(TAG, "onActivityCreated bundle: " bundle);
}
@Override
public void onActivityStarted(Activity activity) {
foregroundActivities ;
if (foregroundActivities == 1 && !isChangingConfiguration) {
// 应用切到前台
DemoLog.i(TAG, "application enter foreground");
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");
}
});
TUIKit.removeIMEventListener(mIMEventListener);
ConversationManagerKit.getInstance().removeUnreadWatcher(mUnreadWatcher);
MessageNotification.getInstance().cancelTimeout();
}
isChangingConfiguration = false;
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
foregroundActivities--;
if (foregroundActivities == 0) {
// 应用切到后台
DemoLog.i(TAG, "application enter background");
int unReadCount = ConversationManagerKit.getInstance().getUnreadTotal();
V2TIMManager.getOfflinePushManager().doBackground(unReadCount, 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");
}
});
// 应用退到后台,消息转化为系统通知
TUIKit.addIMEventListener(mIMEventListener);
ConversationManagerKit.getInstance().addUnreadWatcher(mUnreadWatcher);
}
isChangingConfiguration = activity.isChangingConfigurations();
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
}
}
public class HUAWEIHmsMessageService extends HmsMessageService {
public static void updateBadge(final Context context, final int number) {
if (!BrandUtil.isBrandHuawei()) {
return;
}
DemoLog.i(TAG, "huawei badge = " number);
try {
Bundle extra = new Bundle();
extra.putString("package", "com.tencent.qcloud.tim.tuikit");
extra.putString("class", "com.tencent.qcloud.tim.demo.SplashActivity");
extra.putInt("badgenumber", number);
context.getContentResolver().call(Uri.parse("content://com.huawei.android.launcher.settings/badge/"), "change_badge", null, extra);
} catch (Exception e) {
DemoLog.w(TAG, "huawei badge exception: " e.getLocalizedMessage());
}
}
}
角标刷新效果如下:
八、总结&常见问题
1、tuikit已经集成好了华为离线推送,参考上文3步配置,就可以快速运行起来华为推送。
2、注册华为离线推送账户,需要企业资质,需要几个工作日审批,请提前申请。
3、华为离线推送,必需要release包,签名文件的sha256指纹证书,要添加到华为控制台。
4、指纹证书不对,运行到申请token时,会报错6003
5、手机硬件的token是固定的,如果您的app事先已经集成了极光推送等其他pushsdk,已经申请到了token,可以直接把token交给imsdk去上报。可以正常跑通im离线推送,不过不建议这样集成多个离线推送sdk,有一个稳定的离线推送功能就足够了。
6、imsdk在5.0以上的版本,退后台调用doBackground接口,im后台会推送在线消给app内imsdk,推离线消息给厂商,厂商再推离线消息给手机系统。tuikit当前版本监听了应用前后台状态ActivityLifecycleCallbacks,请注意修改sdk版本判断逻辑,避免退后台同时收到在线、离线消息。
7、由于华为推送的兼容性问题,自定义ext字段,只能在部分 EUI10 的设备上收到
8、进程被杀,厂商推送离线消息到来,弹出的通知栏行为、弹出顶部横幅行为、通知栏样式、震动、提示音、角标增加,都是系统行为,控制开关在手机系统设置里面。
9、imsdk4.8以上的版本,才支持刷新角标,这是在im控制台配置的,配置app首个页面activity的全名称。
10、部分华为机型,杀进程后收到离线消息15 ,角标增加会偶现少1个,不太准。线上开发如果遇到这个问题,im技术支持团队,可以帮忙协助排查的信息有:im后台推送给厂商的信息条数是否足够,厂商接收消息是否成功了。至于厂商投递消息是否成功、厂商投递成功了是否有正常触发角标数 1,是需要咨询华为离线推送支持人员。
11、角标数置0:tuikit目前逻辑是,监听消息已读,调用华为刷新角标api,将角标数置0。目前上报已读的逻辑是:当上报了最新一条消息为已读,那么这条消息之前所有的消息,都置为已读。
12、通知栏点击动作:启动应用、打开网页、跳转到应用内界面,只需要在im控制台修改配置即可,几分钟就能生效。
13、配置打开应用内界面,参考文档配置正确即可。如果是调试tuikit,注意tuikit默认是启动应用,需要修改两处代码:①、DemoApplication里面默认打开SplashActivity滑动界面,②、ChatActivity里面默认判断intent.getExtras,如果是空就跳转到SplashActivity滑动界面,可以在EUI10 的设备上调试,使能收到自定义ext字段,就能正常打开停留在ChatActivity界面。
14、写作日期:2021/1/13日,以上部分功能点,后续可能会升级,文章会同步/滞后更新。