Android 推送服务开发指南

    概述

    融云推送服务指的是从服务端远程发送一条包含消息内容的推送,当客户端 App 接收到之后,可以在通知栏弹出提醒。

    由于 Android GCM(Google Cloud Message)在国内不能使用,所以 Android App 需要实现自己的推送服务,融云作为即时通信能力的提供者,也提供了实时推送的能力。您集成 IMKit 和 IMLib 都会默认具备推送服务的能力。

    什么情况下会收到推送

    融云 SDK 根据 Android App 运行的特性,主要有以下三种运行状态:

    1、前台状态:如字面意思,App 前台可见时 SDK 处于前台状态。此时 App 使用融云的长连接通道来收发消息。

    这种情况下,因为已经收到了实时的消息,您的 App 不会收到推送。

    2、后台活动状态:当 App 进入后台 3 分钟之内,SDK 处于后台活跃状态。此时 App 使用融云的长连接通道接收消息。

    如果您使用 IMKit,此时 SDK 收到消息会弹出本地通知(必须实现用户信息提供者和群组信息提供者,否则将不会有本地通知提示弹出)。

    如果您使用 IMLib,由于 IMLib 是不带界面功能的 SDK,因此需要您自己根据消息回调来在本地弹出通知提示。

    在这种情况下,您收到的还是实时消息,并不是推送。

    3、后台暂停状态:当 App 的主进程被杀死或者回收后,或者您主动调用 disconnect() 跟融云断开连接后。此时,融云会通过一个后台服务保持一条推送长连接,跟融云服务器保持连接,我们称之为 Push 连接通道。

    在这种情况下,你会收到实时的推送。推送的内容是包含消息内容的数据。

    小米、华为、GCM 推送说明

    由于 GCM 在国内不能正常使用,所以 App 都必须使用单独的推送服务。但随着 Android 版本的升级,为了解决 Android 系统待机性能差的问题,Android 系统对于后台运行的推送服务的封禁力度越来越大,所以实际上所有第三方推送和 App 自己开发的推送到达率都极低。

    为了提高推送到达率,融云建议您集成小米推送。在小米、华为系统上,可极大提高推送消息的到达率。在非小米、华为系统上,仍将继续使用融云自有的推送服务。融云通过官方服务保证了消息的实时到达。在集成小米、华为推送之后,推送整体到达率可远远超过各类第三方推送。

    在华为系统上,如果集成了华为新的 HMS,需要手机上安装了华为移动服务(华为帐号),这样才能收到华为移动服务通道的 Push 消息。

    如果您的 App 在海外运营或者有较大比率海外用户,融云建议您开通 GCM 推送。融云会根据 IP 判断是否当前客户端在海外,且当前手机支持谷歌服务框架(GoogleServicesFramework),这种情况下,融云会把实时推送通过谷歌服务推给客户。

    关于自启动。目前 Android 手机支持这样一个设置,如果是打开了自启动权限的 App,可以被后台保活服务唤醒。而关闭了自启动权限的 App,即使推送服务收到了推送,仍然不能唤醒 App。微信、QQ 等 App 之所以可以有持续稳定的推送通知,是因为在各类手机上都给他们打开了白名单。如果 App 打开了自启动,融云具备跟微信、QQ 同等的推送能力。

    消息推送的流程

    当 SDK 处于后台暂停状态时,消息推送流程如下:

    App / App Server -> RongCloud Server: 应用或应用服务端向目标用户发送消息 RongCloud Server -> RongCloud Server: 判断用户在线状态 RongCloud Server -> RongCloud Push Server: 向未在线目标用户,发消息到 RongCloud Push Server RongCloud Push Server -> RongCloud Push Server: 判断是否设置了小米推送 RongCloud Push Server -> 小米 Push Server: 已设置,则发送到小米推送服务 小米 Push Server -> App: 向目标用户,发送远程通知至客户端 RongCloud Push Server -> App: 未设置,则直接向目标用户,发送远程通知至客户端

    如何使用推送

    集成 SDK

    参考集成融云 SDK 文档

    注:使用融云推送功能前必须在“融云开发者后台 - 应用标识”中设置了 Android 应用包名,否无法接收推送消息。

    自定义一个 BroadcastReceiver 类

    为了接收推送消息,您需要自定义一个继承自 PushMessageReceiver 类的 BroadcastReceiver (必须实现,否则会收不到推送消息),实现其中的 onNotificationMessageArrivedonNotificationMessageClicked 然后把该 receiver 注册到 AndroidManifest.xml 文件中。

    自定义的 BroadcastReceiver

    public class DemoNotificationReceiver extends PushMessageReceiver {
     @Override
     public boolean onNotificationMessageArrived(Context context, PushNotificationMessage message) {
        return false;
     }
    
     @Override
     public boolean onNotificationMessageClicked(Context context, PushNotificationMessage message) {
        return false;
     }
    }
    

    注册到应用的 AndroidManifest.xml 里面:

    <receiver
        android:exported="true"
        android:name="您自定义的 broadcastReceiver 类名">
        <intent-filter>
            <action android:name="io.rong.push.intent.MESSAGE_ARRIVED" />
            <action android:name="io.rong.push.intent.MI_MESSAGE_ARRIVED" />
            <action android:name="io.rong.push.intent.MESSAGE_CLICKED" />
            <action android:name="io.rong.push.intent.MI_MESSAGE_CLICKED" />
        </intent-filter>
    </receiver>
    

    onNotificationMessageArrived 用来接收服务器发来的通知栏消息(消息到达客户端时触发),默认return false,通知消息会以融云 SDK 的默认形式展现。如果需要自定义通知栏的展示,在这里实现自己的通知栏展现代码,同时 return true 即可。

    onNotificationMessageClicked 是在用户点击通知栏消息时触发 (注意:如果自定义了通知栏的展现,则不会触发),默认 return false 。如果需要自定义点击通知时的跳转,return true 即可。融云 SDK 默认跳转规则如下

    只有一个联系人发来一条或者多条消息时,会通过 intent 隐式启动会话 activityintent 的 uri 如下:

    Intent intent = new Intent();
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    Uri.Builder builder = Uri.parse("rong://" + this.getPackageName()).buildUpon();
    
    builder.appendPath("conversation").appendPath(type.getName())
            .appendQueryParameter("targetId", targetId)
            .appendQueryParameter("title", targetName);
    uri = builder.build();
    intent.setData(uri);
    startActivity(intent);
    

    如果你的 AndroidManifest.xml 里面配置 A activity 拦截了这个 intent ,那在点击这条通知栏消息 时就会启动 activity A

    <activity
    android:name="A"
    android:launchMode="singleTop"
    android:screenOrientation="portrait"
    android:windowSoftInputMode="stateHidden|adjustResize">
      <intent-filter>
          <action android:name="android.intent.action.VIEW" />
          <category android:name="android.intent.category.DEFAULT" />
    
          <data
              android:host="你的包名"
              android:pathPrefix="/conversation/"
              android:scheme="rong" />
      </intent-filter>
    </activity>
    

    多个联系人发来多条消息时,通过 intent 隐式启动会话列表 activityintenturi 配置如下:

    Intent intent = new Intent();
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    Uri.Builder builder = Uri.parse("rong://" + this.getPackageName()).buildUpon();
    builder.appendPath("conversationlist");
    Uri uri = builder.build();
    intent.setData(uri);
    startActivity(intent);
    

    如果你的 AndroidManifest.xml 里面配置 B activity 拦截了这个 intent,那在点击这条通知栏消息 时就会启动 activity B

    <activity
       android:name="B"
       android:launchMode="singleTask"
       android:screenOrientation="portrait"
       android:windowSoftInputMode="stateHidden|adjustResize">
    
       <intent-filter>
           <action android:name="android.intent.action.VIEW" />
           <category android:name="android.intent.category.DEFAULT" />
           <data
               android:host="你的包名"
               android:path="/conversationlist"
               android:scheme="rong" />
       </intent-filter>
    </activity>
    
    可以在融云开发者后台广播推送-广播消息-推送中,发起远程推送。

    点击推送消息时会触发出如下 action 事件:

    Intent intent = new Intent();
    intent.setFlags(intent.FLAG_ACTIVITY_NEW_TASK);
    
    Uri.Builder uriBuilder = Uri.parse("rong://" + this.getPackageName()).buildUpon();
    uriBuilder.appendPath("push_message")
            .appendQueryParameter("targetId", targetId)
            .appendQueryParameter("pushData", pushData)
            .appendQueryParameter("pushId", pushId)
            .appendQueryParameter("extra", extra);
    
    startActivity(intent);
    

    如果你的 AndroidManifest.xml 里面配置了 C activity 拦截这个 action, 那么点击时就会跳转到 activity C

    <activity
    android:name="C"
    android:launchMode="singleTask"
    android:screenOrientation="portrait">
    
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
    
        <data
            android:host="你的包名"
            android:pathPrefix="/push_message"
            android:scheme="rong" />
    </intent-filter>
    </activity>
    

    通过以上步骤,您已完成了融云推送服务的集成。

    注意:部分 Android 手机系统在黑屏待机后自动清理后台运行的软件,这样影响了应用正常接收新的消息,需要将应用设置为后台运行应用。查看各类机型的设置说明

    升级须知

    如果您是从 2.6.0 之前的版本升级来的,请注意从这个版本开始关于 Push 消息的监听机制变了,去掉了原有的 setOnReceivePushMessageListener(),请把您应用里关于 Push 消息监听的代码直接删除即可。

    混淆脚本说明

    融云 SDK 支持小米和 GCM 推送,SDK 内部帮用户做了部分集成, 所以在您没有集成这几个第三方 jar 包时, 会有一些告警,混淆时加入下面语句即可:

    -dontwarn io.rong.push.**
    
-dontnote com.xiaomi.**
    -dontnote com.google.android.gms.gcm.**
    
-dontnote io.rong.**
    

    另外,你需要 keep 自定义的 BroadcastReceiver。自定义的 BroadcastReceiver 继承PushMessageReceiver,使用下面的代码是不行的。

    -keep public class * extends android.content.BroadcastReceiver
    

    你需要使用下面的代码 keep 自定义的 BroadcastReceiver。

    这里 io.rong.app.DemoNotificationReceiver 改成你的应用自定义的完整类名

    -keep class io.rong.app.DemoNotificationReceiver {*;}
    

    配置文件说明

    2.6.0 以上版本的 AndroidManifest.xml 配置文件中,push 相关核心配置如下:

    <!--必选: SDK 核心功能-->
    <!--第三方相关,向第三方推送服务请求 token 的服务 -->
    <service
        android:name="io.rong.push.core.PushRegistrationService"
        android:exported="false">
    </service>
    
    
    <!-- 处理 push 消息相关的服务 -->
    <service
        android:name="io.rong.push.core.MessageHandleService"
        android:exported="true">
    </service>
    
    
    <!-- push服务 -->
    <service
        android:name="io.rong.push.PushService"
        android:exported="false"
        android:process="io.rong.push">  <!-- push进程,可以改名 -->
    </service>
    
    
    <!-- push 相关事件接收器 -->
    <receiver
        android:name="io.rong.push.PushReceiver"
        android:process="io.rong.push">  
         <!-- 此处进程可以改名,名称需要和PushService所在进程统一 -->
    
        <!-- 心跳事件 -->
        <intent-filter>
            <action android:name="io.rong.push.intent.action.HEART_BEAT" />
        </intent-filter>
        <!-- 网络变动事件 -->
        <intent-filter>
            <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
        </intent-filter>
        <!-- 部分用户事件 -->
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.USER_PRESENT" />
            <action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
            <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
        </intent-filter>
    </receiver>
    
    <!--必选: SDK 核心功能-->
    

    注:如果您是从 2.6.0 以前版本升级,请注意将旧版本中原有的 push 配置,如 CommandService 等全部删除!

    API 说明

    RongPushCient API 说明

    /**
      * 注册 GCM 推送。必须在 init()之前调用。
      *
      * @param context 上下文。
      */
     public static void registerGCM(Context context) throws RongException {}
    
     /**
      * 注册小米推送。必须在 init() 之前调用。
      *
      * @param context  上下文。
      * @param miAppId  您在小米开发者站注册的应用的 AppId.
      * @param miAppKey 您在小米开发者站注册的应用的 AppKey.
      */
     public static void registerMiPush(Context context, String miAppId, String miAppKey) {}
    
     /**
      * 清除通知栏所有通知消息。
      *
      * @param context 上下文
      */
     public static void clearAllNotifications(Context context) {}
    
     /**
      * <p>清除所有的推送通知和后台消息通知。ex: A 账号离线或者退到后台,B 账号给 A 发送消息,A 设备会收到该消息的通知,调用此函数会清除该类型的通知。
      * 但是,如果是从开发者后台使用广播推送服务发送的推送通知,仍然会保留,不会清除   </p>。
      *
      * @param context 上下文。
      */
     public static void clearAllPushNotifications(Context context) {}
    
     /**
      * 清除所有后台推送服务的推送通知。后台推送服务,是指开发者后台的广播推送服务。
      *
      * @param context 上下文。
      */
     public static void clearAllPushServiceNotifications(Context context) {}
    
     /**
      * 根据 notification id 清除通知栏。
      *
      * @param context        上下文。
      * @param notificationId 通知栏消息 id。
      */
     public static void clearNotificationById(Context context, int notificationId) {}
    
     /**
      * <p>记录在开发者后台使用后台推送功能时,对应的推送通知的点击事件。开发者后台的推送打开率既根据客户端上传的该事件进行相应统计和计算。
      * 2.6.0之前版本,推送打开率的使用请在知识库里搜索标签 push,有相关说明。
      * 2.6.0之后版本,如果用户使用的 SDK 内置的通知实现,则不需要调用该方法来统计推送打开率,SDK 内部已经帮用户做了统计。
      * 但是如果用户自己定义了推送时通知栏的显示,则需要在点击通知时,调用此方法,来向服务器上传推送打开事件。</p>
      *
      * @param pushId push 通知的 Id。只有使用开发者后台广播消息和开发者后台推送服务时,pushId 才会有值,其余非后台情况下都为空。
      */
     public static void recordNotificationEvent(String pushId){}
    
     /**
      * 停止融云推送服务。
      *
      * @param context 上下文。
      */
     public static void stopRongPush(Context context) {}
    
     /**
      * 发送通知。如果使用 IMLib 开发,当应用在后台需要弹后台通知时,可以直接调用此函数弹出通知。
      *
      * @param context             上下文
      * @param notificationMessage 融云对外公开的通知消息。
      */
     public static void sendNotification(Context context, PushNotificationMessage notificationMessage) {}
    
     /**
      * 帮助方法,用来检查 AndroidManifest 里融云 Push 服务相关的配置是否正确。
      * 在 init() 之后调用即可。 注意: 这只是个帮助方法,检查完毕需要把它删除掉。
      * @param context 上下文。
      */
     public static void checkManifest(Context context) throws RongException {}
    

    PushNotificationMessage 成员列表

    API 功能 备注
    getPushId() 获取标识该推送消息的唯一 Id。 当使用开发者后台的广播推送服务时,该 Id 用来追踪推送打开率。只有当使用开发者后台的广播推送服务时,返回有效字符串。 其余情况,返回 null。 如果您自定义了推送通知的展示,在点击通知时需要调用 RongPushClient.recordNotificationEvent() 来上传推送打开事件,以便融云后台正确的帮您统计推送打开率。如果您没有自定义通知,则不需要额外操作,SDK 内部已经帮您做了打开率的统计。
    getConversationType() 获取该推送消息所属的会话类型。比如单聊,群组,讨论组等。
    getTargetId() 获取该推送消息的目标 Id。 比如单聊时,是对方的 Id ; 群组时,是群 Id ; 讨论组时,是该讨论组 Id。
    getTargetUserName() 获取该推送消息的目标名称。 单聊时,返回对方的名称; 群组时,返回群名称; 讨论组时,返回该讨论组的名称。
    getReceivedTime() 获取该推送消息的到达时间。 该时间是此条消息到达融云服务器的时间。
    getObjectName() 获取该推送消息的消息类型。 比如文本消息为 “RC:TxtMsg”,图片消息为“RC:ImgMsg”,语音消息为 “RC:VcMsg”,或者如果是自定义消息,则此处对应自定义消息的注解 MessageTag 里 value 的值。
    getSenderId() 获取该推送消息的发送者 Id。
    getSenderName() 获取该推送消息的发送者名称。
    getSenderPortrait() 获取该推送消息的发送者头像。
    getPushTitle() 推送标题,目前此字段暂未启用。
    getPushContent() 获取推送通知的显示内容。 当该推送消息是自定义消息时,此处的值对应 sendMessage() 的参数 pushContent 的内容。如果您在发送自定义消息时,没有填写 pushContent,则收不到推送。
    getPushData() 获取推送消息携带的附加信息。此处的值对应 sendMessage() 的参数 pushData 的内容。 发送自定义消息时,如果在 sendMessage() 时填写参数 pushData, 则收到这条消息的推送时,可以通过该方法获取 pushData 的内容。
    getExtra() 获取从开发者后台使用推送服务时,自定义的附加信息键值对。 该字段仅为开发者后台的推送服务使用,其余情况为 null。
    getPushFlag() 标识该消息是推送消息还是后台消息。

    集成第三方推送

    由于小米设备(或安装了小米 ROM 的设备)深度定制了安卓操作系统,致使一些后台自启动的系统服务接口被系统修改或屏蔽,进而导致几乎全部第三方推送服务都无法在这些设备上正常运行。

    为了保证各个设备使用推送服务的稳定性推送消息送达的准确和及时性,融云同时支持 GCM小米推送,用户通过少量的配置,即可支持这些推送。在用户做了对应配置的情况下,融云 SDK 会对设备及操作系统类型智能识别,在小米设备上将采用小米官方的推送服务将消息推送到小米设备;在国外设备上使用 GCM 推送;而其他设备仍然采用融云的原生推送服务。用户可以在开发者后台启动或者关闭这些配置。

    如何开通第三方推送

    在“开发者后台-应用标识”中,设置开通 谷歌推送(GCM)小米华为第三方推送功能,如图:

    image
    设置推送密钥后,表示开通对应第三方推送功能,如不想使用第三方推送功能,将设置保存为空即可,设置后 2 小时内生效。
    推送规则:优先根据你设置的 谷歌推送(GCM)小米华为第三方,匹配用户当前使用的手机终端后进行推送,如果匹配不成功,则默认使用融云推送功能。

    小米推送集成指南

    注:如不需要对小米设备进行单独的适配工作,则只需参考融云 Push 集成文档进行推送服务的集成工作,而不需要按照此文章进行更多的额外工作。

    前期准备

    在进行小米设备的推送适配之前,请确保您已经按照融云 Push 集成文档为您的 APP 集成了融云 SDK;这是对小米设备进行特殊适配的前提条件。

    注册小米开发者账号并开通小米推送

    由于对小米设备进行的推送采用了小米官方的推送渠道,因此,在开启小米推送服务之前,需要在小米官方的开发者中心进行开发者账号注册,并申请开发者资质,请根据自身情况选择开发者资质的申请类型,并等待审核通过:

    image

    开发者资质审核通过后,您可以进入 管理控制台 -> 消息推送,创建一个新 Android 应用:

    image

    注意,此处的应用包名需与您的 Android APP 的包名一致,否则可能引起无法收到推送消息的问题。

    成功创建应用后,进入该应用的详情页面,分别记录下 AppIDAppKey包名AppSecret 的值:

    image

    最后,在融云开发者后台-应用标识-Android 第三方推送,将上述 AppSecret 的值填入并设置推送通知标题后保存。

    推送通知标题:推送消息时在移动端通知栏展示的推送通知标题。

    至此,准备工作已完成!

    导入小米推送客户端 SDK

    请从小米开发者控制台中下载 SDK ,并将小米 SDK jar 导入到你的应用 module 的 libs 目录下,然后右键-》Add as library

    配置 AndroidManifest.xml 文件

    在应用 module 的 Androidmanifest.xml 文件下增加如下配置(注意这里需要替换成您自己的包名):

    <!-- 小米 配置开始 < -->
    <permission
    android:name="您的包名.permission.MIPUSH_RECEIVE"
    android:protectionLevel="signature" />
    <uses-permission android:name="您的包名.permission.MIPUSH_RECEIVE" /> <!-- 小米 配置结束 < -->
    

    application 节点增加配置如下的服务和广播:

    <!-- 小米 配置开始 < -->
     <service android:name="com.xiaomi.push.service.XMPushService" android:enabled="true" />
     <service android:name="com.xiaomi.mipush.sdk.PushMessageHandler" android:enabled="true" android:exported="true" />
     <service android:name="com.xiaomi.mipush.sdk.MessageHandleService" android:enabled="true" />
     <!--注:此service必须在2.2.5版本以后(包括2.2.5版本)加入-->
    
     <service
         android:name="com.xiaomi.push.service.XMJobService"
         android:enabled="true"
         android:exported="false"
         android:permission="android.permission.BIND_JOB_SERVICE" />
     <!--注:此service必须在3.0.1版本以后(包括3.0.1版本)加入-->
    
     <receiver android:name="com.xiaomi.push.service.receivers.NetworkStatusReceiver" android:exported="true">
       <intent-filter>
         <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
         <category android:name="android.intent.category.DEFAULT" />
       </intent-filter>
     </receiver>
     <receiver android:name="com.xiaomi.push.service.receivers.PingReceiver" android:exported="false">
       <intent-filter>
         <action android:name="com.xiaomi.push.PING_TIMER" />
       </intent-filter>
     </receiver>
     <receiver android:name="io.rong.push.platform.MiMessageReceiver" android:exported="true">
       <intent-filter>
         <action android:name="com.xiaomi.mipush.RECEIVE_MESSAGE" />
       </intent-filter>
       <intent-filter>
         <action android:name="com.xiaomi.mipush.MESSAGE_ARRIVED" />
       </intent-filter>
       <intent-filter>
         <action android:name="com.xiaomi.mipush.ERROR" />
       </intent-filter>
     </receiver>
     <!-- 小米 配置结束 < -->
    

    配置小米推送 AppID、AppKey

    最后,在您调用 RongIM.init() 之前,调用 RongPushClient.registerMiPush() 在客户端配置小米的 appIdappKey

    RongPushClient.registerMiPush(this, miAppId, miAppKey);
    

    注意:RongPushClient.registerMiPush() 一定要放在 RongIM.init() 前面。

    至此,全部适配工作已经完成。 现在您可以将 APP 安装至小米设备上,进行消息推送的测试。

    关于使用小米推送时通知的显示

    由于小米推送的透传消息,要求应用在后台处于启动状态,这样不能保证推送的及时性,所以在使用小米推送时,我们默认使用的是小米通知栏提醒的方式。

    小米透传类推送,小米官方说明:

    透传类推送是指开发者可选择不通过任何预定义的方式展现,由应用直接接收推送消息。
    利用透传消息,开发者可自定义更多使用推送的方式和展现形式,从而能更为灵活地使用消息推送通道。
    在一些拥有应用启动管理功能的 Android 系统上(如 MIUI ),透传的实现需要应用在后台处于启动状态。

    这种情况下您是无法自定义通知栏显示的,不过您仍然可以通过自定义的广播接收器里的 onNotificationMessageClicked() 捕捉到通知的点击事件,在里面做您需要的处理逻辑。

    在自启动权限打开的情况下,您还可以通过自定义广播接收器的 onNotificationMessageArrived() 捕捉到通知的到达事件。但是自启动权限没有打开的时候,是无法捕捉到通知到达事件的,这是由小米的手机系统特性决定的。

    public class DemoNotificationReceiver extends PushMessageReceiver {
      @Override
      public boolean onNotificationMessageArrived(Context context, NotificationMessage message) {
        return false;
      }
      @Override
      public boolean onNotificationMessageClicked(Context context, NotificationMessage message) {
        return false;
      }
    }
    

    华为 HMS 推送集成指南

    接入流程:

    1. 开发者平台注册成为融云开发者。
    2. 在“开发者后台”创建应用。
    3. 申请 HMS 权益。
    4. 集成 HMS-SDK,完成应用开发。

    注册成为开发者,创建应用:

    华为开发者联盟网站:

    中文: http://developer.huawei.com/cn/consumer/

    英文:http://developer.huawei.com/en/consumer/

    打开华为开发者联盟网站,进入“注册”,注册成为开发者。然后进入管理中心创建移动应用。

    申请 HMS 权益:

    登录华为开发者联盟,进入“管理中心”->“应用管理”, 找到要申请权益的移动应用,点击权益列的“+”,打开“配置权益”对话框,申请需要的 HMS 权益(这里是 Push 权益)。

    image
    申请权限

    申请 HMS 权益时,需要提供应用签名证书的 SHA256 指纹,可以进入到 App Key 文件的路径下,通过下面方法获得:

     keytool -v -list -keystore App 的 .key 文件
    

    注:如果您之前集成过华为推送,升级 HMS Push 不需要另外开通 Push 权益。

    copy 到华为后台,点击保存即可。

    image
    保存 sha256

    集成 HMS-SDK 到应用:

    • 配置 gradle 文件

    App 目录下创建 aars 目录,并把 HMS-SDK-{version}.aar 复制到此 aars 目录。修改 build.gradle 文件使工程依赖 HMS-SDK,在 App 下的 build.gradle 中加入:

    dependencies {
        compile(name: 'HMS-SDK-2.4.0.300', ext: 'aar')
    
    }
    repositories { flatDir { dirs 'aars' } }
    
    • 配置 manifest 文件

    在 application 节点下增加:

    <meta-data
         android:name="com.huawei.hms.client.appid"
         android:value="app id" />
    
    <provider
        android:name="com.huawei.hms.update.provider.UpdateProvider"
        android:authorities="xxx.xxx.xxx.hms.update.provider"
        android:exported="false"
        android:grantUriPermissions="true"></provider>
    <!-- 第三方相关 :接收Push消息(注册、Push消息、Push连接状态)广播 -->
    <receiver android:name="io.rong.push.platform.HMSReceiver" >
        <intent-filter>
    <!-- 必须,用于接收token -->
        <action android:name="com.huawei.android.push.intent.REGISTRATION" />
        <!-- 必须,用于接收消息 -->
        <action android:name="com.huawei.android.push.intent.RECEIVE" />
        <!-- 可选,用于点击通知栏或通知栏上的按钮后触发onEvent回调 -->
        <action android:name="com.huawei.android.push.intent.CLICK" />
        <!-- 可选,查看push通道是否连接,不查看则不需要 -->
        <action android:name="com.huawei.intent.action.PUSH_STATE" />
                </intent-filter>
    </receiver>
    <receiver android:name="com.huawei.hms.support.api.push.PushEventReceiver" >
         <intent-filter>
    
         <!-- 接收通道发来的通知栏消息,兼容老版本Push -->
         <action android:name="com.huawei.intent.action.PUSH" />
         </intent-filter>
    </receiver>
    

    其中 meta-data 中,指定了应用 ID,“appid” 用实际申请的应用 ID 替换。provider,用于 HMS-SDK 引导升级 HMS, 供给系统安装器读取升级文件, “xxx.xxx.xxx” 用实际的应用包名替换。

    在 manifest 节点下增加:

    <uses-permission android:name="android.permission.INTERNET" />
    
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    

    其中 android.permission.INTERNET,用于 HMS-SDK 引导升级 HMS 功能,访问 OTA 服务器; 其中 android.permission.WRITE_EXTERNAL_STORAGE,用于 HMS-SDK 引导升级 HMS 功 能,保存下载的升级包。

    客户端调用 HMS 接口

    最后,在您调用 RongIM.init() 之前,调用 RongPushClient.registerHWPush(this) 在客户端配置 HMS 推送。

    try {
      RongPushClient.registerHWPush(this);
    } catch (RongException e) {
      e.printStackTrace();
    }
    
    注意:如果之前集成过老的华为 Push,则把之前的 jar 包删除以及之前配置的配置文件部分删除即可。HMS 需要在手机上安装 2.4.0+ 版本的华为移动服务(华为帐号),并且需要开启后台保护,否则无法收到 Push 消息。
    融云 SDK 从 2.8.10 版本开始支持 HMS 推送,如您使用的是之前版本的 SDK 请先进行升级。

    处理连接错误:

    • 在连接获取 Token 中返回错误码 6XXX:

      先检查网络是否正常连接 检查应用是否已经在开发者联盟上面开通 Push 权益,如果都没问题是否在开发者联盟配置了签名证书的 SHA256 指纹,如果配置了还是不行说明应该是配置还没生效。

    • 在连接华为移动服务中返回错误码 2:

      是手机上的华为移动服务(华为账号)版本太低,不支持,需要升级到最新版。

    • 接口鉴权失败,返回 9071357XX 错误:

      不允许使用 Push 能力 联盟上的证书指纹与应用的证书指纹不匹配

    混淆打包

    如果需要混淆,则加上下面的混淆脚本:

    -ignorewarning -keepattributes *Annotation*
    -keepattributes Exceptions -keepattributes InnerClasses -keepattributes Signature
    # hmscore-support: remote transport
    -keep class * extends com.huawei.hms.core.aidl.IMessageEntity { *; }
    # hmscore-support: remote transport
    -keepclasseswithmembers class * implements com.huawei.hms.support.api.transport.DatagramTransport {
    <init>(...); }
    # manifest: provider for updates
    -keep public class com.huawei.hms.update.provider.UpdateProvider { public *; protected *; }
    

    通知栏推送方式

    华为推送分为透传推送和通知栏推送两种方式。 两种方式的区别如下:

    • 透传推送:华为推送服务会将原始 json 数据发给目标客户端,客户端内嵌的融云 SDK 接受到该 json 数据后,会进行解析并在通知栏显示。
    • 通知栏推送:由华为操作系统直接弹通知栏。

    融云默认为透传推送方式,如需要使用通知栏推送方式,则需要在“融云开发者后台 -》 应用标识 -》华为推送设置下面”,勾选“开启华为推送通知栏消息”开启。

    拦截通知点击事件

    华为通知栏推送方式下,当用户点击通知时,华为系统会通过隐式调用的方式,发出 intent 事件,您可以拦截该事件,获取用户点击行为以及携带的数据。

    默认传递的 intent 如下:

    intent://cn.rongcloud.im/conversationlist?isFromPush=true#Intent;scheme=rong;launchFlags=0x4000000;end
    

    您需要在 AndroidManifest.xml 中进行如下配置,点击通知时,即会启动对应的 Activity。

    <activity
        android:name=".ui.activity.ConversationListActivity"
        android:launchMode="singleTask"
        android:screenOrientation="portrait"
        android:windowSoftInputMode="stateHidden|adjustResize">
    
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <data
                android:host="cn.rongcloud.im"
                android:path="/subconversationlist"
                android:scheme="rong" />
        </intent-filter>
    </activity>
    

    该 intent 会同时携带一些附加数据,比如您在调用 RongIMClient.getInstance.sendMessage() 时传递的参数 pushData,比如您在使用开发者后台的广播推送功能时,自定义的键值对等。

    您可以参考以下示例获取数据,并进行相应的业务处理。

    Intent intent = getIntent();
    if (intent.getData().getScheme().equals("rong") && intent.getData().getQueryParameter("isFromPush") != null) {
        if (intent.getData().getQueryParameter("isFromPush").equals("true")) {
            String options = getIntent().getStringExtra("options"); // 获取 intent 里携带的附加数据
            NLog.d(TAG, "options:", options);
            try{
                JSONObject jsonObject = new JSONObject(options);
                if(jsonObject.has("appData")) {   // appData 对应的是客户端 sendMessage() 时的参数 pushData
                    NLog.d(TAG, "pushData:", jsonObject.getString("appData"));
                }
                if(jsonObject.has("rc")) {
                    JSONObject rc = jsonObject.getJSONObject("rc");
                    NLog.d(TAG, "rc:", rc);
                    String targetId = rc.getString("tId"); // 该推送通知对应的目标 id.
                    String pushId = rc.getString("id");  // 开发者后台使用广播推送功能发出的推送通知,会有该字段,代表该推送的唯一 id, 需要调用下面接口,上传用户打开事件。
                    if(!TextUtils.isEmpty(pushId)) {
                        RongPushClient.recordNotificationEvent(pushId); // 上传用户打开事件,以便进行推送打开率的统计。
                        NLog.d(TAG, "pushId:", pushId);
                    }
                    if (rc.has("ext") && rc.getJSONObject("ext") != null) {
                        String ext = rc.getJSONObject("ext").toString(); // 使用开发者后台的广播推送功能时,填充的自定义键值对。
                        NLog.d(TAG, "ext:", ext);
                    }
                }
            } catch (JSONException e) {
    
            }
            enterActivity();
        }
    }
    

    自定义 intent

    您也可以自定义点击通知时,发出的 intent,自定义的 intent 获取方式可以参考如下代码:

    Intent intent = new Intent(Intent.ACTION_VIEW);
    Uri uri = Uri.parse("rong://" + this.getPackageName()).buildUpon()
            .appendPath("conversationlist")
            .appendQueryParameter("isFromPush", "true").build();
    intent.setData(uri);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    String intentUri = intent.toUri(Intent.URI_INTENT_SCHEME);
    

    将上面代码中最终生成的 intentUri 填写到开发者后台即可。

    GCM 推送集成指南

    前期准备

    在阅读本教程前,你需要为 Android 应用的开发完成一些准备工作。

    安装开发工具

    • 安装 Android Studio。另外,按需安装其他 Android 开发工具。你可以使用其他开发工具。本教程中,我们将以 Android Studio 为例。

    注册成为 Android 开发者

    • 谷歌开发者平台中注册成为 Android 开发者并获得创建新项目和修改配置的权限。

    创建 Google 项目

    第一步需要在谷歌环境下创建一个项目。

    请注意:在阅读以下内容之前,你需要注册成为 Google Play 开发者。

    本部分主要讲解如何使用 GCM 文档中所述的 Get a configuration file 功能创建一个项目 (也可以在 Google Developers Console 上创建一个项目)。

    配置完成后,你可以下载 JSON 文件。我们将会把此文件集成到 Android 项目中,从而使应用程序可以引用此配置。

    创建一个项目

    打开 GCM 文档中的教程页面

    向下翻到 2. Get a configuration file 并点击 GET A CONFIGURATION FILE 按钮。

    image

    你将会直接来到 Google Developers 页面。在此,你可以通过输入如下信息并点击 Choose and configure services 按钮来创建一个新的应用程序:

    • App name 这里填入您的应用的名称。该值将用于 Google Developers Console 识别该项目。
    • Android package name 这里填写您的 Android 应用程序的包名。
    • Share your Google Mobile Developer Services data... 如果需要提供应用程序的使用情况给 Google ,请勾选。
    • Your country/region 设置应用用程序开发者所在的位置。
    image

    然后到服务选择界面,选择 Cloud Messaging 进入下一个界面。点击 ENABLE GOOGLE CLOUD MESSAGING

    image
    image

    当功能激活时,将显示为绿色。稍微向下翻动即可看到 Server API KeySender IDServer API KeySender ID 将在稍后用到。此时,请将这两个值保存起来(比如,复制并粘贴到记事本中)。

    • Server API Key:我们将会把它设置到开发者平台上名为 GCM Key 的配置中。
    • Sender ID:如果你使用 Unity,此值将被设置在源代码中。如果使用 Android,此值将随 JSON 文件被自动设置。

    然后,向下翻动并点击 Generate configuration files 按钮。

    用于下载 JSON 文件的按钮将会显示出来。点击 Generate configuration files 按钮并将 JSON 文件保存到电脑中。

    image

    将刚下载的文件拷贝到 Android Studio 项目的 App 目录下。 GCM 端的设置已经完成。

    再次查看各值

    如果你需要再次查看 Sender API KeySender ID,请前往 Google Developers Console 从列表中选择目标项目并查看如下值:

    • Server API KeyCredential 界面内 Server key 中的 Key
    • Sender ID:当你打开项目时显示的 Project number

    配置编译环境

    为了使用 GCM,你需要配置项目以使用 Google Play Services 提供的库。

    下载库

    你可以在 Android Studio 上下载 Google Play Service 库(Google Repository)。 启动 Android Studio 上方工具栏中的 SDK Manager

    image

    将会显示 SDK 配置界面。打开 SDK Tool 标签页,在列表中选择 Google Repository,并点击 Apply 按钮。库将会被下载并安装到编译环境中。

    image

    如果已经安装了库,请直接关闭此界面。

    配置 build.gradle(项目)

    接下来,打开位于项目根目录下的 build.gradle 并在 buildscript/dependencies 中加入 classpath 'com.google.gms:google-services:1.3.0'

    配置后的文件如下:

    buildscript {
      repositories {
        jcenter()
        maven { url "http://nexus.add1.me:8101/nexus/content/repositories/thirdparty" } mavenCentral()
      }
      dependencies {
        classpath 'com.android.tools.build:gradle:1.3.0'
        // just for GCM.
        classpath 'com.google.gms:google-services:2.0.0-alpha3'
      }
    }
    allprojects {
      repositories {
        jcenter()
        maven { url "http://nexus.add1.me:8101/nexus/content/repositories/thirdparty" } mavenCentral()
      }
    }
    

    1.3.0 是此文档创建时能用的最新版本。如果有更新的版本可用,那么使用新版本是没有问题的(但是下一步中设置的 play-services-gcm 可能会导致冲突)。你也可以指定 + 来使用最新的版本,但是请注意这可能会对你的项目进行一些无法预料的更新。

    配置 build.gradle (组件)

    打开位于编译目标组件下的 build.gradle 文件(通常位于 App 目录下)并按如下方式修改此文件:

    • compile 'com.google.android.gms:play-services-gcm:8.4.0' 加入到 dependencies 中。
    • apply plugin: 'com.google.gms.google-services' 加入根级别中。

    配置后的文件如下:

    apply plugin: 'com.android.application'
    
    android {
      ...
    }
    dependencies {
      compile fileTree(dir: 'libs', include: ['*.jar']) ...
      compile 'com.google.android.gms:play-services-gcm:8.4.0'
    }
    apply plugin: 'com.google.gms.google-services'
    

    8.4.0 是此文档创建时能用的最新版本。如果有更新的版本可用,那么使用新版本是没有问题的(和 google-services 一样)。

    完成以上步骤后,点击工具栏中的 Sync Project with Cradle Files 按钮来同步修改。现在你可以在项目中引用 Google Play Services 了。

    配置 AndroidManifest.xml 文件

    在 AndroidManifest.xml 文件里增加下面的 permission. 注意,这里的包名 io.rong.app 需要替换成您自己的包名。

    <!-- GCM 配置开始 < -->
    <permission
      android:name="io.rong.app.permission.C2D_MESSAGE"
      android:protectionLevel="signature" />
    <uses-permission android:name="io.rong.app.permission.C2D_MESSAGE" />
    <!-- GCM 配置结束 < -->
    

    application 节点增加如下 servicebroadcastReceiver 的配置:

    <!-- GCM 配置 < -->
     <receiver android:name="com.google.android.gms.gcm.GcmReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND">
       <intent-filter>
         <action android:name="com.google.android.c2dm.intent.RECEIVE" />
         <category android:name="io.rong.app" />
         <!-- 替换为自己的packagename < -->
       </intent-filter>
     </receiver>
    
     <service android:name="io.rong.push.platform.RongGcmListenerService" android:exported="false">
       <intent-filter>
         <action android:name="com.google.android.c2dm.intent.RECEIVE" />
       </intent-filter>
     </service>
     <service android:name="io.rong.push.platform.RongGCMInstanceIDListenerService" android:exported="false">
       <intent-filter>
         <action android:name="com.google.android.gms.iid.InstanceID" />
       </intent-filter>
     </service>
     <!-- GCM 配置结束 < -->
    

    客户端配置 GCM 推送

    最后,在您调用 RongIM.init() 之前,调用 RongPushClient.registerGCMPush() 在客户端配置 GCM 推送。

    try {
      RongPushClient.registerGCM(this);
    } catch (RongException e) {
      e.printStackTrace();
    }
    

    注意:RongPushClient.registerGCMPush() 一定要放在 RongIM.init() 前面。

    registerGCM 的时候,融云 SDK 会调用 checkPlayServices 方法检查 Google Play Services 是否可用。如果服务不可用,会抛出 RongException,您可以根据应用需要,选择是否忽略或者弹出对话框提醒用户安装对应环境等。

    混淆脚本

    -dontwarn com.xiaomi.mipush.sdk.**
    -keep public class com.xiaomi.mipush.sdk.* {*; }
    -keep public class com.google.android.gms.gcm.**
    

    常见问题

    如何自定义通知栏的显示?

    2.6.0 之前的版本,请参考文档

    2.6.0 之后的版本,通知事件统一在您自定义的继承自 PushMessageReceiver 的广播接收器里处理,请参考如何使用远程推送自定义 BroadcastReceiver 部分。分为两种情况:

    1、自定义通知显示

    a. 自定义接受到 push 消息时的通知显示:

    请在自定义的广播接收器里做如下处理:

    @Override
    public boolean onNotificationMessageArrived(Context context, PushNotificationMessage message) {
        #此处实现你的自定义通知#
        return true;  //返回true,代表不走融云SDK默认处理逻辑
    }
    

    b. 自定义后台通知的显示。

    设置接受消息的监听 setOnReceiveMessageListener, 在 onReceived() 回调里返回 true, 并弹出您的自定义通知。

    c. 最后,在你的自定义通知的点击事件里,调用下面的 API 来上传通知点击事件,以便统计您的推送打开率。

    if(!TextUtils.isEmpty(message.getPushId())) {
        RongPushClient.recordNotificationEvent(message.getPushId());
    }
    
    注:如果集成了小米推送,由于小米系统的一些特殊原因,我们使用的是小米通知栏提醒方式由小米系统直接弹出通知栏,所以这种情况下的通知是无法自定义的,具体说明可参考小米推送集成指南关于使用小米推送时通知的显示说明。

    2、自定义通知点击时的跳转

    从 2.6.0 开始,可以支持自定义通知点击时的跳转事件,而不需要自定义通知。

    在自定义的广播接收器里做如下处理:

    /**
    * 自定义SDK默认通知的点击跳转
    */
    @Override
    public boolean onNotificationMessageClicked(Context context, PushNotificationMessage message) {
    # 此处实现您在点击通知时的跳转处理代码 #
    return true; //返回true,代表不走融云SDK默认处理逻辑
    }
    

    怎么进行推送打开率的统计?

    从开发者后台使用推送服务的时候,怎么进行推送打开率的统计?客户端需要做什么?

    2.6.0 之前的版本,请参考文档

    2.6.0 之后的版本:

    如果您没有自定义通知,那客户端不需要做额外操作,SDK 内部已经进行了打开率的统计。

    如果您自定义了通知显示(请参考问答:如何自定义通知栏的显示),需要在你的自定义通知的点击事件里,调用下面的 API 上传打开事件:

    if(!TextUtils.isEmpty(message.getPushId())) {
        RongPushClient.recordNotificationEvent(message.getPushId());
    }
    

    收不到推送如何排查?

    1、首先确认没有多端登录,没有开启消息免打扰,没有关闭新消息提醒。

    另外还需要确认手机上没有安装净化大师,360 安全助手等第三方管理软件,因为这些软件有可能会阻止应用的后台启动或者阻止通知显示。

    2、调用 RongPushClient.checkManifest() 检查 AndroidManifest.xml 里面关于融云 push 的配置是否正确。如果有遗漏,这个方法会抛出异常,根据错误信息正确修改你的配置文件。需要注意的是,这只是个帮助方法,在你检查完毕后,把它删除即可。

    3、经过第二步检查,如果你的应用按 back 键调用 disconnect() 后,仍然收不到推送。那换一个品牌的手机试试,确认是否是手机操作系统权限问题引起的(建议用三星手机测试,三星手机一般没有权限要求)。或者试试打开手机的自启动权限。

    4、如果你的应用是从任务管理器杀掉进程后,收不到推送,那一般是由于自启动权限引起的。部分 Android 手机系统在黑屏待机后自动清理后台运行的软件,这样影响了应用正常接收新的消息,需要将应用设置为后台运行应用。查看各类机型的设置说明

    5、如果不属于上面任何情况,那可以提工单,工单里详细描述如下几点:

    • a、是直接退出收不到推送,还是杀掉进程后收不到?
    • b、对应权限是否都已打开。
    • c、有没有集成小米推送或华为推送?
    • d、附件里附上日志。先卸载掉你的应用,抓取你的应用从第一次安装到启动成功的日志,不要过滤,输出到文件里上传到工单中。