离线推送(华为)

最近更新时间:2019-09-04 19:32:38

流程说明

实现离线消息推送的过程如下:

  1. 开发者到厂商的平台注册账号,并通过开发者认证后,申请开通推送服务。
  2. 创建推送服务,并绑定应用信息,获取推送证书、密码、密钥等信息。
  3. 登录 即时通信 IM 控制台 填写推送证书及相关信息,即时通信 IM 服务端会为每个证书生成不同的证书 ID。
  4. 将厂商提供的推送 SDK 集成到开发者的项目工程中,并按各厂商的要求进行配置。
  5. 集成即时通信 IM SDK 到项目后,将证书 ID、设备信息等上报至即时通信 IM 服务端。
  6. 当客户端 App 在即时通信 IM 没有退出登录的情况下,被系统或者用户 kill 时,即时通信 IM 服务端将通过消息推送进行提醒。

华为推送

华为 EMUI 是一款深度定制的 Android 系统,后台策略严格,默认情况下第三方 App 不具有自启动权限,App 在后台时很容易被系统强制 kill,因此推荐在华为设备上集成华为推送服务,华为推送服务是华为消息服务(HMS)的一部分,在 EMUI 中属于系统级服务,推送到达率相比第三方更高。目前,即时通信 IM 仅支持华为推送的通知栏消息

注意:

  • 此指引文档是直接参考华为推送官方文档所写,若华为推送有变动,请以 华为推送官网文档 为准。
  • 如果不需要对华为设备做专门的离线推送适配,可以忽略此章节。

步骤1:申请华为推送证书

  1. 打开 华为开发者联盟官网 进行注册并通过开发者认证。
    说明:

    认证过程大约需要30分钟左右,请务必提前阅读 华为推送服务启用指南,以免影响您的接入进度。

  2. 登录华为开发者联盟的管理控制台,选择【应用服务】>【开发服务】>【PUSH】,创建华为推送服务应用。
    华为推送在申请 PUSH 服务时,需要您提供应用签名证书的 SHA256 指纹,最多允许添加5个。 华为推送服务应用创建完成后,在应用详情中,您可以查看详细的应用信息。
  3. 记录包名APP IDAPP Secret信息。

步骤2:托管证书信息到即时通信 IM

  1. 登录腾讯云 即时通信 IM 控制台 ,选择您的即时通信 IM 应用,进入应用配置页面。
  2. 在基础配置页签中,单击应用平台右侧的【编辑】。
  3. 勾选【Android】,单击【保存】。
  4. 单击【Android推送证书】区域的【添加证书】。
    说明:

    如果您原来已有的证书只需变更信息,可以单击【Android推送证书】区域【编辑】进行修改更新。

  5. 根据 步骤1 中获取的信息设置以下参数:
    • 推送平台:选择华为
    • 应用包名称:填写华为推送服务应用的包名
    • APPID:填写华为推送服务应用的 APP ID
    • AppSecret:填写华为推送服务应用的 APP SECRET
    • 点击通知后:选择点击通知栏消息后的响应操作,支持打开应用跳转到自定义页面,更多详情请参见 配置点击通知栏消息事件
  6. 单击【确定】保存信息,证书信息保存后10分钟内生效。
  7. 待推送证书信息生成后,记录证书 ID

步骤3:集成推送 SDK

说明:

  • 即时通信 IM 默认推送的通知标题为a new message
  • 阅读此小节前,请确保您已经正常集成并使用即时通信 IM SDK。
  • 您可以在我们的 demo 里找到华为推送的实现示例,请注意:华为推送版本更新时有可能会有功能调整,若您发现本节内容存在差异,烦请您及时查阅 华为推送官网文档,并将文档信息差异反馈给我们,我们会及时跟进修改。

步骤3.1:下载华为推送 SDK 并添加引用

  1. 访问 华为推送运营平台 下载HMS Agent 套件

  2. 解压 HMS Agent 套件。

  3. hmsagents\src\main\java文件夹内的文件拷贝到您项目的 src\main\java 目录中。

  4. 通过 Gradle 进行华为推送 SDK 集成,在您项目的 build.gradle 里添加以下代码:

    allprojects {
     repositories {
         jcenter()
         maven {url 'http://developer.huawei.com/repo/'}
     }
    }    
  5. 在子项目的 build.gradle 里添加以下信息:

    dependencies {
     // 华为推送 SDK,2.6.3.301可替换成实际所需要的版本
     implementation 'com.huawei.android.hms:push:2.6.3.301'
     // 如果碰到报错:com.huawei.hms.api 不存在,这条也需要加上,注意版本号必须相同
     // implementation 'com.huawei.android.hms:base:2.6.3.301'
    }

步骤3.2:配置 AndroidManifest.xml 文件

  1. 添加华为推送的必要权限:
<!--HMS-SDK 引导升级 HMS 功能,访问 OTA 服务器需要网络权限-->    
<uses-permission android:name="android.permission.INTERNET" />    
<!--HMS-SDK 引导升级 HMS 功能,保存下载的升级包需要 SD 卡写权限-->    
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />    
<!--检测网络状态-->  
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>  
<!--检测 Wi-Fi 状态-->  
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>  
<!--为了获取用户手机的 IMEI,用来唯一的标识用户。-->  
<uses-permission android:name="android.permission.READ_PHONE_STATE"/> 

<!--如果是Android 8.0,应用编译配置的 targetSdkVersion>=26,请务必添加以下权限 -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

<!--这里的 com.tencent.qcloud.tim.tuikit 改成您的 App 的包名-->
<permission
    android:name="com.tencent.qcloud.tim.tuikit.permission.PROCESS_PUSH_MSG"
    android:protectionLevel="signatureOrSystem"/>
<uses-permission android:name="com.tencent.qcloud.tim.tuikit.permission.PROCESS_PUSH_MSG" />
<!--这里的 com.tencent.qcloud.tim.tuikit 改成您的 App 的包名-->
  1. 在 application 下增加以下内容,具体说明请参见 华为推送配置 manifest 文件说明
<meta-data
    android:name="com.huawei.hms.client.appid"
    android:value="appid=1234567890"/> <!--这里的 appid 值修改为您的华为推送 App ID-->
<provider
    android:name="com.huawei.hms.update.provider.UpdateProvider"
    android:authorities="com.tencent.qcloud.tim.tuikit.hms.update.provider"
    android:exported="false"
    android:grantUriPermissions="true"/>
<provider
    android:name="com.huawei.updatesdk.fileprovider.UpdateSdkFileProvider"
    android:authorities="com.tencent.qcloud.tim.tuikit.updateSdk.fileProvider"
    android:exported="false"
    android:grantUriPermissions="true">
</provider>
<activity
    android:name="com.huawei.android.hms.agent.common.HMSAgentActivity"
    android:configChanges="orientation|locale|screenSize|layoutDirection|fontScale"
    android:excludeFromRecents="true"
    android:exported="false"
    android:hardwareAccelerated="true"
    android:theme="@android:style/Theme.Translucent" >
    <meta-data
        android:name="hwc-theme"
        android:value="androidhwext:style/Theme.Emui.Translucent" />
</activity>
<activity
    android:name="com.huawei.hms.activity.BridgeActivity"
    android:configChanges="orientation|locale|screenSize|layoutDirection|fontScale"
    android:excludeFromRecents="true"
    android:exported="false"
    android:hardwareAccelerated="true"
    android:theme="@android:style/Theme.Translucent" >
    <meta-data
        android:name="hwc-theme"
        android:value="androidhwext:style/Theme.Emui.Translucent" />
</activity>

<service
    android:name="com.huawei.hms.support.api.push.service.HmsMsgService"
    android:enabled="true"
    android:exported="true"
    android:process=":pushservice">
    <intent-filter>
        <action android:name="com.huawei.push.msg.NOTIFY_MSG" />
        <action android:name="com.huawei.push.msg.PASSBY_MSG" />
    </intent-filter>
</service>

步骤3.3:自定义一个 BroadcastReceiver 类

为了接收消息,您需要自定义一个继承自PushReceiver类的 BroadcastReceiver,我们只需要实现其中的onToken方法,然后将此 receiver 注册到 AndroidManifest.xml 中。

以下为 Demo 中的示例代码:

public class HUAWEIPushReceiver extends PushReceiver {
    private static final String TAG = "HUAWEIPushReceiver";

    @Override
    public void onToken(Context context, String token, Bundle extras) {
        Log.i(TAG, "onToken:" + token);
        ThirdPushTokenMgr.getInstance().setThirdPushToken(token);  // token 在此处传入,后续推送信息上报时需要使用
        ThirdPushTokenMgr.getInstance().setPushTokenToTIM();
    }
}

将自定义的 BroadcastReceiver 注册到 AndroidManifest.xml:

<!--这里的 com.tencent.qcloud.tim.demo.thirdpush.HUAWEIPushReceiver 修改成您 App 中的完整类名-->
<receiver android:name="com.tencent.qcloud.tim.demo.thirdpush.HUAWEIPushReceiver"
    android:permission="com.tencent.qcloud.tim.tuikit.permission.PROCESS_PUSH_MSG">
    <intent-filter>
        <!-- 必须,用于接收 token -->
        <action android:name="com.huawei.android.push.intent.REGISTRATION" />
        <!-- 必须, 用于接收透传消息 -->
        <action android:name="com.huawei.android.push.intent.RECEIVE" />
        <!-- 必须, 用于接收通知栏消息点击事件 此事件不需要开发者处理,只需注册就可以-->
        <action android:name="com.huawei.intent.action.PUSH_DELAY_NOTIFY"/>
    </intent-filter>
</receiver>

步骤3.4:在 App 中注册华为推送服务

如果您选择启用华为推送服务,需要在 Application 的onCreate中调用HMSAgent.init来对华为推送服务进行初始化。

注册成功后,您将在 步骤3.3 自定义的 BroadcastReceiver 的onToken中收到注册结果。其中token为当前设备上当前 App 的唯一标识,请记录token信息。

以下为 Demo 中的示例代码:

public class DemoApplication extends Application {

    private static PojoApplication instance;

    @Override
    public void onCreate() {
        super.onCreate();
        // 判断是否是在主线程
        if (SessionWrapper.isMainProcess(getApplicationContext())) {
            /**
             * TUIKit 的初始化函数
             *
             * @param context  应用的上下文,一般为对应应用的 ApplicationContext
             * @param sdkAppID 您在腾讯云注册应用时分配的 SDKAppID
             * @param configs  TUIKit 的相关配置项,一般使用默认即可,需特殊配置参考 API 文档
             */
            long current = System.currentTimeMillis();
            TUIKit.init(this, Constants.SDKAPPID, BaseUIKitConfigs.getDefaultConfigs());
            System.out.println(">>>>>>>>>>>>>>>>>>"+(System.currentTimeMillis()-current));
            // 添加自定初始化配置
            customConfig();
            System.out.println(">>>>>>>>>>>>>>>>>>"+(System.currentTimeMillis()-current));

            if(IMFunc.isBrandXiaoMi()){
                // 小米离线推送
                MiPushClient.registerPush(this, Constants.XM_PUSH_APPID, Constants.XM_PUSH_APPKEY);
            }
            if(IMFunc.isBrandHuawei()){
                // 华为离线推送
                HMSAgent.init(this);
            }
            if(MzSystemUtils.isBrandMeizu(this)){
                // 魅族离线推送
                PushManager.register(this, Constants.MZ_PUSH_APPID, Constants.MZ_PUSH_APPKEY);
            }
            if(IMFunc.isBrandVivo()){
                // vivo 离线推送
                PushClient.getInstance(getApplicationContext()).initialize();
            }
        }
        instance = this;
    }
}

在主界面中获取 token:

if (IMFunc.isBrandHuawei()) {
    // 华为离线推送
    HMSAgent.connect(this, new ConnectHandler() {
    @Override
    public void onConnect(int rst) {
        QLog.i(TAG, "huawei push HMS connect end:" + rst);
        }
    });
    getHuaWeiPushToken();
}

步骤4:上报推送信息至即时通信 IM 服务端

若您需要通过华为推送进行即时通信 IM 消息的推送通知,必须在用户登录成功后通过TIMManager中的setOfflinePushToken方法将您托管到即时通信 IM 控制台生成的证书 ID 及华为推送服务返回的 token 上报到即时通信 IM 服务端。

注意:

正确上报 token 与证书 ID 后,即时通信 IM 服务才能将用户与对应的设备信息绑定,从而使用华为推送服务进行推送通知。

以下为 Demo 中的示例代码:

定义证书 ID 常量:

/**
 * 我们先定义一些常量信息在 Constants.java
 */
/****** 华为离线推送参数 start ******/
 // 使用您在即时通信 IM 控制台上华为推送证书信息里的证书 ID
public static final long HW_PUSH_BUZID = 6666;
// 华为开发者联盟给应用分配的应用 APPID
public static final String HW_PUSH_APPID = "1234567890"; // 见清单文件
/****** 华为离线推送参数 end ******/

上报推送的证书 ID 及 token:

/**
 * 在 ThirdPushTokenMgr.java 中对推送的证书 ID 及设备信息进行上报操作
 */
public class ThirdPushTokenMgr {
    private static final String TAG = "ThirdPushTokenMgr";

    private String mThirdPushToken;

    private boolean mIsTokenSet = false;
    private boolean mIsLogin = false;

    public static ThirdPushTokenMgr getInstance () {
        return ThirdPushTokenHolder.instance;
    }

    private static class ThirdPushTokenHolder {
        private static final ThirdPushTokenMgr instance = new ThirdPushTokenMgr();
    }

    public void setIsLogin(boolean isLogin){
        mIsLogin = isLogin;
    }

    public String getThirdPushToken() {
        return mThirdPushToken;
    }

    public void setThirdPushToken(String mThirdPushToken) {
        this.mThirdPushToken = mThirdPushToken;  // token 在此处传值,结合上文自定义 BroadcastReciever 类文档说明
    }

    public void setPushTokenToTIM(){
        if(mIsTokenSet){
            QLog.i(TAG, "setPushTokenToTIM mIsTokenSet true, ignore");
            return;
        }
        String token = ThirdPushTokenMgr.getInstance().getThirdPushToken();
        if(TextUtils.isEmpty(token)){
            QLog.i(TAG, "setPushTokenToTIM third token is empty");
            mIsTokenSet = false;
            return;
        }
        if( !mIsLogin ){
            QLog.i(TAG, "setPushTokenToTIM not login, ignore");
            return;
        }
        TIMOfflinePushToken param = null;
        if(IMFunc.isBrandXiaoMi()){     // 判断厂商品牌,根据不同厂商选择不同的推送服务
            param = new TIMOfflinePushToken(Constants.XM_PUSH_BUZID, token);
        }else if(IMFunc.isBrandHuawei()){
            param = new TIMOfflinePushToken(Constants.HW_PUSH_BUZID, token);
        }else if(IMFunc.isBrandMeizu()){
            param = new TIMOfflinePushToken(Constants.MZ_PUSH_BUZID, token);
        }else if(IMFunc.isBrandOppo()){
            param = new TIMOfflinePushToken(Constants.OPPO_PUSH_BUZID, token);
        }else if(IMFunc.isBrandVivo()){
            param = new TIMOfflinePushToken(Constants.VIVO_PUSH_BUZID, token);
        }else{
            return;
        }
        TIMManager.getInstance().setOfflinePushToken(param, new TIMCallBack() {
            @Override
            public void onError(int code, String desc) {
                Log.d(TAG, "setOfflinePushToken err code = " + code);
            }

            @Override
            public void onSuccess() {
                Log.d(TAG, "setOfflinePushToken success");
                mIsTokenSet = true;
            }
        });
    }
}

步骤5:离线推送

成功上报证书 ID 及 token 后,即时通信 IM 服务端会在该设备上的即时通信 IM 用户 logout 之前、App 被 kill 之后,将消息通过华为推送通知到用户端。

说明:

  • 华为推送并非100%必达。
  • 华为推送可能会有一定延时,通常与 App 被 kill 的时机有关,部分情况下与华为推送服务有关。
  • 若即时通信 IM 用户已经 logout 或被即时通信 IM 服务端主动下线(例如在其他端登录被踢等情况),则该设备上不会再收到消息推送。

配置点击通知栏消息事件

您可以选择点击通知栏消息后的响应操作,支持打开应用跳转到自定义页面

打开应用

默认为点击通知栏消息打开应用。

跳转到自定义界面

点击通知栏消息后,可以配置为用内置浏览器打开指定网页,也可以配置为打开应用内指定界面。
如需配置跳转到自定义界面,您需要在 添加证书 时选择【跳转到自定义页面】并输入需要执行的操作或 URL:

  • 如需打开网页,则需选择【跳转到自定义界面】并输入以http://https://开头的网址,例如https://cloud.tencent.com/document/product/269

  • 如需打开应用内指定界面,需执行以下步骤。

    1. 在 manifest 中配置需要打开的 Activity 的intent-filter,示例代码如下:

      <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"
                 android:path="/detail"
                 android:scheme="pushscheme" />
         </intent-filter>
      
      </activity>
    2. 获取 intent URL,方式如下:

      Intent intent = new Intent(this, ChatActivity.class);
      intent.setData(Uri.parse("pushscheme://com.tencent.qcloud.tim/detail?title=testTitle"));
      intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
      String intentUri = intent.toUri(Intent.URI_INTENT_SCHEME);
      Log.i(TAG, "intentUri = " + intentUri);
      
      // 打印结果
      intent://com.tencent.qcloud.tim/detail?title=testTitle#Intent;scheme=pushscheme;launchFlags=0x4000000;component=com.tencent.qcloud.tim.tuikit/com.tencent.qcloud.tim.demo.chat.ChatActivity;end
    3. 选择【跳转到自定义页面】并输入上述打印结果。

常见问题

如果应用使用了混淆,如何防止华为离线推送功能异常?

如果您的应用使用了混淆,为了防止华为离线推送功能异常,您需要 keep 自定义的 BroadcastReceiver,参考添加以下混淆规则:

说明:

以下代码仅为示例,请根据实际情况修改后再使用。

-ignorewarning
-keepattributes *Annotation*
-keepattributes Exceptions
-keepattributes InnerClasses
-keepattributes Signature
-keepattributes SourceFile,LineNumberTable
-keep class com.hianalytics.android.**{*;}
-keep class com.huawei.updatesdk.**{*;}
-keep class com.huawei.hms.**{*;}
-keep class com.huawei.android.hms.agent.**{*;}  
# 请将 com.tencent.qcloud.uipojo.thirdpush.HUAWEIPushReceiver 改成您 App 中定义的完整类名
-keep com.tencent.qcloud.uipojo.thirdpush.HUAWEIPushReceiver {*;}

能否自定义配置推送提示音?

目前华为推送不支持自定义的提示音。

收不到推送时,如何排查问题?

  1. 任何推送都不是100%必达,厂商推送也不例外。因此,若在快速、连续的推送过程中偶现一两条推送未通知提醒,通常是由厂商推送频控的限制引起。
  2. 按照推送的流程,确认华为推送证书信息是否正确配置在 即时通信 IM 控制台 中。
  3. 确认您的项目 集成华为推送 SDK 的配置正确,并正常获取到了 token。
  4. 确认您已将正确的 推送信息上报 至即时通信 IM 服务端。
  5. 在设备中手动 kill App,发送若干条消息,确认是否能在一分钟内接收到通知。
  6. 若通过上述步骤后仍然接收不到推送,可以将您的问题时间点SDKAppID证书 ID接收推送的 UserID 提交工单 处理。