腾讯云即时通信IMAndroid_AI解决方案_同尘科技

即时通信 IM 1年前 (2023-12-19) 浏览 94

概述

即时通信 IM 的终端用户需要随时都能够得知最新的消息,而由于移动端设备的性能与电量有限,当 App 处于后台时,为了避免维持长连接而导致的过多资源消耗,即时通信 IM 推荐您使用各厂商提供的系统级推送通道来进行消息通知,系统级的推送通道相比第三方推送拥有更稳定的系统级长连接,可以做到随时接受推送消息,且资源消耗大幅降低。下面是自主集成推送方式指引,仅支持普通消息推送功能。如果您想快速集成推送能力,并有需求场景是将营销广告、通知、新闻咨询等推送给所有用户或者指定群体,还需要消息推送的全链路排查工具、推送记录和各类型指标统计数据,旨在查看和提升推送触达率、点击率和转化率等各类指标,请关注接入消息推送插件。

接入方法 集成方式对比 功能对比
消息推送插件TIMPush 插件快速集成:不再需要逐个厂商填写配置,控制台下载引入 json 配置文件,即可完成所有手机厂商的推送信息配置;支持按需集成一个或者多个对应厂商的推送渠道包,更加满足合规要求;不需要客户处理推送注册、token 上报、前后台状态上报等,接入后插件自动闭环;不再需要关注和添加打点和上报逻辑,开启功能后插件自行上报和汇总,支持链路排查和指标统计等;插件封装界面跳转、图标自定义等方法,直接使用即可; 普通消息推送全员标签推送自定义界面跳转推送自定义样式设备推送状态查询全链路排查工具推送记录和指标统计分析
自行集成 按照本篇文档自行集成 普通消息推送

注意在没有主动退出登录的情况下,应用退后台、手机锁屏、或者应用进程被用户主动杀掉三种场景下,如果想继续接收到 IM 消息提醒,可以接入即时通信 IM 离线推送。如果应用主动调用 logout 退出登录,或者多端登录被踢下线,即使接入了 IM 离线推送,也收不到离线推送消息。

跑通离线推送功能

TUIKitDemo 已经按照如下步骤接入了离线推送功能,文档中已有源码指引链接可供参考。

步骤1:注册应用到厂商推送平台

离线推送功能依赖厂商原始通道,您需要将自己的应用注册到各个厂商的推送平台,得到 AppID 和 AppKey 等参数。目前国内支持的手机厂商有:小米、华为、荣耀、OPPO、VIVO、魅族,境外支持 Google FCM。

步骤2:IM 控制台配置

登录腾讯云 即时通信 IM 控制台 ,添加各个厂商推送证书,并将您在步骤1中获取的各厂商的 AppId、AppKey、AppSecret 等参数配置给 IM 控制台的推送证书,其中单击后续动作参见步骤3。小米华为荣耀OPPOvivo魅族Google FCM

厂商推送平台 IM 控制台配置
 



厂商推送平台 IM 控制台配置
 注:Client ID 对应 AppID,Client Secret 对应 AppSecret 



厂商推送平台 IM 控制台配置
 



厂商推送平台 IM 控制台配置
 



厂商推送平台 IM 控制台配置
 



厂商推送平台 IM 控制台配置
 



厂商推送平台 IM 控制台配置
 


注意对于小米厂商,如果在厂商开发者官网配置了 ChannelID,需要在 即时通信 IM 控制台 配置同样的 ChannelID,否则可能推送不成功。

步骤3:配置离线推送跳转界面

收到离线推送后,通知栏会显示推送信息如图所示,单击通知栏会打开应用并进入配置的跳转界面。请您参见下面的步骤,配置单击通知消息后跳转的 Activity。
控制台配置
各个厂商的跳转界面配置方式有所不同,具体如下:

厂商 单击后后续动作 应用内指定界面
小米 打开应用内指定页面 intent://您配置的 hostname/您配置的 path#Intent;scheme=您配置的协议,也就是您定义的 scheme;launchFlags=0x4000000;component=您应用跳转界面的完整类名;endTUIKitDemo 配置的是: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=您应用跳转界面的完整类名;endTUIKitDemo 配置的是: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=您应用跳转界面的完整类名;endTUIKitDemo 配置的是: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=您应用跳转界面的完整类名;endTUIKitDemo 配置的是: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 控制台推送证书的单击后续动作配置保持一致。

 


步骤4:配置厂商推送规则

应用离线参数配置
步骤2推送证书添加成功之后,IM 控制台会为您分配一个对应厂商的证书 ID,请您进行本地保存。在登录完成后注册推送服务时候,将获取的厂商 token和该证书 ID 通过接口 setOfflinePushConfig 给到后台。以小米为例:
推送证书 ID 如下:
本地保存证书 ID 和推送参数:

public class PrivateConstants {/****** 小米离线推送参数start ******/// 在腾讯云控制台上传第三方推送证书后分配的证书 IDpublic static final long XM_PUSH_BUZID = 您应用分配的证书 ID;// 小米开放平台分配的应用APPID及APPKEYpublic static final String XM_PUSH_APPID = "您应用分配的 APPID";public static final String XM_PUSH_APPKEY = "您应用分配的 APPKEY";/****** 小米离线推送参数end ******/}

清单文件配置厂商推送权限相关
清单文件中需要添加各个厂商的推送规则以及推送继承类,具体如下:小米华为荣耀OPPOvivo魅族Google FCM


uses-permission android:name="xxxx.permission.MIPUSH_RECEIVE" />


                

    android:name="com.vivo.push.sdk.LinkProxyClientActivity"    android:exported="false"    android:screenOrientation="portrait"    android:theme="@android:style/Theme.Translucent.NoTitleBar" />                        






vivo 和荣耀适配
根据 vivo 和荣耀厂商接入指引,需要将 APPID 和 APPKEY 添加到清单文件中,否则会出现编译问题:方法1方法2

android {    ...    defaultConfig {            ...        manifestPlaceholders = [                "VIVO_APPKEY" : "您应用分配的证书 APPKEY",                "VIVO_APPID" : "您应用分配的证书 APPID"                "HONOR_APPID" : "您应用分配的证书 APPID"        ]    }}
// vivo begin                        
// vivo end
// honor begin 
// honor end

华为和 Google FCM 适配
华为和 Google FCM 需要按照厂商方法,集成对应的 plugin 和 json 配置文件。1.1 下载配置文件添加到工程根目录。

华为 Google FCM
 

1.2 在项目级 build.gradle 文件中 buildscript -> dependencies 下添加以下配置:

classpath 'com.google.gms:google-services:4.2.0'classpath 'com.huawei.agconnect:agcp:1.4.1.300'

在项目级 build.gradle 文件中 allprojects -> repositories 下添加以下配置:

mavenCentral()// 配置HMS Core SDK的Maven仓地址。maven {url 'https://developer.huawei.com/repo/'}

添加后效果如下:

repositories {... // 配置HMS Core SDK的Maven仓地址。 maven {url 'https://developer.huawei.com/repo/'}}
dependencies {...classpath 'com.google.gms:google-services:4.2.0'classpath 'com.huawei.agconnect:agcp:1.4.1.300'}

1.3 在应用级 build.gradle 文件中添加下方配置。

apply plugin: 'com.google.gms.google-services'apply plugin: 'com.huawei.agconnect'

1.4 单击项目右上角 Sync Now 同步项目。

步骤5:集成厂商推送 SDK

集成 SDK
在应用的 build.gradle 文件中添加厂商推送 SDK。

dependencies { ...... // 华为 implementation 'com.tencent.timpush:huawei:7.7.5282' // Google FCM implementation 'com.tencent.timpush:fcm:7.7.5282' // 小米 implementation 'com.tencent.timpush:xiaomi:7.7.5282' // OPPO implementation 'com.tencent.timpush:oppo:7.7.5282' // vivo implementation 'com.tencent.timpush:vivo:7.7.5282' // honor implementation 'com.tencent.timpush:honor:7.7.5282' // 魅族 implementation 'com.tencent.timpush:meizu:7.7.5282'}

添加推送类
引入厂商推送类,各个厂商推送方式有区别,具体如下:小米华为荣耀OPPOvivo魅族Google FCM

package xxx.xxx.xxx;

import android.content.Context;
import android.text.TextUtils;
import com.tencent.qcloud.tim.tuiofflinepush.utils.TUIOfflinePushErrorBean;
import android.util.Log;
import com.xiaomi.mipush.sdk.ErrorCode;
import com.xiaomi.mipush.sdk.MiPushClient;
import com.xiaomi.mipush.sdk.MiPushCommandMessage;
import com.xiaomi.mipush.sdk.MiPushMessage;
import com.xiaomi.mipush.sdk.PushMessageReceiver;
import java.util.List;
import java.util.Map;

public class XiaomiMsgReceiver extends PushMessageReceiver {
    private static final String TAG = XiaomiMsgReceiver.class.getSimpleName();
    private String mRegId;

    @Override
    public void onReceivePassThroughMessage(Context context, MiPushMessage miPushMessage) {
        Log.d(TAG, "onReceivePassThroughMessage is called. ");
    }

    @Override
    public void onNotificationMessageClicked(Context context, MiPushMessage miPushMessage) {
        Log.d(TAG, "onNotificationMessageClicked miPushMessage " + miPushMessage.toString());

        if (OEMPushSetting.mPushCallback == null) {
            return;
        }

        Map extra = miPushMessage.getExtra();
        String ext = extra.get("ext");
        if (TextUtils.isEmpty(ext)) {
            Log.w(TAG, "onNotificationMessageClicked: no extra data found");
            return;
        }        // IM 控制台点击后续动作选择为"打开应用",点击通知栏会回调此处               
    }

    @Override
    public void onNotificationMessageArrived(Context context, MiPushMessage miPushMessage) {
        Log.d(TAG, "onNotificationMessageArrived is called. ");
    }

    @Override
    public void onReceiveRegisterResult(Context context, MiPushCommandMessage miPushCommandMessage) {
        Log.d(TAG, "onReceiveRegisterResult is called. " + miPushCommandMessage.toString());
        String command = miPushCommandMessage.getCommand();
        List arguments = miPushCommandMessage.getCommandArguments();
        String cmdArg1 = ((arguments != null && arguments.size() > 0) ? arguments.get(0) : null);

        Log.d(TAG,
            "cmd: " + command + " | arg: " + cmdArg1 + " | result: " + miPushCommandMessage.getResultCode() + " | reason: " + miPushCommandMessage.getReason());

        if (MiPushClient.COMMAND_REGISTER.equals(command)) {
            if (miPushCommandMessage.getResultCode() == ErrorCode.SUCCESS) {
                mRegId = cmdArg1;
            }
        }

        Log.d(TAG, "regId: " + mRegId);
        // 获取 token 后调用接口上报 IM 后台                   
    }

    @Override
    public void onCommandResult(Context context, MiPushCommandMessage miPushCommandMessage) {
        super.onCommandResult(context, miPushCommandMessage);
    }
}

package xxx.xxx.xxx;

import com.huawei.hms.push.HmsMessageService;
import com.huawei.hms.push.RemoteMessage;
import com.tencent.qcloud.tim.tuiofflinepush.TUIOfflinePushConfig;
import com.tencent.qcloud.tim.tuiofflinepush.utils.TUIOfflinePushErrorBean;
import android.util.Log;

public class HUAWEIHmsMessageService extends HmsMessageService {
    private static final String TAG = HUAWEIHmsMessageService.class.getSimpleName();

    @Override
    public void onMessageReceived(RemoteMessage message) {
        Log.i(TAG, "onMessageReceived message=" + message);
    }

    @Override
    public void onMessageSent(String msgId) {
        Log.i(TAG, "onMessageSent msgId=" + msgId);
    }

    @Override
    public void onSendError(String msgId, Exception exception) {
        Log.i(TAG, "onSendError msgId=" + msgId);
    }

    @Override
    public void onNewToken(String token) {
        Log.i(TAG, "onNewToken token=" + token);
        // 获取 token 后调用接口上报 IM 后台
    }

    @Override
    public void onTokenError(Exception exception) {
        Log.i(TAG, "onTokenError exception=" + exception);
    }

    @Override
    public void onMessageDelivered(String msgId, Exception exception) {
        Log.i(TAG, "onMessageDelivered msgId=" + msgId);
    }
}



package xxx.xxx.xxx;

import android.text.TextUtils;
import com.hihonor.push.sdk.HonorMessageService;
import com.hihonor.push.sdk.bean.DataMessage;
import android.util.Log;

public class MyHonorMessageService extends HonorMessageService {
    private static final String TAG = MyHonorMessageService.class.getSimpleName();

    @Override
    public void onNewToken(String token) {
        Log.i(TAG, "onNewToken token=" + token);

        if (TextUtils.isEmpty(token)) {
            return;
        }

        // 获取 token 后调用接口上报 IM 后台
    }

    @Override
    public void onMessageReceived(DataMessage dataMessage) {
        Log.i(TAG, "onMessageReceived message=" + dataMessage);
    }
}

package xxx.xxx.xxx;

import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
import com.heytap.msp.push.callback.ICallBackResultService;
import com.tencent.qcloud.tim.tuiofflinepush.utils.TUIOfflinePushErrorBean;
import android.util.Log;

public class OPPOPushImpl implements ICallBackResultService {
    private static final String TAG = OPPOPushImpl.class.getSimpleName();

    @Override
    public void onRegister(int responseCode, String registerID) {
        Log.i(TAG, "onRegister responseCode: " + responseCode + " registerID: " + registerID);

        if (responseCode != 0) {
            // 打印报错 
        } else {
            // 获取 token 后调用接口上报 IM 后台
        }
    }

    @Override
    public void onUnRegister(int responseCode) {
        Log.i(TAG, "onUnRegister responseCode: " + responseCode);
    }

    @Override
    public void onSetPushTime(int responseCode, String s) {
        Log.i(TAG, "onSetPushTime responseCode: " + responseCode + " s: " + s);
    }

    @Override
    public void onGetPushStatus(int responseCode, int status) {
        Log.i(TAG, "onGetPushStatus responseCode: " + responseCode + " status: " + status);
    }

    @Override
    public void onGetNotificationStatus(int responseCode, int status) {
        Log.i(TAG, "onGetNotificationStatus responseCode: " + responseCode + " status: " + status);
    }

    @Override
    public void onError(int i, String s) {
        Log.i(TAG, "onError code: " + i + " string: " + s);
    }
}



package xxx.xxx.xxx;

import android.content.Context;
import android.util.Log;
import com.vivo.push.model.UPSNotificationMessage;
import com.vivo.push.sdk.OpenClientPushMessageReceiver;
import java.util.Map;

public class VIVOPushMessageReceiverImpl extends OpenClientPushMessageReceiver {
    private static final String TAG = VIVOPushMessageReceiverImpl.class.getSimpleName();

    private static String sExt = "";

    @Override
    public void onNotificationMessageClicked(Context context, UPSNotificationMessage upsNotificationMessage) {
        Log.i(TAG, "onNotificationMessageClicked upsNotificationMessage " + upsNotificationMessage.toString());
        Map extra = upsNotificationMessage.getParams();
        sExt = extra.get("ext");
    }

    public static String getParams() {
        String tmp = sExt;
        sExt = "";
        return tmp;
    }

    @Override
    public void onReceiveRegId(Context context, String regId) {
        Log.i(TAG, "onReceiveRegId = " + regId);
    }
}

package xxx.xxx.xxx;

import android.content.Context;
import android.content.Intent;
import com.meizu.cloud.pushsdk.MzPushMessageReceiver;
import com.meizu.cloud.pushsdk.handler.MzPushMessage;
import com.meizu.cloud.pushsdk.notification.PushNotificationBuilder;
import com.meizu.cloud.pushsdk.platform.message.PushSwitchStatus;
import com.meizu.cloud.pushsdk.platform.message.RegisterStatus;
import com.meizu.cloud.pushsdk.platform.message.SubAliasStatus;
import com.meizu.cloud.pushsdk.platform.message.SubTagsStatus;
import com.meizu.cloud.pushsdk.platform.message.UnRegisterStatus;
import android.util.Log;

public class MEIZUPushReceiver extends MzPushMessageReceiver {
    private static final String TAG = MEIZUPushReceiver.class.getSimpleName();

    @Override
    public void onMessage(Context context, String s) {
        Log.i(TAG, "onMessage method1 msg = " + s);
    }

    @Override
    public void onMessage(Context context, String message, String platformExtra) {
        Log.i(TAG, "onMessage method2 msg = " + message + ", platformExtra = " + platformExtra);
    }

    @Override
    public void onMessage(Context context, Intent intent) {
        String content = intent.getExtras().toString();
        Log.i(TAG, "flyme3 onMessage = " + content);
    }

    @Override
    public void onUpdateNotificationBuilder(PushNotificationBuilder pushNotificationBuilder) {
        super.onUpdateNotificationBuilder(pushNotificationBuilder);
    }

    @Override
    public void onNotificationClicked(Context context, MzPushMessage mzPushMessage) {
        Log.i(TAG, "onNotificationClicked mzPushMessage " + mzPushMessage.toString());
    }

    @Override
    public void onNotificationArrived(Context context, MzPushMessage mzPushMessage) {
        super.onNotificationArrived(context, mzPushMessage);
    }

    @Override
    public void onNotificationDeleted(Context context, MzPushMessage mzPushMessage) {
        super.onNotificationDeleted(context, mzPushMessage);
    }

    @Override
    public void onNotifyMessageArrived(Context context, String s) {
        super.onNotifyMessageArrived(context, s);
    }

    @Override
    public void onPushStatus(Context context, PushSwitchStatus pushSwitchStatus) {}

    @Override
    public void onRegisterStatus(Context context, RegisterStatus registerStatus) {
        Log.i(TAG, "onRegisterStatus token = " + registerStatus.getPushId());
        // 获取 token 后调用接口上报 IM 后台
    }

    @Override
    public void onUnRegisterStatus(Context context, UnRegisterStatus unRegisterStatus) {}

    @Override
    public void onSubTagsStatus(Context context, SubTagsStatus subTagsStatus) {}

    @Override
    public void onSubAliasStatus(Context context, SubAliasStatus subAliasStatus) {}

    @Override
    public void onRegister(Context context, String s) {}

    @Override
    public void onUnRegister(Context context, boolean b) {}
}

package xxx.xxx.xxx;

import com.google.firebase.messaging.FirebaseMessagingService;
import android.util.Log;

public class GoogleFCMMsgService extends FirebaseMessagingService {
    private static final String TAG = GoogleFCMMsgService.class.getSimpleName();

    @Override
    public void onNewToken(String token) {
        super.onNewToken(token);
        Log.i(TAG, "onNewToken google fcm onNewToken : " + token);
        // 获取 token 后调用接口上报 IM 后台
    }
}

推送服务注册
应合规要求,在用户同意隐私协议登录成功后,分别初始化注册各个厂商推送服务,并在注册结果回调处保存注册成功后的 token,并调用 setOfflinePushConfig 接口上报推送 token 至后台。部分厂商在注册后,调用一些接口也会返回 token,可以再次同步更新下,具体参见以下代码。

public void init() { ...
if (BrandUtil.isBrandXiaoMi()) { // 小米离线推送 MiPushClient.registerPush(this, PrivateConstants.XM_PUSH_APPID, PrivateConstants.XM_PUSH_APPKEY); } else if (BrandUtil.isBrandHonor()) { HonorMessaging.getInstance(context).turnOnPush().addOnCompleteListener(new OnCompleteListener() { @Override public void onComplete(com.hihonor.push.sdk.tasks.Task task) { if (task.isSuccessful()) { TUIOfflinePushLog.i(TAG, "Honor turnOn push successfully."); } else { TUIOfflinePushLog.i(TAG, "Honor turnOn push failed." + task.getException()); } } });
new Thread(new Runnable() { @Override public void run() { try { String pushToken = HonorInstanceId.getInstance(context).getPushToken(); TUIOfflinePushLog.i(TAG, "Honor get pushToken " + pushToken);
//判断pushToken是否为空 if(!TextUtils.isEmpty(token)) { // 该 token 需要保存并调用 setOfflinePushConfig 接口上报 IMSDK String pushToken = token; } } catch (com.hihonor.push.sdk.common.data.ApiException e) { TUIOfflinePushLog.i(TAG, "Honor get pushToken failed, " + e); } } }).start(); } else if (BrandUtil.isBrandHuawei()) { // 华为离线推送,设置是否接收Push通知栏消息调用示例 HmsMessaging.getInstance(this).turnOnPush().addOnCompleteListener(new com.huawei.hmf.tasks.OnCompleteListener() { @Override public void onComplete(com.huawei.hmf.tasks.Task 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 pushToken = token; } } 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 pushToken = token; } 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() { @Override public void onComplete(Task 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 pushToken = token; } }); }}

以华为为例,在注册结果回调处保存注册成功后的 token,并调用 setOfflinePushConfig 接口上报给后台。

public class HUAWEIHmsMessageService extends HmsMessageService {
...
@Override public void onNewToken(String token) { DemoLog.i(TAG, "onNewToken token=" + token);
// 该 token 需要保存并调用 setOfflinePushConfig 接口上报 IMSDK String pushToken = token; }
...}

推送证书和 token 上报后台
调用 setOfflinePushConfig 接口上报推送 token。构造 V2TIMOfflinePushConfig 类,需设置 businessID 为对应厂商的证书 ID,上报注册厂商推送服务获取的 token。

V2TIMOfflinePushConfig v2TIMOfflinePushConfig = null;// 需要设置 businessID 为对应厂商的证书 ID,上报注册厂商推送服务获取的 token。  v2TIMOfflinePushConfig = new V2TIMOfflinePushConfig(businessID, token);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"); }});

步骤6:前后台状态同步

如果您的应用退到后台,收到新消息时需要在手机通知栏进行展示,请您调用 IMSDK 的 doBackground() 接口,将应用的状态同步给 IM 后台;当应用回到前台时,请您调用 IMSDK 的 doForeground() 接口,将应用的状态同步给 IM 后台。

// 应用切到后台时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"); }});

步骤7:发消息时设置离线推送参数

调用 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() { @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); } });

步骤8:解析离线推送消息

收到离线推送的通知栏消息,点击会自动跳转到您在 步骤3 配置的跳转界面,可以在界面启动的 onResume() 方法中,调用 getIntent().getExtras() 获取透传的离线推送参数再自定义跳转。具体可以参照 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 界面,您可以在界面启动的 onResume() 方法中,通过调用 getIntent().getExtras() 获取透传的离线推送参数再自定义跳转。以上完成后,当您的应用退到后台或者进程被杀掉时,消息会进行离线推送通知栏展示,可单击通知栏跳转到设定的应用界面,完成实现离线推送功能。

离线推送自定义推送铃音

Android 8.0 以前系统设置,接口调用 setAndroidSound() 和 setIOSSound()。

1. 定制的铃音资源文件,Android 添加到工程 raw 目录下,iOS 链接进 Xcode 工程。2. 消息指定使用自定义的铃音。

V2TIMOfflinePushInfo v2TIMOfflinePushInfo = new V2TIMOfflinePushInfo();v2TIMOfflinePushInfo.setAndroidSound("铃音名称");v2TIMOfflinePushInfo.setIOSSound("铃音名称.mp3");
String msgID = V2TIMManager.getMessageManager().sendMessage(v2TIMMessage, isGroup ? null : userID, isGroup ? groupID : null, V2TIMMessage.V2TIM_PRIORITY_DEFAULT, false, v2TIMOfflinePushInfo, new V2TIMSendCallback() {@Override public void onProgress(int progress) { TUIChatUtils.callbackOnProgress(callBack, progress); }
@Override public void onError(int code, String desc) { TUIChatUtils.callbackOnError(callBack, TAG, code, desc); }
@Override public void onSuccess(V2TIMMessage v2TIMMessage) {
}});

注意IMSDK 6.1.2155 及以上版本支持。接口支持华为、小米、FCM 和 APNS。

Android 8.0 及以后系统设置需要通过 channel 实现。

华为 与 ANPS
华为、APNS 仍然调用 setAndroidSound() 和 setIOSSound() 来设置离线推送铃音提示。小米1.1 登录厂商控制台 创建 channel 并做好配置,其中铃音文件需要添加到您本地 Android Studio 工程的 raw 目录下。
1.2 发送消息指定自定义铃音的 channel ID,详见 setAndroidXiaoMiChannelID。

V2TIMOfflinePushInfo v2TIMOfflinePushInfo = new V2TIMOfflinePushInfo();v2TIMOfflinePushInfo.setAndroidXiaoMiChannelID("厂商申请的 channel ID");
String msgID = V2TIMManager.getMessageManager().sendMessage(v2TIMMessage, isGroup ? null : userID, isGroup ? groupID : null, V2TIMMessage.V2TIM_PRIORITY_DEFAULT, false, v2TIMOfflinePushInfo, new V2TIMSendCallback() {@Override public void onProgress(int progress) { TUIChatUtils.callbackOnProgress(callBack, progress); }
@Override public void onError(int code, String desc) { TUIChatUtils.callbackOnError(callBack, TAG, code, desc); }
@Override public void onSuccess(V2TIMMessage v2TIMMessage) {
}});

FCM1.1 创建需要自定义铃音的 channel。您需要在代码中先默认创建好带自定义铃音的 channel,铃音文件需要添加到您本地 Android Studio 工程的 raw 目录下,并记录下 channel ID 的名称。1.2 发送消息指定自定义铃音的 channel ID,详见 setAndroidFCMChannelID。

V2TIMOfflinePushInfo v2TIMOfflinePushInfo = new V2TIMOfflinePushInfo();v2TIMOfflinePushInfo.setAndroidFCMChannelID(PrivateConstants.fcmPushChannelId);
String msgID = V2TIMManager.getMessageManager().sendMessage(v2TIMMessage, isGroup ? null : userID, isGroup ? groupID : null, V2TIMMessage.V2TIM_PRIORITY_DEFAULT, false, v2TIMOfflinePushInfo, new V2TIMSendCallback() {@Override public void onProgress(int progress) { TUIChatUtils.callbackOnProgress(callBack, progress); }
@Override public void onError(int code, String desc) { TUIChatUtils.callbackOnError(callBack, TAG, code, desc); }
@Override public void onSuccess(V2TIMMessage v2TIMMessage) {
}});

注意IMSDK 7.0.3754 及以上版本支持。FCM 自定义铃声或者设置 channnel id 仅支持证书模式。

常见问题

收不到离线推送怎么排查?

OPPO 手机

OPPO 手机收不到推送一般有以下几种情况:按照 OPPO 推送官网要求,在 Android 8.0 及以上系统版本的 OPPO 手机上必须配置 ChannelID,否则推送消息无法展示。配置方法可以参见 setAndroidOPPOChannelID。OPPO 安装应用通知栏显示默认关闭,需要确认下开关状态。

Google FCM

收不到推送需要确认下 IM 控制台是否正确上传证书。排查路径参照文档 “IM 控制台配置 – Google FCM”,对照示意图看下是否添加正确。

发送消息为自定义消息

自定义消息的离线推送和普通消息不太一样,自定义消息的内容我们无法解析,不能确定推送的内容,所以默认不推送,如果您有推送需求,需要您在 sendMessage 的时候设置 offlinePushInfo 的 desc 字段,推送的时候会默认展示 desc 信息。

设备通知栏设置影响

离线推送的直观表现就是通知栏提示,所以同其他通知一样受设备通知相关设置的影响,以华为为例:“手机设置-通知-锁屏通知-隐藏或者不显示通知”,会影响锁屏状态下离线推送通知显示。“手机设置-通知-更多通知设置-状态栏显示通知图标”,会影响状态栏下离线推送通知的图标显示。“手机设置-通知-应用的通知管理-允许通知”,打开关闭会直接影响离线推送通知显示。“手机设置-通知-应用的通知管理-通知铃声” 和 “手机设置-通知-应用的通知管理-静默通知”,会影响离线推送通知铃音的效果。

按照流程接入完成,还是收不到离线推送

首先在 IM 控制台通过 离线测试工具 自测下是否可以正常推送。
推送异常情况,设备状态异常,需要检查下 IM 控制台配置各项参数是否正确,再者需要检查下代码初始化注册逻辑,包括厂商推送服务注册和 IM 设置离线推送配置相关逻辑是否正确设置。
推送异常情况,设备状态正常,需要看下是否需要正确填写 channel ID 或者后台服务是否正常。离线推送依赖厂商能力,一些简单的字符可能会被厂商过滤不能透传推送。如果离线推送消息出现推送不及时或者偶尔收不到情况,需要看下厂商的推送限制。

跳转界面不成功怎么排查?

单击离线推送消息的通知栏,跳转到指定界面,原理是后台根据您在控制台配置的各个厂商的跳转方式和界面参数,根据厂商接口规则,传递给厂商服务器,单击时候进行对应界面启动跳转。对应界面启动还依赖清单文件的配置,必须和控制台配置的相对应,才能正确启动和跳转。1. 首先需要重点排查下控制台和清单文件相关配置是否对应且正确,可参见 TUIKitDemo 的配置,注意部分厂商提供接口方式存在差异。2. 如果跳转到了配置的界面,需要再看下配置界面内离线消息的解析和界面重定向是否正常。

厂商推送限制

1. 国内厂商都有消息分类机制,不同类型也会有不同的推送策略。如果想要推送及时可靠,需要按照厂商规则设置自己应用的推送类型为高优先级的系统消息类型或者重要消息类型。反之,离线推送消息会受厂商推送消息分类影响,与预期会有差异。2. 另外,一些厂商对于应用每天的推送数量也是有限制的,可以在厂商控制台查看应用每日限制的推送数量。
如果离线推送消息出现推送不及时或者偶尔收不到情况,需要考虑下这里:华为:华为推送从 EMUI 10.0版本开始将通知消息智能分成两个级别:服务与通讯资讯营销。EMUI 10.0之前的版本没有对通知消息进行分类,只有一个级别,消息全部通过“默认通知”渠道展示,等价于 EMUI 10.0的服务与通讯。资讯营销类消息的每日推送数量自2023年01月05日起根据应用类型对推送数量进行上限管理,服务与通讯类消息每日推送数量不受限。另外,消息分类还和自分类权益有关:无自分类权益,推送消息厂商还会进行二次智能分类 。有申请自分类权益,且推送消息携带 category 字段,消息会按照自定义的分类进行推送,详见 setAndroidHuaWeiCategory。
具体请参见 厂商描述1 或 厂商描述2。荣耀:荣耀手机推送和系统版本有关。当前荣耀通道仅支持国内 Magic UI 4.0 及以上和海外 Magic UI 4.2 及以上荣耀设备使用。低于上述版本的荣耀设备可以按照华为厂商接入推送。
具体请参见 厂商描述。vivo:推送服务将于 2023 年 4 月 3 日起,优化消息分类规则,并调整不同消息类别的数量限制。关于消息分类优化,增加二级分类 category 字段,并根据不同二级分类配置不同推送速度。接口详见 setAndroidVIVOCategory 。category 默认值可前往 IM 控制台配置。关于消息数量限制调整,系统消息可申请不限量权限,不限制单应用单用户单日接收数量;运营消息单日可推送量级 = 通知开启的有效用户数 * 倍数,默认倍数为 2 倍,新闻资讯类应用为 3 倍。单应用单用户单日接收数量限制为 2 条,新闻资讯类应用为 5 条。
具体请参见 厂商描述1 或 厂商描述2。OPPO:将推送消息分为私信消息类和公信消息类,推送效果和策略不同。其中私信消息是针对用户有一定关注度,且希望能及时接收的信息,私信通道权益需要邮件申请。公信通道推送数量有限制。
具体请参见 厂商描述1 或 厂商描述2。小米:将推送消息分为“私信消息”和“公信消息”两个类别,默认通道为公信消息。公信消息的单日推送数量将进行上限管理,公信消息适用于推送热点新闻、新品推广、平台公告、社区话题、有奖活动等,多为用户普适性的内容。私信消息适用于推送聊天消息、个人订单变化、快递通知、交易提醒、IOT系统通知等与私人通知相关的内容,通知消息的推送数量不受限制。消息分类管理实现需要在厂商控制台进行 channel 申请及接入。
具体请参见 厂商描述1 或 厂商描述2。魅族:推送消息数量有限制。
具体请参见 厂商描述。FCM:推送上行消息频率有限制。
具体请参见 厂商描述。

对即时通讯IM解决方案有疑惑?想了解解决方案收费? 联系解决方案专家

腾讯云限时活动1折起,即将结束: 马上收藏

同尘科技为腾讯云授权服务中心,购买腾讯云享受折上折,更有现金返利:同意关联,立享优惠

阿里云解决方案也看看?: 点击对比阿里云的解决方案

- 0人点赞 -

发表点评 (0条)

not found

暂无评论,你要说点什么吗?