讓你的 Android 應用也能聊天

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、敏感詞過濾、消息實時監聽等等,有興趣的朋友可以繼續探索。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容