IMSDK华为离线推送快速调试

2021-01-14 11:19:36 浏览数 (1)

一、简介

即时通信 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) 方法实现。
代码语言:javascript复制
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日,以上部分功能点,后续可能会升级,文章会同步/滞后更新。

0 人点赞