让你的 Android 应用也能聊天

作者: 丰俊文 | 来源:发表于2014-11-18 19:44 被阅读1856次

    LeanMessage 移动开发 SDK 是由 LeanCloud 提供的,专为 iOS、Android 和 WindowsPhone® 等客户端程序提供应用内聊天的 API 和服务,并且也提供了 JavaScript API,方便开发者打通网页和客户端应用,给最终用户提供统一的使用体验。使用 LeanMessage API,您可以极快地以最少工作量让您的移动应用支持实时聊天,得到一种如微信一般的沟通体验。

    开始之前
    出于本文的目的,我假设您已经非常熟悉使用 JSON、Android 和 Eclipse 进行移动应用编程的基本概念。在您继续阅读本文之前,请访问 leancloud.cn 并创建您的应用程序。只需遵循注册页面中的简单指令即可。

    本文介绍了包含单聊、群聊、历史记录和应用鉴权的核心 API 类。您将学习如何简单进行用户间一对一单聊,以及如何创建群组让多用户进行群聊,还有如何通过签名来对聊天通道进行控制,以保护应用和用户的隐私。示例均构建于 LeanMessage SDK for Android 之上(请参阅Android开发指南)。

    基本概念和类

    聊天参与者 Peer

    在 LeanMessage 实时消息世界中,每一个参与者(一般而言是「人」)都是一个 Peer。Peer 拥有一个在应用内唯一标识自己的 ID(称为 PeerID,字符串类型,长度不超过 50 字节,具体数值由应用自身确定),系统中的每一条消息都来自于一个 Peer,发送到一个或多个 Peer。并且,LeanMessage 的消息服务允许一个 Peer 在多个不同设备上登录,也允许一个设备上同时登录多个 Peer,究竟该如何使用,由应用根据使用场景自己选择。

    这里要注意的是,PeerID 是由应用开发者自己賦值的,LeanMessage 本身并没有任何强制要求,所以:

    • 实时消息系统是可以和用户账户系统解耦合的,应用开发者不需要把除了 PeerID 以外的任何信息告诉 LeanMessage;
    • LeanMessage 在消息转发的时候是按照 PeerID 来唯一定位的,因此如果应用自身支持同一账户的多点登录,那么 LeanMessage 就会把消息通知到所有终端;
    • 匿名聊天/非匿名聊天这都是应用层自己决定的,如果应用自身能为匿名用户指定一个唯一的 ID,那么这个用户参与到聊天系统里来,是完全没有问题的。

    为了防止骚扰,一个 Peer 需要先关注(watch)了对方才能给对方发送消息;因为 LeanMessage 提供了更细粒度的权限控制,应用开发者可以在关注(watch)动作上增加签名来保证安全性。这一点后面会进行详细说明。

    实时消息 AVMessage

    在 LeanMessage 中所有的消息都是 AVMessage 的实例,AVMessage 只支持文本,并且长度不能超过 5KB。消息分为暂态(transient)和持久消息两种类型。所有持久消息都会在 LeanMessage 云端保存,所以用户离线之后也可以得到通知和接收,而暂态消息并不会离线保存,适合开发者用来进行协议控制。

    AVMessage 的定义如清单 1 所示:

    public class AVMessage implements Parcelable {
      private List<String> toPeerIds; // 消息接收方的 PeerID,支持一个或多个
    
      String groupId; // 消息所属群组的ID,对于普通一对一聊天消息而言,此值为空
      String message; // 消息体
      long timestamp; // 消息发送时间戳
      boolean isTransient; // 是否是暂态消息
      String fromPeerId; // 消息发送方的 PeerID
    
      public AVMessage();
      public AVMessage(String message);
      public AVMessage(String message, List<String> toPeerIds, boolean isTransient);
      public AVMessage(String message, boolean isTransient);
    }
    

    LeanMessage 为所有历史消息都提供了存储和查询的功能,存储时间则根据开发者的类型有所不同。

    聊天会话 Session

    每一个 Peer 通过开启(open)一个会话(Session)而加入实时消息服务,Peer 可以在一个会话中关注(watch)一个或多个 Peer,当被关注者上下线时,会收到通知。Peer 在开启会话后只能向自己关注的其他 Peers 发送消息,但可以收到任何 Peer 发来的消息,也就是说单向关注时,消息可以顺利地由关注者发往被关注者。

    Session 有如下几种状态:

    • opened。 Session 被正常打开,此时可以进行正常的通信;
    • pause。 网络异常(譬如 wifi 断开,3G/2G 切换,iOS 应用进入后台,等),Session 进入暂停状态,当网络恢复时,Session 会自动重连;
    • resume。 应用转入前台,会话重新建立起来(此状态只在 iOS 设备上有效)
    • closed。 Session 结束,仅在显示调用了 Session.close 方法时才会发生。用户注销实时通信服务,不再能够接收到消息或推送通知;

    Session 上可以进行的操作有:

    • open 以一个 Peer ID 打开 Session
    • watch 关注一组 Peer ID,关注后可以收到这个 Peer 的上下线通知,发送消息
    • unwatch 取消对一组 Peer ID 的关注
    • sendMessage 给一组 Peer ID 发送消息
    • queryOnlinePeer 查找当前在线的 Peers
    • getHistoryMessageQuery 查找历史消息
    • setSignatureFactory 设置签名类(为了保证安全性,后面会讲述)
    • close 注销服务,关闭 Session

    一对一的文本聊天

    明白了这三个概念之后,我们就可以开始进入实际聊天环节了。

    首先我们需要在 application 的 onCreate 函数中进行 LeanCloud 最基本的初始化:

    @Override
    public void onCreate() {
      super.onCreate();
      AVOSCloud.initialize(this, "pleaseReplaceWithYourAppId", "pleaseReplaceWithYourAppKey");
    }
    

    接下来我们来看一下怎么样进行一对一的基本聊天。首先,我们需要开启一个会话(Session),示例代码如清单 2 所示:

    SessionManager session = SessionManager.getInstance(selfId);//获取SessionManager实例,以便于后续的操作。这里的 selfId 可以是用户的实际 id,也可以是其他唯一的字符串,譬如「Tom」。
    List<String> watchedIds = new LinkedList<String>();
    session.open(watchedIds); //打开Session,同时关注一些 PeerID。此时没有关注对象
    

    注意!

    一般而言,会话的开启是在用户登录之后的 RootActivity 中进行的。对于支持匿名聊天的应用,也可以在 Application 启动的时候进行。千万不要在一个临时或短命的 Activity 中开启聊天会话。上面代码中 SessionManager 也是 Session 的子类,所以可以直接调用 Session 的方法。

    接下来,我们开始跟「Bob」这个用户进行聊天。为了给他发送消息,我们先要关注(watch)他,代码如下:

    List<String> peerIds = new LinkedList<String>();
    peerIds.add("Bob");
    session.watchPeers(peerIds);
    

    之后我们给「Bob」发送一条消息:

    List<String> peerIds = new LinkedList<String>();
    peerIds.add("Bob");
    session.sendMessage(new AVMessage("嗨,你好,我是 Tom", peers, false));
    

    好了,这样一条消息就发送过去了。但是问题来了,对于「Bob」而言,他怎么才能收到别人发给他的消息呢?

    上面对于 Session 的所有操作都是异步的。与一般 Android 异步方法调用不同,LeanMessage SDK 的异步并不是通过 Callback 或者类似 RsyncTask 的机制实现的,而是通过继承 AVMessageReceiver 这一 BoardcastReceiver,实现以下方法来处理来自服务器端的响应的。AVMessageReceiver 接口定义如清单 3 所示:

    /**
     * 当服务器成功与客户端打开session时产生本次回调
     */
    public abstract void onSessionOpen(Context context, Session session);
    
    /**
     * 在 session 暂停时调用,一般都是由网络连接丢失导致的隐性调用
     */
    public abstract void onSessionPaused(Context context, Session session);
    
    /**
     * Session 恢复时,一般都是网络连接恢复以后的
     * 这个时候你可以处理一些由于网络异常导致的失败消息
     */
    public abstract void onSessionResumed(Context context, Session session);
    
    /**
     * 从某个Peer接收到消息时,会收到一次调用
     */
    public abstract void onMessage(Context context, Session session,
            AVMessage msg);
    
    /**
     * 服务器反馈消息已经成功发送时,会收到调用
     */
    public abstract void onMessageSent(Context context, Session session,
            AVMessage msg);
    
    /**
     * 在消息发送失败时,产生的调用 在这里你可以保存一下发送失败的消息以便未来重发
     */
    public abstract void onMessageFailure(Context context, Session session,
            AVMessage msg);
    
    /**
     * 当关注的一些peers上线时,产生的调用
     */
    public abstract void onStatusOnline(Context context, Session session,
            List<String> peerIds);
    
    /**
     * 当关注的一些peers下线时,产生的调用
     */
    public abstract void onStatusOffline(Context context, Session session,
            List<String> peerIds);
    
    /**
     * 当与服务器发生交互的过程中的任何错误,都在这里被返回
     */
    public abstract void onError(Context context, Session session, Throwable e);
    

    从上面接口的定义中,我们可以看到,要接收到别人发过来的消息,只需要响应 onMessage() 方法即可。代码示例如清单 4 所示:

    public class CustomeMsgReceiver extends AVMessageReceiver {
        @Override
        public void onMessage(final Context context, Session session, AVMessage avMsg) {
            Logger.d("onMessage "+avMsg.getMessage());
            // 进行上层逻辑处理,譬如 UI 展示,或者消息提醒。
        }
    }
    
    // 在 AndroidManifest.xml 文件中声明这一 BoardcastReceiver。
    <receiver android:name=".receiver.CustomeMsgReceiver">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
            <action android:name="com.avoscloud.session.action" />
        </intent-filter>
    </receiver>
    

    支持富媒体的聊天消息

    上面的代码演示了如何发送文本信息,但是现在的交互方式已经越来越多样化,图片、语音、视频已是非常普遍的媒体类型。而从 AVMessage 的定义来看,只支持不超过 5KB 大小的文本,那么 LeanMessage 又如何能支持富媒体的聊天消息呢?

    记得 LeanStorage 中的 AVFile 吗?
    AVFile 是 LeanStorage 提供的非结构化数据存储解决方案,可以让你的应用程序将二进制文件存储到云端服务器中,并且自动提供 CDN 加速服务,能带给用户更迅捷的下载体验。比如常见的文件类型图像文件、影像文件、音乐文件和任何其他二进制数据都可以使用。具体说明可以参见Android 开发文档

    对于图片、语音、视频这类较大的非结构化数据,存储到云端文件系统之后,在消息中发送 url 已是业界惯例,并且 LeanMessage 中 AVMessage 类的定义并没有规定消息体是什么类型,所以我们可以充分利用这一扩展空间,结合 AVFile 来发送、接收富媒体的聊天消息。实现方法如清单 5 所示:

    AVFile file = AVFile.withAbsoluteLocalPath("test.jpg", Environment.getExternalStorageDirectory() + "/test.jpg");
    file.saveInBackground(new SaveCallback() {
        // override
        public void done(AVException ex) {
            if (null != ex) {
                // error
            } else {
                // construct message body under json format.
                HashMap<String, Object> params = new HashMap<String, Object>();
                params.put("type", "image");
                params.put("context", "嗨,你好,我是 Tom");
                params.put("attachment", file.getUrl());
                
                List<String> peerIds = new LinkedList<String>();
                peerIds.add("Bob");
                session.sendMessage(new AVMessage(JSON.toJSONString(params), peers, false));
            }
        }
    });
    

    新版本的 LeanMessage SDK 会支持富媒体消息,避免让每个开发者都重复做类似的工作。

    群组聊天

    在聊天的需求里,还有一个很重要的场景,就是群组聊天。从前面 AVMessage 的定义我们可以猜到,LeanMessage 应该是支持群聊的,那实际上该如何实现呢?下面我们一步一步来尝试一下。

    基本概念

    与普通的单聊相比,群聊增加了如下两个基本概念:

    • 群组 AVGroup

    AVGroup 代表一个聊天群组,可以对应到实际的多人聊天、聊天群、聊天室等,每个 AVGroup 有一个唯一的 ID(groupID,由 LeanMessage 云端分配),其定义如清单 6 所示:

       public class AVGroup implements Group {
          String roomId;
          String selfId;
          Session session;
       }
    

    一个 Peer 加入群后向群发送的消息可以被所有群成员收到。当有新成员加入或者既有成员退出时,所有群成员都会得到通知。AVGroup 上可以进行的操作有:

    public interface Group{
        public void join();
        public void sendMessage(AVMessage msg);
        public void kickMember(List<String> peerIds);
        public void inviteMember(List<String> peerIds);
        public void quit();
        public String getGroupId();
        public String getSelfId();
        public AVHistoryMessageQuery getHistoryMessageQuery();
    }
    
    • 群组消息接受器 AVGroupMessageReceiver

    与 AVMessageReceiver 类似,AVGroupMessageReceiver 主要用来处理群组操作的结果。其详细定义如清单 7 所示:

      /**
       *在加入聊天室成功后被调用 如果join时,没有带groupId,您可以在返回的group中间获取groupId
       */
      @Override
      public abstract void onJoined(Context context, Group group);
    
      /**
       * 当你被别人邀请进入某个聊天室以后
       * 
       * @param group
       * @param byPeerId
       *        这个人邀请了你
       */
      @Override
      public abstract void onInvited(Context context, Group group, String byPeerId);
    
      /**
       * 当你被别人踢出聊天室以后
       * 
       * @param group
       * @param byPeerId
       *            是他踢了你
       */
      @Override
      public abstract void onKicked(Context context, Group group, String byPeerId);
    
      /**
       * 处理消息发送成功事件
       */
      @Override
      public abstract void onMessageSent(Context context, Group group,
            AVMessage message);
    
      /**
       * 用来处理消息发送失败事件
       * 可以缓存起来,事后重发
       */
      @Override
      public abstract void onMessageFailure(Context context, Group group,
            AVMessage message);
    
      /**
       * 收到消息以后被调用
       * 一般通过这个接口来处理和接受来自Group的消息
       * @param context
       * @param group
       * @param message
       * @param fromPeerId
       *            发消息者
       */
      @Override
      public abstract void onMessage(Context context, Group group,
            AVMessage message);
    
      /**
       * 处理退出成功事件
       */
      @Override
      public abstract void onQuit(Context context, Group group);
    
      /**
       * 处理Group操作被拒绝的时间
       * @param context
       * @param group
       * @param op 这里可能存在的操作有 "join","invite","kick"
       * @param targetIds
       *            一般来说是指被操作的对象,在join操作中间就是指groupId本身,
       *            invite和kick中则指被邀请或者被踢除的peerIds
       */
      @Override
      public abstract void onReject(Context context, Group group, String op,
            List<String> targetIds);
    
      /**
       * 处理新用户加入事件
       */
      @Override
      public abstract void onMemberJoin(Context context, Group group,
            List<String> joinedPeerIds);
    
      /**
       * 处理用户退出事件
       */
      @Override
      public abstract void onMemberLeft(Context context, Group group,
            List<String> leftPeerIds);
    
      /**
       * 处理所有Group相关的异常
       */
      @Override
      public abstract void onError(Context context, Group group, Throwable e);
    

    加入聊天室

    由于整个实时通信功能都是建立在 Session 的基础上,所以我们要加入一个聊天室也需要建立在一个已经打开的 Session 上。 已经打开一个 Session 以后,可以通过以下操作来加入一个 Group:

        Group group = SessionManager.getInstance(selfId).getGroup();//准备新建一个聊天室
        //Group group = SessionManager.getInstance(selfId).getGroup(groupId); 加入一个已经存在的聊天室
        group.join(); // LeanMessage 云端会判断 groupId 是否存在,如果不存在就新建一个 Group,否则加入已有 Group
    

    加入成功之后 AVGroupMessageReceiver 子类中的 onJoined 方法就会被调用。

    往聊天室发送消息

    发送消息非常简单,通过如下代码就可以向特定聊天室发送消息了:

        Group group = SessionManager.getInstance(selfId).getGroup(groupId);
        group.sendMessage(new AVMessage("hello world"));
    

    发送成功之后,AVGroupMessageReceiver 子类中的 onMessageSent 方法会被调用,反之则 onMessageFailure 方法会被调用。

    接收聊天室消息

    接收一个聊天室的消息,与接收单聊的消息一样,需要开发者实现 AVGroupMessageReceiver 接口,并在 AndroidManifest.xml 中注册即可,如代码清单 8 所示:

    public class CustomeGroupMsgReceiver extends AVGroupMessageReceiver {
        ...
        @Override
        public void onMessage(final Context context, Group group, AVMessage avMsg) {
            Logger.d("onMessage "+avMsg.getMessage());
            // 进行上层逻辑处理,譬如 UI 展示,或者消息提醒。
        }
        ...
    }
    
    // 在 AndroidManifest.xml 文件中声明这一 BoardcastReceiver。
    <receiver android:name=".receiver.CustomeGroupMsgReceiver">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
            <action android:name="com.avoscloud.session.action" />
        </intent-filter>
    </receiver>
    

    查询聊天室成员

    在加入一个聊天室之后,我们第一步就是看看有哪些人在这个群组里面。LeanMessage 和 LeanStorage 是结合在一起的,通过使用 LeanStorage 的数据存储功能,来保存一个聊天室的基本信息(表名:AVOSRealtimeGroups),在 LeanStorage 应用管理平台的数据中心,我们可以看到 AVOSRealtimeGroups 的所有字段。

    LeanStorage 的数据中心
    LeanStorage 也是 LeanCloud 平台的核心服务之一,提供了应用内数据和文件数据的存储功能。对于应用内数据,LeanStorage 支持 schema free 的存储,开发者不需要事先定义数据的模式,只要符合 JSON 格式的 Object 都可以自由存储到 LeanStorage 云端。同时,LeanStorage 也提供一个 Web 版的数据管理界面,可以非常方便地增、删、改、查任何数据。

    当然,在我们知道一个聊天室的 groupId 的时候,也可以在代码中,通过 AVObject 的 fetch 接口来查看这个聊天室的组员情况,代码如清单 9 所示:

        AVObject groupObject = AVObject.createWithoutData("AVOSRealtimeGroups",groupId);
        groupObject.fetch();//如果您在UI进程中,请使用异步方法调用
        List groupMembers= groupObject.getList("m");
    

    谨防系统线程阻塞!
    回想一下,在移动应用程序中,长时间的操作(如网络、文件或长的计算)不应该在主系统线程上完成。相反,应在一个单独的工作线程中执行它们。阻塞系统线程会对应用程序的用户界面的响应能力产生负面影响,有可能导致强行关闭您的应用程序。

    成员管理

    在查询到聊天室成员之后,可以让用户邀请一些自己的朋友加入,作为管理员也可以剔除一些「可怕」的成员。代码如清单 10 所示:

        Group group = SessionManager.getInstance(selfId).getGroup(groupId);
        List<String> toInvite = Arrays.asList("peerId1","peerId2","peerId3");
        group.inviteMember(toInvite);
        List<String> toKickOff = Arrays.asList("badBoy1","badBoy2");
        group.kickMembers(toKickOff);
    

    邀请成功以后,通知的流程是这样的:

        操作者(管理员)                           被邀请者                        其他人
    1,发出请求 inviteMember
    2,AVGroupMessageReceiver.onInvited
    3,                                      AVGroupMessageReceiver.onJoined
    4,AVGroupMessageReceiver.onMemberJoin                          AVGroupMessageReceiver.onMemberJoin
    

    相应地,踢人的流程如下:

        操作者(管理员)                           被踢者                         其他人
    1,发出请求 kickMember
    2,AVGroupMessageReceiver.onKicked
    3,                                    AVGroupMessageReceiver.onQuit
    4,AVGroupMessageReceiver.onMemberLeft                         AVGroupMessageReceiver.onMemberLeft
    

    查询历史消息

    LeanMessage 会将非暂态消息自动保存在云端,之后开发者可以通过 AVHistoryMessageQuery 这个对象来进行查询。AVHistoryMessageQuery 定义如清单 11 所示:

    public class AVHistoryMessageQuery {
        int limit;
        String convid;
        String from;
        long timestamp;
    
        /**
         * 设置查询返回集合的大小
         * 默认100,最大1000
         */
        public void setLimit(int limit);
    
        /**
         * 设定聊天的发起人是谁
         */
        public void setFrom(String from);
    
        /**
         * 设置查询从哪个时间开始的聊天记录
         */
        public void setTimestamp(long timestamp);
    
        /**
         * 指定聊天记录查询条件中,聊天发送的对象条件
         */
        public void setPeerIds(List<String> peerIds);
    
        /**
         * 同步方法查询聊天记录
         * 请确保在一个异步方法中调用此方法,否则会出现UI线程中的网络请求而导致的UI卡死
         */
        public List<AVHistoryMessage> find() throws AVException;
    
        /**
         * 异步方法查询聊天记录
         */
        public void findInBackground(HistoryMessageCallback callback);
    
        /**
         * 此接口为异步查询聊天记录的回调类
         */
        public static interface HistoryMessageCallback;
    }
    

    通过 AVHistoryMessageQuery 查询得到的结果是 AVHistoryMessage,该类的定义如清单 12 所示:

    public class AVHistoryMessage extends AVMessage {
        /**
         * 查看是否属于聊天室聊天记录
         */
        public boolean isRoom();
    
        /**
         * 查看聊天记录所在的conversation Id,对于 Group 来说等于 GroupID,对于单聊来说,是内部生成的一个值。
         */
        public String getConvid();
    }
    

    聊天记录的查询的基本方法跟 AVQuery 类似但是略有不同。 针对 Session 的聊天记录和聊天室 Group 的聊天记录查询略有不同,但是基本流程是一样(代码清单 12):

           String selfId = "Tom";
           SessionManager sm = SessionManager.getInstance(selfId);
           List<String> peers = new ArrayList<String>();
           peers.add(selfId);
           peers.add("Bob");
           AVHistroyMessageQuery sessionHistoryQuery = sm.getHistroyMessageQuery();
           sessionHistoryQuery.setLimit(100);  //设置查询结果大小
           sessionHistoryQuery.setPeerIds(peers); // 设置单聊的参与方,多个参与者之间是「与」的关系
           sessionHistoryQuery.setTimestamp(1413184345686);  //查询时间片1413184345686以前的聊天记录
           sessionHistoryQuery.findInBackground(new HistoryMessageCallback() {
                 @Override
                 public void done(List<AVHistoryMessage> messages, AVException error) {
                      System.out.println(messages.size());
                 }
           });//查询session里的聊天记录
           
           Group group = sm.getGroup("140a534fd092809500e6d651e73400c7");
           AVHistroyMessageQuery groupHistoryQuery = group.getHistoryMessageQuery();//获取AVHistoryMessageQuery对象来查询聊天室的聊天记录
           groupHistoryQuery.findInBackground(new HistoryMessageCallback(){
                @Override
                public void done(List<AVHistoryMessage> messages,AVException error){
                  for(AVHistoryMessage msg:messages){
                     System.out.println(msg.getMessage());
                  }
                }
           })
    

    上面第一个查询会拿到「Tom」和「Bob」在特定时间点以前的 100 条聊天记录;第二个查询会拿到特定聊天室的所有聊天记录(如果总数不超过 1000 条的话)。

    一览查看所有聊天室

    查看所有聊天室的方法和查看单个聊天室成员的方法类似,都是直接通过 AVQuery 或者 AVObject 来遍历 AVOSRealtimeGroups 表实现的,这里不再赘述。

    聊天记录和安全

    前面实现了单聊、群聊、富媒体聊天诸多功能,但是开发者可能已经发现了,这都是直接调用 LeanMessage SDK 来实现的,对于我们开发者来说,能控制的东西很少,在安全性上会存在一些担心。譬如:万一不怀好意的人破解了我的 appId 和 appKey,是不是就可以在我的聊天社区里面为所欲为?

    为了满足开发者对权限和认证的要求,LeanMessage 还设计了操作签名的机制。我们可以在 LeanCloud 应用控制台、设置、应用选项中强制启用签名(强烈推荐这样做)。启用后,所有的 Session open 和 watch 行为都需要验证签名,这样开发者就可以对用户登录以及他可以关注哪些人,进而可以给哪些人发消息进行充分的控制。

    签名采用 Hmac-sha1 算法,输出字节流的十六进制字符串 (hex dump),签名的消息格式如下:

    app_id:peer_id:watch_peer_ids:timestamp:nonce
    

    其中:

    • app_id 是你的应用 ID
    • peer_id 是打开此 Session 的 Peer ID
    • watch_peer_ids 是 open 或 watch 请求中关注的 peer ids,升序排序后以: 分隔
    • timestamp 是当前的 UTC 时间距离 unix epoch 的秒数
    • nonce 为随机字符串

    在群组操作中,LeanMessage 对加群、邀请和踢出群这三个动作也允许加入签名,它的签名格式是:

    app_id:peer_id:group_id:group_peer_ids:timestamp:nonce:action
    

    其中:

    • app_id, peer_id, timestamp 和 nonce 同上
    • group_id 是此次行为关联的群组 ID,对于创建群尚没有 id 的情况,group_id 是空字符串
    • group_peer_ids 是: 分隔的升序排序的 peer id,即邀请和踢出的 peer_id;对加入群的情况,这里是空字符串
    • action 是此次行为的动作,三种行为分别对应常量 join, invite 和 kick

    签名的 key 是应用的 master key。开发者可以实现自己的 SignatureFactory,调用远程的服务器的签名接口获得签名。如果没有自己的服务器,可以直接在 LeanCloud 的云代码上通过 Web Hosting 动态接口实现自己的签名接口。在移动应用中直接做签名是非常危险的,它可能导致你的 master key 泄漏。

    LeanCloud 的 appKey 分类
    在 LeanCloud 平台上申请了应用之后,LeanCloud 会分配给我们两个 key:一个 appKey,一个 master Key。其中 appKey 可以执行一些普通的操作,并且受到 LeanCloud 平台安全设置的限制,类似于操作系统中的普通 User 账号,所以可以直接用在客户端;master Key 则拥有所有权限,类似于操作系统中的 Root/Administrator 账号,所以请妥善保管。

    好,有了签名机制之后,我们究竟该如何使用呢?我们只需要实现自己的 SignatureFactory,然后在开启 session 的时候,把这个 signatureFactory 传进去即可。示例代码如清单 13 所示:

    // Signature 定义如下,由 LeanMessage 提供
    public class Signature {
        private String signature;
        private long timestamp;
        private String nonce;
        private List<String> signedPeerIds;
    
        // getter / setter for properties
        ......
    }
    // customise signature factory,由开发者实现
    public MySignatureFactory implements SignatureFactory {
        @override
        public Signature createSignature(String selfId, List<String> watchIds) {
            // call remote server for correct signature.
        }
       @override
        public Signature createGroupSignature(String groupId, String selfId, List<String> targetPeerIds,
      String action) {
            // call remote server for correct group signature.
        }
    }
    
    // open session with signature factory.
    SignatureFactory signatureFacatory = new MySignatureFactory();
    SessionManager sm = SessionManager.getInstance(selfId);
    sm.setSignatureFactory(signatureFactory);
    sm.open(selfId);
    

    设定了 SignatureFactory 之后,对于需要鉴权的操作,LeanMessage SDK 与服务器端通讯的时候都会带上应用自己生成的 Signature 信息,这样 LeanMessage 服务器端就会使用 app 的 masterKey 来验证信息的有效性,保证聊天渠道的安全。

    结束语

    LeanMessage 是一个非常稳定可靠的聊天服务平台,提供的功能也足以满足我们应用开发者的需求。这里我通过介绍 LeanMessage API 来实现应用内的单聊、群聊、富媒体消息等基本功能,但是 LeanMessage 还支持更多高阶功能,譬如 Super Peer、敏感词过滤、消息实时监听等等,有兴趣的朋友可以继续探索。

    相关文章

      网友评论

      本文标题:让你的 Android 应用也能聊天

      本文链接:https://www.haomeiwen.com/subject/yzpytttx.html