这是本文档旧的修订版!


第三方推送集成


当app在后台运行时环信IM SDK默认通过一个后台服务保持一条连接环信服务器的长连接,但Android为了解决系统待机性能差的问题,随着Android版本的升级逐渐禁止了app级别的后台服务的运行。所以在一些版本比较高的Android系统上会有接收不到消息的情况。为了提高消息的到达率,我们SDK增加了对第三方推送服务的支持,包括小米推送,华为推送,OPPO推送,魅族推送,VIVO推送,Google FCM推送。

目前小米、魅族、OPPO、VIVO推送的主要实现集成在了环信 IM SDK 中,尽量提供给开发者最简单的集成三方推送的形式。Google FCM 和华为推送的实现仍在 Demo 层,需要开发者自己集成,详情请参考环信的 Google FCM 和华为的推送集成文档。

各推送使用条件:
  • Google FCM:需要Google Play Service和能连接Google服务器的网络
  • 小米推送:在小米系统上可用
  • 华为推送:在华为系统上可用
  • 魅族推送:在魅族系统上可用
  • OPPO推送:在OPPO系统上可用
  • VIVO推送:在VIVO系统上可用
  • SDK内部会按照这个顺序去检测设备的推送支持情况
  • 如果未设置第三方推送或者不满足使用第三方推送的条件,环信IM SDK会通过一些保活手段尽可能的保持与环信服务器的长连接,以确保消息的及时送达

建议:如果你的App有海外使用场景,建议开启FCM推送;由于各推送使用条件不同,建议尽可能同时支持小米和华为推送。

消息推送流程:

消息推送流程图

  1. 判断设备支持哪种推送,(app配置了第三方推送并且满足该推送的使用条件)
  2. 根据集成第三方推送SDK获取推送token
  3. 上传证书名称(环信服务器用来判断使用哪种推送通道)和推送token到环信服务器
  4. 向某设备发送消息时,环信服务器会先判断目标设备是否在线,如果目标设备不在线,则判断目标设备使用了哪种推送通道(根据目标设备上传的证书名称),使用该推送通道通过第三方推送服务器将消息推送至目标设备
环信服务器应具备的能力:
  • 拥有向你的app发送推送消息的能力
    • 开发者通过环信后台配置App的推送证书,推送证书会要求填写证书名称(或者appKey),证书名称是环信服务器用来判断目标设备使用哪种推送通道的唯一条件,所以证书名称必须与Android终端设备上传的证书名称一致
  • 跟终端Android设备一一对应的推送token
    • 需Android设备上报
  • 推送token属于哪个推送通道
    • 需Android设备上报
Android设备需要做的事:
  • 判断当前设备支持哪种推送通道
  • 通过集成第三方推送SDK获取推送token
  • 上传证书名称(注意跟通过环信后台配置的证书名称保持一致)和推送token至环信服务器(该步骤必须在环信SDK登录成功后)

开发者配置推送的接口:

EMPushConfig.Builder builder = new EMPushConfig.Builder(context);
        builder.enableVivoPush() // 推送证书相关信息配置在AndroidManifest.xml中
                .enableMeiZuPush(String appId, String appKey)
                .enableMiPush(String appId, String appKey)
                .enableOppoPush(String appKey, String appSecret)
                .enableHWPush() //开发者需要调用该方法来开启华为推送
                .enableFCM(String senderId); //开发者需要调用该方法来开启FCM推送

EMOptions options = new EMOptions();
options.setPushConfig(builder.build());

开发者需要把上述配置中的相关信息换成开发者自己申请的各平台的配置。建议开发者尽可能多的配置三方推送,以确保离线消息的到达率。

开发者可以调用以下代码监听推送相关事件(仅支持小米、魅族、OPPO、VIVO):

EMPushHelper.getInstance().setPushListener(new PushListener() {
	@Override
	public void onError(EMPushType pushType, long errorCode) {
		EMLog.e("PushClient", "Push client occur a error: " + pushType + " - " + errorCode);
// TODO: 开发者会在这个回调中收到使用推送的相关错误信息,各推送类型的error code开发者可以自己去各推送平台官网查询错误原因。
	}

	@Override
	public boolean isSupportPush(EMPushType pushType, EMPushConfig pushConfig) {
		return super.isSupportPush(pushType, pushConfig);
// TODO:开发者可以复写该方法控制设备是否支持某推送的判断。
	}
});

SDK 3.4.2 版本开始默认优先使用FCM推送,在一些带Google Play Service的华为设备上,开发者可通过设置EMOptions#setUseFCM(false)关闭FCM推送的使用,来达到使用华为推送的目的。

说明

  • FCM 使用主要针对海外用户;
  • FCM 要求设备安装有 Google Play 服务、 Google Play 商店 和 能连接Google服务器的网络。

服务端

1.登录Firebase管理后台

2.在Firebase欢迎界面点击 Add Project,输入相应内容并点击 Create Project

3.在Firebase欢迎界面选择 Add Firebase to your Android App

4.选择应用类型后需要输入包名、项目昵称、SHA-1,然后点击 Register App

5.进入引导页,如下图,点击按钮下载google-services.json文件到本地。注意该json文件在Android项目中的放置位置。

6.跳过引导页,点击Cloud Messaging tab页,复制Server Key和Sender ID。

7.登录环信管理后台,选择你的应用—选择推送证书—新增证书,证书的名称要求填上方复制Sender ID,证书秘钥填写上方复制的Server Key。

移动端

1.添加Google Play Service相关依赖库

用于检查设备是否支持Google FCM推送,该步骤在FCM官方集成文档上不存在,由于国内特殊的使用环境,所以我们增加了该配置用于辅助检测

compile 'com.google.android.gms:play-services-base:11.4.0'该行配置添加到项目相应的build.gradle文件中,SDK demo中的配置在easeui/build.gradle中,如下:

dependencies {
    // 添加此行
    compile 'com.google.android.gms:play-services-base:11.4.0'
}

注意:Google推送相关依赖库版本必须对应(该文档中均为:11.4.0),否则可能会出现类冲突的错误。

2.在project-level的build.gradle中添加FCM相关库文件配置:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        // 添加此行
        classpath 'com.google.gms:google-services:3.1.1'
    }
}

allprojects {
    repositories {
        // 添加此行
        maven { url 'https://maven.google.com' }
    }
}

3.在app-level的build.gradle中添加FCM相关库文件配置:Push

dependencies {
// 添加此行,Google Firebase cloud messaging
    compile 'com.google.firebase:firebase-messaging:11.4.0'
}

// 此行添加在文件末尾
apply plugin: 'com.google.gms.google-services'

注意:Google推送相关依赖库版本必须对应(该文档中均为:11.4.0),否则可能会出现类冲突的错误。

4.放置下载的google-services.json在app-level的根目录下

5.实现一个继承自FirebaseInstanceIdService的自定义service,该类用于监听FCM token的创建和更新。一个设备对应一个FCM token,该token用于服务端向该设备推送消息,所以该token创建或更新后需及时上传至环信服务器。

自定义FirebaseInstanceIdService:

public class EMFCMTokenRefreshService extends FirebaseInstanceIdService {
    private static final String TAG = "FCMTokenRefreshService";

    @Override
    public void onTokenRefresh() {
        super.onTokenRefresh();
        String token = FirebaseInstanceId.getInstance().getToken();
        Log.i(TAG, "onTokenRefresh: " + token);
        // Important, send the fcm token to the server
        EMClient.getInstance().sendFCMTokenToServer(token);
    }
}

AndroidManifest.xml:

<service android:name=".fcm.EMFCMTokenRefreshService">
    <intent-filter>
        <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
    </intent-filter>
</service>

6.实现一个继承自FirebaseMessagingService的自定义service,该类用于FCM在后台进行接收应用推送消息的处理。并把该service注册到AndroidManifest.xml中。

自定义FirebaseMessagingService:

public class EMFCMMSGService extends FirebaseMessagingService {
    private static final String TAG = "EMFCMMSGService";

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        super.onMessageReceived(remoteMessage);
        if (remoteMessage.getData().size() > 0) {
            String message = remoteMessage.getData().get("alert");
            Log.i(TAG, "onMessageReceived: " + message);
        }
    }
}

AndroidManifest.xml:

<service android:name=".fcm.EMFCMMSGService">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>

7.通过EMOptions#setUseFCM(true)接口设置允许使用FCM推送,SDK会进行FCM推送条件的检查,并在FCM推送条件满足的情况下使用FCM推送。接口使用可参考Demo中的DemoHelper。

8.启用FCM推送

EMPushConfig.Builder builder = new EMPushConfig.Builder(context);
builder.enableFCM(String senderId);
options.setPushConfig(builder.build());

推送服务测试

为了确保推送服务的成功集成,可按如下步骤进行测试,

  1. 运行app并进行登录
  2. 杀掉该app进程(通过Android任务列表滑动结束进程,不是按Home键让app进入后台;通过 设置→应用→强行停止 结束app进程,app无法接收到FCM推送,详情请查阅Google官方对该操作的解释。)
  3. 向该登录账号发送消息,app会收到通过推送服务送达的消息

创建华为应用

首先就是去华为开发者后台创建应用,并开启 push 服务,并上传对应的证书指纹,具体可以看下华为官方介绍: 华为HMS消息推送服务集成

上传推送证书

注册完整后,需要在环信开发者后台上传推送证书,选择你的应用—>推送证书—>Huawei—>新增证书,然后输入你在华为开发者后台创建的应用的APPIDAPP SECRET以及程序的包名

启用华为推送

EMPushConfig.Builder builder = new EMPushConfig.Builder(context);
builder.enableHWPush();
options.setPushConfig(builder.build());

SDK3.4.x 华为推送重大更新

为了方便用户自己升级华为推送相关sdk,环信 SDK 在3.4.x之后的版本中将华为推送的集成从SDK转移到应用层,SDK提供上传华为推送token的接口供用户调用,方便华为推送升级时用户自行升级,以后的版本就需要开发者自己去集成华为推送相关功能,然后调用下边的方法将 token 发送到环信服务器:

// 上传 token 方法,token 是通过广播接收器接收到的
EMClient.getInstance().sendHMSPushTokenToServer("华为appId", "注册华为的 token");

PS:需要注意,此方法必须是登录成功后才能调用,所以请求华为 token 需要放在登录成功之后,所以我们请求华为推送 token 一般放在 MainActivity 类中,环信 IM 的 Demo也已经集成了华为最新推送 SDK,开发者也可以参考 demo 进行集成,token 的获去就是在广播接收器中,Demo 中有实现HMSPushReceiver类,可以看下 demo 的代码

这是华为官方集成文档,开发者可以自己根据华为官方文档进行集成华为推送 华为消息推送服务集成官方文档

Demo 中将华为的 HMSAgent 做成了一个 module 进行引用(这里没有对华为 HMSAgent 进行任何封装和修改),开发者可以直接进行使用,也可以直接下载华为官方最新的HMSAgent自己进行集成,如果使用 demo 中的 module 需要修改以下几个地方:

<application>
    <!-- 接入HMSSDK 需要注册的appid参数。value的值中“10492024”用实际申请的appid替换,来源于开发者联盟网站应用的权益详情。格式 android:value="appid=xxxxxx"-->
    <meta-data
        android:name="com.huawei.hms.client.appid"
        android:value="appid=10492024" />
    <!-- 接入HMSSDK 需要注册的provider,authorities 一定不能与其他应用一样,所以这边 com.hyphenate.chatuidemo 要替换上您应用的包名-->
    <provider
        android:name="com.huawei.hms.update.provider.UpdateProvider"
        android:authorities="com.hyphenate.chatuidemo.hms.update.provider"
        android:exported="false"
        android:grantUriPermissions="true" />
    <!-- 接入HMSSDK 需要注册的provider,authorities 一定不能与其他应用一样,所以这边 com.hyphenate.chatuidemo 要替换上您应用的包名-->
    <provider
        android:name="com.huawei.updatesdk.fileprovider.UpdateSdkFileProvider"
        android:authorities="com.hyphenate.chatuidemo.updateSdk.fileProvider"
        android:exported="false"
        android:grantUriPermissions="true"/>
        ...
</application>

Demo在集成华为推送时将调用华为推送的几个方法都放在了HMSPushHelper类中,开发者可以进行参考使用

配置完这些之后,在满足条件的华为设备上就可以使用华为推送接收离线推送通知了; 这里的满足条件是指:华为设备必须安装2.6.+以上的华为移动服务,以及开启当前 app 的自启动权限

故障排查

当开发者做完这些之后如果在华为设备上还是收不到推送,可以看下控制台的输出,或者环信 sdcard 上保存的日志,是否有一下日志输出:

// 当设备的华为移动服务版本比较低的时候,无法启用华为推送,会有以下输出
huawei mobile services is not available. please upgrade
// 当注册 token 时,华为开发者后台证书不对应,或者没有开通 Push 服务,当所有的都确认没问题后,如果还是有这样的问题,这个需要联系华为技术支持查看下是否生效
hms service connection suspended. error: 6xxx 或 9xxxxxxxx

后边的 error 为华为官方错误码,更多错误码请参考华为官方错误表:表3-1 HMS 通用错误码及处理方式

魅族推送包含该两种推送类型:Flyme 推送和集成推送。二者的区别是:Flyme 推送是魅族自己的推送;集成推送除了有魅族自己的 Flyme 推送外还可以通过配置集成小米、华为等第三方推送。环信 SDK 内部使用的是 Flyme 推送参考文档

创建魅族应用

首先就是去魅族开发者后台创建应用,并开启 push 服务,并上传对应的证书指纹,具体可以看下魅族官方介绍: flyme 推送服务集成

上传推送证书

注册完整后,需要在环信开发者后台上传推送证书,选择你的应用—>推送证书—>魅族—>新增证书,然后输入你在 flyme 推送平台创建的应用的APP IDAPP SECRET以及程序的包名

接入流程

1.在 app level/build.gradle 中添加 dependency :

dependencies{
// 该aar托管在jcenter中,请确保当前项目已配置jcenter仓库。
implementation 'com.meizu.flyme.internet:push-internal:3.7.0@aar'
}

2.在 AndroidManifest.xml 的 manifest 标签下添加:

<!-- 魅族推送配置 start-->
    <!-- 兼容 flyme5.0 以下版本,魅族内部集成 pushSDK 必填,不然无法收到消息-->
    <uses-permission android:name="com.meizu.flyme.push.permission.RECEIVE" />
    <permission
        android:name="${applicationId}.push.permission.MESSAGE"
        android:protectionLevel="signature" />
    <uses-permission android:name="${applicationId}.push.permission.MESSAGE" />
    <!-- 兼容 flyme3.0 配置权限-->
    <uses-permission android:name="com.meizu.c2dm.permission.RECEIVE" />
    <permission
        android:name="${applicationId}.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />
    <uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" />
<!-- 魅族推送配置 end-->

3.在 AndroidManifest.xml 的 application 标签下添加:

<!-- MEIZU推送配置 start -->
        <receiver android:name="com.hyphenate.push.platform.meizu.EMMzMsgReceiver">
            <intent-filter>
                <!-- 接收 push 消息 -->
                <action android:name="com.meizu.flyme.push.intent.MESSAGE"
                    />
                <!-- 接收 register 消息 -->
                <action
                    android:name="com.meizu.flyme.push.intent.REGISTER.FEEDBACK" />
                <!-- 接收 unregister 消息-->
                <action
                    android:name="com.meizu.flyme.push.intent.UNREGISTER.FEEDBACK"/>
                <!-- 兼容低版本 Flyme3 推送服务配置 -->
                <action android:name="com.meizu.c2dm.intent.REGISTRATION"
                    />
                <action android:name="com.meizu.c2dm.intent.RECEIVE" />
                <category android:name="${applicationId}"></category>
            <	/intent-filter>
        </receiver>
        <!-- MEIZU推送配置 end -->

4.启用魅族推送:

EMPushConfig.Builder builder = new EMPushConfig.Builder(context);
builder.enableMeiZuPush(String appId,String appKey);
options.setPushConfig(builder.build());

注意把上方的 APP ID 和 APP KEY 替换成开发者自己申请的内容。

注意:如果开发者自己集成了魅族Flame推送且实现了 MzPushMessageReceiver ,请把该父类替换为环信 SDK 提供的 EMMzMsgReceiver ,开发者自行判断业务逻辑,非开发者自有业务逻辑请通过 super 方法交给环信 SDK 处理。

创建 OPPO 应用

首先就是去 OPPO 开发者后台创建应用,并开启 push 服务,并上传对应的证书指纹,具体可以看下 OPPO 官方介绍: OPPO 推送服务集成

上传推送证书

注册完整后,需要在环信开发者后台上传推送证书,选择你的应用—>推送证书—>OPPO—>新增证书,然后输入你在 OPPO 开发者后台创建的应用的APP KEYMASTER SECRET以及程序的包名

接入流程

由于申请 OPPO 推送的时候提交的环信 Android IM Demo 的包名被占用,所以更换为 com.hyphenate.chatuidemo.push ,使用环信 Android IM Demo 测试 OPPO 推送时,请更换 app level/build.gradle 中的 applicationId 。如果还配置了 Google FCM 推送,请同时替换google-services.json 中的 package_name 字段。 OPPO 设备安装应用后默认没有打开允许通知权限,测试前请先去设置中打开该应用的允许通知权限。 OPPO推送官方文档

1.配置 OPPO 推送 jar 包: 去 OPPO 推送官网下载推送 SDK 包,把 jar 包放到 libs 目录下并 sync 。也可以直接使用环信 Android IM Demo 中集成的 OPPO 推送的jar 包。

2.在 AndroidManifest.xml 的 manifest 标签下添加:

<!-- OPPO推送配置 start -->
<uses-permission android:name="com.coloros.mcs.permission.RECIEVE_MCS_MESSAGE"/>
<!-- OPPO推送配置 end -->

3.在 AndroidManifest.xml 的 application 标签下添加:

<!-- OPPO 推送配置 start -->
<service
android:name="com.coloros.mcssdk.PushService"       	android:permission="com.coloros.mcs.permission.SEND_MCS_MESSAGE">
        <intent-filter>
        	<action android:name="com.coloros.mcs.action.RECEIVE_MCS_MESSAGE" />
        </intent-filter>
</service>
<!-- OPPO 推送配置 end -->

4.启用 OPPO 推送:

EMPushConfig.Builder builder = new EMPushConfig.Builder(context);
builder.enableOppoPush(String appKey,String appSecret);
options.setPushConfig(builder.build()); 

注意把上方的 APP KEY 和 MASTER SECRET 替换成开发者自己申请的内容。

创建 VIVO 应用

首先就是去 VIVO 开发者后台创建应用,并开启 push 服务,并上传对应的证书指纹,具体可以看下 VIVO 官方介绍: VIVO 推送服务集成

上传推送证书

注册完整后,需要在环信开发者后台上传推送证书,选择你的应用—>推送证书—>VIVO—>新增证书,然后输入你在 VIVO 开发者后台创建的应用的APP IDAPP KEYAPP SECRET以及程序的包名

接入流程

VIVO设备安装应用后默认没有打开允许通知权限,测试前请先去设置中打开该应用的允许通知权限。 VIVO 推送官方文档

1.配置 VIVO 推送 jar 包: 去 VIVO 推送官网下载推送 SDK 包,把 jar 包放到 libs 目录下并 sync 。也可以直接使用环信 Android IM Demo 中集成的 VIVO 推送的 jar 包。

2.在 AndroidManifest.xml 的 application 标签下添加:

<!-- VIVO 推送配置 start -->
        <service
            android:name="com.vivo.push.sdk.service.CommandClientService"
            android:exported="true" />
        <activity
            android:name="com.vivo.push.sdk.LinkProxyClientActivity"
            android:exported="false"
            android:screenOrientation="portrait"
            android:theme="@android:style/Theme.Translucent.NoTitleBar" />
        <!--推送配置项-->
        <meta-data
            android:name="com.vivo.push.api_key"
            android:value="开发者自己申请的appKey" />
        <meta-data
            android:name="com.vivo.push.app_id"
            android:value="开发者自己申请的appId" />

        <receiver android:name="com.hyphenate.push.platform.vivo.EMVivoMsgReceiver" >
            <intent-filter>
                <!-- 接收 push 消息 -->
                <action android:name="com.vivo.pushclient.action.RECEIVE" />
            </intent-filter>
        </receiver>
        <!-- VIVO 推送配置 end -->

3.启用 VIVO 推送:

EMPushConfig.Builder builder = new EMPushConfig.Builder(context);
builder.enableVivoPush();
options.setPushConfig(builder.build());

注意:如果开发者自己集成了 VIVO 推送且实现了 OpenClientPushMessageReceiver ,请把该父类替换为环信 SDK 提供的 EMVivoMsgReceiver ,开发者自行判断业务逻辑,非开发者自有业务逻辑请通过 super 方法交给环信 SDK 处理。

使用第三方推送时需要在退出登录时解绑设备 token,调用EMClient#getInstance()#logout(true)或者EMClient#getInstance()#logout(true,callback)方法,如果是被踢的情况下,则要求设置为 false。


用户可以在消息扩展中增加特定的字段来实现消息的推送配置。

发送静默消息(不推送)

(Android 发消息)

EMMessage message = EMMessage.createSendMessage(EMMessage.Type.TXT);
EMTextMessageBody txtBody = new EMTextMessageBody("test");
message.setTo("6006");
// 设置自定义扩展字段
message.setAttribute("em_ignore_notification", true);
// 设置消息回调
message.setMessageStatusCallback(new EMCallBack() {...});
// 发送消息
EMClient.getInstance().chatManager().sendMessage(message);

强制推送

(Android 发消息)

EMMessage message = EMMessage.createSendMessage(EMMessage.Type.TXT);
EMTextMessageBody txtBody = new EMTextMessageBody("test");
message.setTo("6006");
// 设置自定义扩展字段
message.setAttribute("em_force_notification", true);
// 设置消息回调
message.setMessageStatusCallback(new EMCallBack() {...});
// 发送消息
EMClient.getInstance().chatManager().sendMessage(message);

自定义推送提示

(Android 发消息)

// 这里只是一 TXT 消息为例,IMAGE FILE 等类型的消息设置方法相同
EMMessage message = EMMessage.createSendMessage(EMMessage.Type.TXT);
EMTextMessageBody txtBody = new EMTextMessageBody("消息内容");
message.setTo("6006");
// 设置自定义推送提示
JSONObject extObject = new JSONObject();
try {
    extObject.put("em_push_name", "离线推送标题");
    extObject.put("em_push_content", "离线推送内容部分");
} catch (JSONException e) {
    e.printStackTrace();
}
// 将推送扩展设置到消息中
message.setAttribute("em_apns_ext", extObject);
// 设置消息回调
message.setMessageStatusCallback(new EMCallBack() {...});
// 发送消息
EMClient.getInstance().chatManager().sendMessage(message);

您可以登录环信管理后台,在应用列表中选中相应的APP,再点击“IM用户”,在用户列表中搜索收不到推送的用户,找到相应的用户后,点击右侧的“操作”下拉菜单,再点击“测试离线推送”,系统会推送一条测试消息给这个用户,如果出现了问题会做出对应的错误原因提示来协助您排查问题,可能的错误提示有:

  • 用户不存在
  • 用户没有绑定Device Token
  • 用户没有绑定证书
  • 证书不存在或被禁