簡介
自去年 LeanCloud 釋出實時通訊(IM)服務之後,基於使用者反饋和工程師對需求的消化和對業務的提煉,上週正式釋出了「實時通訊 2.0 」。設計理念依然是「靈活、解耦、可組合、可定製」,具體可以參考《實時通訊開發指南》,瞭解 LeanCloud 實時通訊的基本概念和模型。
下載和安裝
可以到 LeanCloud 官方下載點下載 LeanCloud IM SDK v2 版本。將下載到的 jar 包加入工程即可。
Demo 及示例程式碼
如果您覺得一點點閱讀文件較慢,可以直接看我們的「Demo 程式碼」,並且下載自己執行一下試試看。
一對一的文字聊天
我們先從最簡單的環節入手,看看怎麼用 LeanCloud IM SDK 實現一對一文字聊天。
初始化
和 LeanCloud 其他服務一樣,實時聊天服務的初始化也是在 Application 的 onCreate
方法中進行的:
public class MyApplication extends Application{
public void onCreate(){
...
AVOSCloud.initialize(this,"{{appId}}","{{appKey}}");
...
}
}
並且在AndroidManifest.xml中間宣告:
<manifest>
...
<application
android:name=".MyApplication"
....>
...
<service android:name="com.avos.avoscloud.PushService" />
<receiver android:name="com.avos.avoscloud.AVBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.USER_PRESENT" />
</intent-filter>
</receiver>
...
</application>
</manifest>
接下來我們開始一步一步接入聊天服務。
登入
使用者在開始聊天之前,需要先登入 LeanCloud 雲端。這個登入並不需要使用者名稱、密碼認證,只是與 LeanCloud 雲端建立一個長連線,所以只需要傳入一個可唯一標識當前使用者的 clientId
即可。
在本 SDK 中,我們會為每一個終端使用者開啟一個 AVIMClient
例項,獲取這一例項的方法位於 com.avos.avoscloud.im.v2.AVIMClient
類中,其宣告如下:
public static AVIMClient getInstance(String clientId)
SDK 內部會為每一個 clientId 建立唯一的 AVIMClient
例項,同一個 clientId 多次呼叫該方法,得到的都是同一個結果。所以如果要支援同一個客戶端內多賬號登入,只要使用不同的 clientId 多次呼叫該方法即可。LeanCloud IM SDK 本身是支援多賬戶同時登入的。
得到 AVIMClient
例項之後,我們需要登入 LeanCloud 雲端。這是通過呼叫 AVIMClient 的 open
方法實現的,其宣告如下:
public void open(final AVIMClientCallback callback)
open
函式返回的時候,會把 AVIMClient
例項和 AVException
資訊(如果發生錯誤的話)傳給 AVIMClientCallback
回撥介面。
好了,我們現在來實際看一下這個過程如何實現。假定聊天發起方名叫 Tom,為直觀起見,我們使用使用者名稱來作為 clientId
登入聊天系統(LeanCloud 雲端只要求 clientId
在應用內唯一即可,具體用什麼資料由應用層決定),程式碼如下:
AVIMClient imClient = AVIMClient.getInstance("Tom");
imClient.open(new IMClientCallback(){
@Override
public void done(AVIMClient client, AVException e) {
if (null != e) {
// 出錯了,可能是網路問題無法連線 LeanCloud 雲端,請檢查網路之後重試。
// 此時聊天服務不可用。
e.printStackTrace();
} else {
// 成功登入,可以開始進行聊天了(假設為 MainActivity)。
Intent intent = new Intent(currentActivity, MainActivity.class);
currentActivity.startActivity(intent);
};
}
});
建立對話
在本版本 IM SDK 中,開始聊天之前,需要先建立或者加入一個「對話」(AVIMConversation),所有訊息都是由某個 client 發往一個「對話」,「對話」內的所有成員會實時收到新訊息。
對話支援如下預設屬性:
- conversationId,字串,對話 id,只讀,對話建立之後由 LeanCloud 雲端賦予一個全域性唯一的 id。
- creator,字串,對話建立者的 clientId,只讀,標識對話建立者資訊
- members,陣列,對話參與者,這裡記錄了所有的參與者
- name,字串,對話的名字,可選,可用來對於群組命名
- attributes,Map/Dict,自定義屬性,可選,供開發者自己擴充套件用。
- transient,布林值,表示對話是否為暫態對話(關於暫態對話,後面會詳細解釋)
我們可以通過 AVIMClient
來建立一個對話,其函式宣告為:
//指定成員、自定義屬性,建立對話
public void createConversation(final List<String> conversationMembers, final Map<String, Object> attributes, final AVIMConversationCreatedCallback callback);
//指定成員、名字、自定義屬性,建立對話
public void createConversation(final List<String> conversationMembers, String name, final Map<String, Object> attributes, final AVIMConversationCreatedCallback callback);
//指定成員、名字、自定義屬性和對話標誌,建立對話
public void createConversation(final List<String> conversationMembers, String name, final Map<String, Object> attributes, final boolean isTransient, final AVIMConversationCreatedCallback callback);
各引數的含義如下:
- conversationMembers – 對話初始成員列表,可以為空
- attributes - 自定義屬性,可選
- name - 對話的名字,可選
- isTransient - 是否為暫態對話標誌,預設為 false
- callback - 結果回撥介面,在建立結束之後呼叫,通知開發者成功與否
接下來我們看看實際如何建立一個對話。假定我們要跟「Bob」這個使用者進行聊天,我們先建立一個對話,程式碼如下:
List<String> clientIds = new ArrayList<String>();
clientIds.add("Tom");
clientIds.add("Bob");
// 我們給對話增加一個自定義屬性 type,表示單聊還是群聊
// 常量定義:
// int ConversationType_OneOne = 0; // 兩個人之間的單聊
// int ConversationType_Group = 1; // 多人之間的群聊
Map<String, Object> attr = new HashMap<String, Object>();
attr.put("type", ConversationType_OneOne);
imClient.createConversation(clientIds, attr, new AVIMConversationCreatedCallback() {
@Override
public void done(AVIMConversation conversation, AVException e) {
if (null != conversation) {
// 成功了,這時候可以顯示對話的 Activity 頁面(假定為 ChatActivity)了。
Intent intent = new Intent(this, ChatActivity.class);
Intent.putExtra(“conversation”, conversation);
startActivity(intent);
}
}
});
建立的「對話」在控制檯怎麼檢視
如你所見,我們建立一個對話的時候,指定了成員(Tom 和 Bob)和一個額外的屬性({type: 0})。這些資料儲存到雲端後,你在 控制檯 -> 儲存 -> 資料 裡面會看到,_Conversation 表中增加了一條記錄,新記錄的
m
屬性值為["Tom", "Bob"]
,attr
屬性值為{"type":0}
。如你所料,m
屬性就是對應著成員列表,attr
屬性就是使用者增加的額外屬性值(以物件的形式儲存)。
傳送訊息
通過 AVIMConversation
的 sendMessage
族方法,可以將訊息發往目標對話。方法宣告如下:
// 直接傳送一條訊息,在大多數情況下,你該呼叫這個方法
public void sendMessage(AVIMMessage message, final AVIMConversationCallback callback);
// 傳送訊息時,指定特殊的訊息選項,用來傳送特別的訊息
public void sendMessage(final AVIMMessage message, final int messageFlag, final AVIMConversationCallback callback);
各引數的含義如下:
- message – 訊息例項
- messageFlag - 訊息選項(普通訊息,暫態訊息,待回執訊息),可選,預設值是普通訊息
- callback - 結果回撥介面,在傳送結束之後呼叫,通知開發者成功與否
接下來我們試著傳送一條普通文字訊息。示例程式碼如下:
AVIMMessage message = new AVIMMessage();
message.setContent("hello");
conversation.sendMessage(message, new AVIMConversationCallback() {
@Override
public void done(AVException e) {
if (null != e) {
// 出錯了。。。
e.printStackTrace();
} else {
Logger.d("傳送成功,msgId=" + message.getMessageId());
}
}
});
好了,這樣一條訊息就傳送過去了。但是問題來了,對於「Bob」而言,他怎麼才能收到別人發給他的訊息呢?
訊息接收
在 Bob 這一端,要能接收到訊息,需要如下幾步:
1,進行初始化;
2,準備好自己的 AVIMMessageHandler
,響應新訊息到達通知。
在本版本 IM SDK 中,我們設計的框架是將訊息型別與具體的 handler 類繫結起來,這樣開發者可以為不同型別的訊息設定不同的 handler,處理起來更加靈活自由。這一繫結過程是通過 com.avos.avoscloud.im.v2.AVIMMessageManager
類的 void registerMessageHandler(Class<? extends AVIMMessage> clazz, MessageHandler<?> handler)
函式實現的。AVIMMessageManager
類中還有一個方法 void registerDefaultMessageHandler(AVIMMessageHandler handler)
則用來指定全域性預設的訊息處理 handler。
AVIMMessageHandler
的主要函式如下:
public void onMessage(AVIMMessage message, AVIMConversation conversation, AVIMClient client);
對於 Tom 發過來的訊息,要接收並顯示出來,我們只需實現 onMessage
方法即可,示例程式碼如下:
class CustomMessageHandler extends AVIMMessageHandler {
@Override
public void onMessage(AVIMMessage message, AVIMConversation conversation, AVIMClient client) {
// 新訊息到來了。在這裡增加你自己的處理程式碼。
String msgContent = message.getContent();
Logger.d(conversation.getConversationid() + " 收到一條新訊息:" + msgContent);
}
}
3,進行登入,程式碼也與傳送端一樣。
Bob 這邊要接收到 Tom 發過來的訊息,其完整流程如下:
// 自定義訊息響應類
class CustomMessageHandler extends AVIMMessageHandler {
@Override
public void onMessage(AVIMMessage message, AVIMConversation conversation, AVIMClient client) {
// 新訊息到來了。在這裡增加你自己的處理程式碼。
String msgContent = message.getContent();
Logger.d(conversation.getConversationid() + " 收到一條新訊息:" + msgContent);
}
}
// application 的初始化部分
public void onCreate(){
...
AVOSCloud.initialize(this,"{{appId}}","{{appKey}}");
AVIMMessageManager.registerDefaultMessageHandler(new CustomMessageHandler());
...
}
// 使用者登入部分
AVIMClient imClient = AVIMClient.getInstance("Bob");
imClient.open(new IMClientCallback(){
@Override
public void done(AVIMClient client, AVException e) {
if (null != e) {
// 出錯了,可能是網路問題無法連線 LeanCloud 雲端,請檢查網路之後重試。
// 此時聊天服務不可用。
e.printStackTrace();
} else {
// 成功登入,可以開始進行聊天了。
};
}
});
注意!
AVIMMessageManager.registerDefaultMessageHandler()
一定要在AVIMClient.open()
之前呼叫,否則可能導致伺服器發回來的部分訊息丟失。
退出登入
在 app 退出的時候,或者切換使用者的時候,我們需要斷開與 LeanCloud 實時通訊服務的長連線,這時候需要呼叫 AVIMClient.close(final AVIMClientCallback callback)
函式。一般情況下,這個函式都會關閉連線並立刻返回,這時候 Leancloud 實時通訊服務端就會認為當前使用者已經下線。
幾個主要的回撥介面
從上面的例子中可以看到,要接收到別人給你傳送的訊息,需要過載 AVIMMessageHandler 類。從 v2 版開始,LeanCloud IM SDK 大量採用回撥來反饋操作結果,但是對於一些被動的訊息通知,則還是採用介面來實現的,包括:
- 當前網路出現變化
- 對話中有新的訊息
- 對話中有新成員加入
- 對話中有成員離開
- 被邀請加入某對話
- 被踢出對話
LeanCloud IM SDK 內部使用了三種介面來響應這些事件。
網路事件響應介面
主要用來處理網路變化事件,介面定義在 AVIMClientEventHandler
,主要函式為:
-
void onConnectionPaused(AVIMClient client)
指網路連線斷開事件發生,此時聊天服務不可用。 -
void onConnectionResume(AVIMClient client)
指網路連線恢復正常,此時聊天服務變得可用。
在網路中斷的情況下,所有的訊息收發和對話操作都會出現問題。
通過 AVIMClient.setClientEventHandler(AVIMClientEventHandler handler)
可以設定全域性的 ClientEventHandler。
對話成員變化響應介面
主要用來處理對話中成員變化的事件,介面定義在 AVIMConversationEventHandler
,主要函式為:
-
onMemberLeft(AVIMClient client, AVIMConversation conversation, List<String> members, String kickedBy)
對話中有成員離開時所有剩餘成員都會收到這一通知。引數意義說明如下:- client 指已經登入的 client,因為支援一個客戶端多賬戶登入,每個賬戶會對應一個 client
- conversation 指目標對話;
- members 指離開的成員列表;
- kickedBy 表示踢人者的 id;
-
onMemberJoined(AVIMClient client, AVIMConversation conversation, List<String> members, String invitedBy)
對話中有新成員加入時所有成員都會收到這一通知。引數意義說明如下:- client 指已經登入的 client,因為支援一個客戶端多賬戶登入,每個賬戶會對應一個 client
- conversation 指目標對話;
- members 指加入的新成員列表;
- invitedBy 表示邀請者的 id
-
onKicked(AVIMClient client, AVIMConversation conversation, String kickedBy)
當前使用者被踢出對話的通知,引數意義說明如下:- client 指已經登入的 client,因為支援一個客戶端多賬戶登入,每個賬戶會對應一個 client
- conversation 指目標對話;
- kickedBy 表示踢人者的 id
-
onInvited(AVIMClient client, AVIMConversation conversation, String operator)
當前使用者被邀請加入對話的通知。引數意義說明如下:- client 指已經登入的 client,因為支援一個客戶端多賬戶登入,每個賬戶會對應一個 client
- conversation 指目標對話;
- operator 表示邀請者的 id
通過 AVIMMessageManager.setConversationEventHandler(AVIMConversationEventHandler handler)
可以設定全域性的 ConversationEventHandler。
訊息響應介面
主要用來處理新訊息到達事件,介面定義在 MessageHandler
,AVIMMessageHandler
是一個空的實現類,我們應該通過過載 AVIMMessageHandler 的相關方法來完成訊息處理。主要的方法有:
-
onMessage(AVIMMessage message, AVIMConversation conversation, AVIMClient client)
指接收到新的訊息。引數意義說明如下:- client 指已經登入的 client,因為支援一個客戶端多賬戶登入,每個賬戶會對應一個 client
- conversation 指目標對話;
- message 表示訊息例項
-
onMessageReceipt(AVIMMessage message, AVIMConversation conversation, AVIMClient client)
自己傳送的訊息被對方接收時會收到此通知,各引數意義同上。
通過 AVIMMessageManager.registerDefaultMessageHandler(handler)
可以設定全域性的 MessageHandler。
我們實現這三類介面,就可以處理所有的通知訊息了。示例程式碼如下:
// 處理網路狀態變化事件
class CustomNetworkHandler extends AVIMClientEventHandler {
@Override
public void onConnectionPaused(AVIMClient client) {
// 請按自己需求改寫
Logger.d("connect paused");
}
@Override
public void onConnectionResume(AVIMClient client) {
// 請按自己需求改寫
Logger.d("connect resume");
}
}
// 處理對話成員變化事件
class CustomConversationHandler extends AVIMConversationEventHandler {
public private Context gContext = null;
private void toast(String str) {
Toast.makeText(gContext, str, Toast.LENGTH_SHORT).show();
}
private void toast(Context context, String str) {
Toast.makeText(context, str, Toast.LENGTH_SHORT).show();
}
@Override
public void onMemberLeft(AVIMClient client, AVIMConversation conversation, List<String> members, String kickedBy) {
// 請按自己需求改寫
toast(MsgUtils.nameByUserIds(members) + " left, kicked by " + MsgUtils.nameByUserId(kickedBy));
//注:MsgUtils 是一個輔助類,nameByUserIds 用來將 userId 轉換成使用者名稱
}
@Override
public void onMemberJoined(AVIMClient client, AVIMConversation conversation, List<String> members, String invitedBy) {
// 請按自己需求改寫
toast(MsgUtils.nameByUserIds(members) + " joined , invited by " + MsgUtils.nameByUserId(invitedBy));
//注:MsgUtils 是一個輔助類,nameByUserIds 用來將 userId 轉換成使用者名稱
}
@Override
public void onKicked(AVIMClient client, AVIMConversation conversation, String kickedBy) {
// 請按自己需求改寫
toast("you are kicked by " + MsgUtils.nameByUserId(kickedBy));
}
@Override
public void onInvited(AVIMClient client, AVIMConversation conversation, String operator) {
// 請按自己需求改寫
toast("you are invited by " + MsgUtils.nameByUserId(operator));
}
};
// 處理新訊息到達事件
class CustomMsgHandler extends AVIMMessageHandler {
@Override
public void onMessage(AVIMMessage message, AVIMConversation conversation, AVIMClient client) {
// 請按自己需求改寫
String msgContent = message.getContent();
Logger.d(conversation.getConversationid() + " 收到一條新訊息:" + msgContent);
}
@Override
public void onMessageReceipt(AVIMMessage message, AVIMConversation conversation, AVIMClient client) {
// 請按自己需求改寫
Logger.d("發往對話 " + conversation.getConversationid() + " 的訊息 "+ message.getMessageId() +" 已被接收");
}
}
// 設定事件響應介面
AVIMClient.setClientEventHandler(new CustomNetworkHandler());
AVIMMessageManager.setConversationEventHandler(new CustomConversationHandler());
AVIMMessageManager.registerDefaultMessageHandler(new CustomMsgHandler());
支援富媒體的聊天訊息
上面的程式碼演示瞭如何傳送簡單文字資訊,但是現在的互動方式已經越來越多樣化,影像、語音、視訊已是非常普遍的訊息型別。v2 版的 IM SDK 已經可以很好地支援這些富媒體訊息,具體說明如下:
基類:AVIMTypedMessage
我們預設支援文字、影像、語音、視訊、檔案、地理位置等富媒體訊息,所有這些訊息型別都有一個共同的基類:AVIMTypedMessage,其宣告為
public abstract class AVIMTypedMessage extends AVIMMessage {
public AVIMTypedMessage();
public int getMessageType();
@Override
public final String getContent();
@Override
public final void setContent(String content);
}
這裡我們為每一種富媒體訊息定義了一個訊息型別,LeanCloud SDK 自身使用的型別是負數(如下面列表所示),所有正數留給開發者自定義擴充套件型別使用,0 作為「沒有型別」被保留起來。
訊息 | 對應的訊息型別 |
---|---|
文字訊息 | -1 |
影像訊息 | -2 |
音訊訊息 | -3 |
視訊訊息 | -4 |
位置訊息 | -5 |
檔案訊息 | -6 |
文字訊息(AVIMTextMessage)
AVIMTypedMessage 子類,表示一般的文字訊息,其宣告為
public class AVIMTextMessage extends AVIMTypedMessage {
public String getText();
public void setText(String text);
public Map<String, Object> getAttrs();
public void setAttrs(Map<String, Object> attr);
}
可以看到,對於文字訊息,主要的屬性有 text
和 attrs
兩個,通過簡單的 getter/setter 就可以訪問到。要傳送文字訊息,示例程式碼為:
AVIMTextMessage message = new AVIMTextMessage();
message.setText("hello");
conversation.sendMessage(message, new AVIMConversationCallback() {
@Override
public void done(AVException e) {
if (null != e) {
// 出錯了。。。
e.printStackTrace();
} else {
Logger.d("message sent.");
}
}
});
檔案訊息(AVIMFileMessage)
AVIMTypedMessage 子類,用來傳送帶附件的訊息,開發者可以用它來傳送「離線檔案」。對於此類訊息,LeanCloud IM SDK 內部會先把檔案上傳到 LeanCloud 檔案儲存伺服器(自帶 CDN 功能),然後把檔案後設資料(url,檔案大小等等)放在訊息包內傳送到 LeanCloud 實時通訊服務端。其建構函式宣告為:
// 傳入本地檔案路徑,構造訊息物件
public AVIMFileessage(String localPath) throws FileNotFoundException, IOException;
// 傳入本地檔案,構造訊息物件
public AVIMFileMessage(File localFile) throws FileNotFoundException, IOException;
// 傳入 AVFile 例項,構造訊息物件
public AVIMFileMessage(AVFile file);
與文字訊息類似,檔案訊息也支援附帶文字和其他自定義屬性,可以通過如下方法新增 / 獲取更多資訊:
- String getText() / void setText(String text)
- Map<String, Object> getAttrs() / void setAttrs(Map<String, Object> attr);
傳送檔案訊息的示例程式碼為:
String localZipfilePath;
try {
AVIMFileMessage message = new AVIMFileMessage(localZipfilePath);
message.setText("這是你要的文件");
conversation.sendMessage(message, new AVIMConversationCallback() {
@Override
public void done(AVException e) {
if (null != e) {
// 出錯了。。。
e.printStackTrace();
} else {
Logger.d("message sent");
}
}
});
} catch (Exception ex) {
}
接收到這樣訊息之後,開發者可以通過以下方法,獲取到檔案後設資料(size 等)和一個包含二進位制資料的 AVFile 物件:
-
AVFile getAVFile()
方法會返回一個二進位制檔案的 AVFile 例項,之後可以通過 AVFile 來完成資料下載或者其他操作,具體可以參見 AVFile 說明 -
String getFileUrl()
方法會返回二進位制檔案的 url -
long getSize()
方法會返回二進位制檔案的實際大小(單位:byte) -
Map<String, Object> getFileMetaData()
可以獲取二進位制檔案的其他後設資料資訊。 -
String getText()
方法會返回隨檔案一起傳送的文字資訊。
影像訊息(AVIMImageMessage)
AVIMFileMessage 子類,專門用來傳送影像和附帶文字的混合訊息,其建構函式宣告為:
// 傳入本地檔案路徑,構造訊息物件
public AVIMImageMessage(String localPath) throws FileNotFoundException, IOException;
// 傳入本地檔案,構造訊息物件
public AVIMImageMessage(File localFile) throws FileNotFoundException, IOException;
// 傳入 AVFile 例項,構造訊息物件
public AVIMImageMessage(AVFile file);
傳送影像訊息的示例程式碼為:
String localImagePath;
try {
AVIMImageMessage message = new AVIMImageMessage(localImagePath);
message.setText("你說我好看不?");
conversation.sendMessage(message, new AVIMConversationCallback() {
@Override
public void done(AVException e) {
if (null != e) {
// 出錯了。。。
e.printStackTrace();
} else {
Logger.d("message sent");
}
}
});
} catch (Exception ex) {
}
接收到這樣訊息之後,開發者可以通過如下方法,獲取到若干影像後設資料(width,height,影像 size)和一個包含影像資料的 AVFile 物件:
-
int getWidth()
方法會返回影像的寬度(單位:pixel) -
int getHeight()
方法會返回影像的高度(單位:pixel) -
AVFile getAVFile()
(繼承自 AVIMFileMessage)方法會返回一個影像檔案的 AVFile 例項 -
String getFileUrl()
(繼承自 AVIMFileMessage)方法會返回影像檔案的 url -
long getSize()
(繼承自 AVIMFileMessage)方法會返回影像檔案的實際大小(單位:byte) -
String getText()
(繼承自 AVIMFileMessage)方法會返回隨影像一起傳送的文字資訊。 -
Map<String, Object> getFileMetaData()
(繼承自 AVIMFileMessage)可以獲取影像的其他後設資料資訊。
音訊訊息(AVIMAudioMessage)
AVIMFileMessage 子類,專門用來傳送語音和附帶文字的混合訊息,其建構函式宣告為:
// 傳入本地檔案路徑,構造訊息物件
public AVIMAudioMessage(String localPath) throws FileNotFoundException, IOException;
// 傳入本地檔案,構造訊息物件
public AVIMAudioMessage(File localFile) throws FileNotFoundException, IOException;
// 傳入 AVFile 例項,構造訊息物件
public AVIMAudioMessage(AVFile file);
傳送音訊訊息的示例程式碼為:
String localAudioPath;
try {
AVIMAudioMessage message = new AVIMAudioMessage(localAudioPath);
message.setText("聽聽我唱的小蘋果:)");
conversation.sendMessage(message, new AVIMConversationCallback() {
@Override
public void done(AVException e) {
if (null != e) {
// 出錯了。。。
e.printStackTrace();
} else {
Logger.d("message sent");
}
}
});
} catch (Exception ex) {
}
接收到這樣訊息之後,開發者可以通過如下方法,獲取到若干音訊後設資料(時長 duration、音訊 size)和一個包含音訊資料的 AVFile 物件:
-
double getDuration()
方法會返回音訊的長度(單位:秒) -
AVFile getAVFile()
(繼承自 AVIMFileMessage)方法會返回一個音訊檔案的 AVFile 例項 -
String getFileUrl()
(繼承自 AVIMFileMessage)方法會返回音訊檔案的 url -
long getSize()
(繼承自 AVIMFileMessage)方法會返回音訊檔案的實際大小(單位:byte) -
String getText()
(繼承自 AVIMFileMessage)方法會返回隨音訊一起傳送的文字資訊。 -
Map<String, Object> getFileMetaData()
(繼承自 AVIMFileMessage)可以獲取音訊的其他後設資料資訊。
視訊訊息(AVIMVideoMessage)
AVIMFileMessage 子類,專門用來傳送視訊和附帶文字的混合訊息,其建構函式宣告為:
// 傳入本地檔案路徑,構造訊息物件
public AVIMVideoMessage(String localPath) throws FileNotFoundException, IOException;
// 傳入本地檔案,構造訊息物件
public AVIMVideoMessage(File localFile) throws FileNotFoundException, IOException;
// 傳入 AVFile 檔案,構造訊息物件
public AVIMVideoMessage(AVFile file);
傳送視訊訊息的示例程式碼為:
String localVideoPath;
try {
AVIMVideoMessage message = new AVIMVideoMessage(localVideoPath);
message.setText("敢不敢跟我比一比");
conversation.sendMessage(message, new AVIMConversationCallback() {
@Override
public void done(AVException e) {
if (null != e) {
// 出錯了。。。
e.printStackTrace();
} else {
Logger.d("message sent");
}
}
});
} catch (Exception ex) {
}
接收到這樣訊息之後,開發者可以可以通過如下方法,獲取到若干視訊後設資料(時長 duration、視訊 size)和一個包含視訊資料的 AVFile 物件:
-
double getDuration()
方法會返回視訊的長度(單位:秒) -
AVFile getAVFile()
(繼承自 AVIMFileMessage)方法會返回一個視訊檔案的 AVFile 例項 -
String getFileUrl()
(繼承自 AVIMFileMessage)方法會返回視訊檔案的 url -
long getSize()
(繼承自 AVIMFileMessage)方法會返回視訊檔案的實際大小(單位:byte) -
String getText()
(繼承自 AVIMFileMessage)方法會返回隨視訊一起傳送的文字資訊。 -
Map<String, Object> getFileMetaData()
(繼承自 AVIMFileMessage)可以獲取視訊的其他後設資料資訊。
地理位置訊息(AVIMLocationMessage)
AVIMTypedMessage 子類,支援傳送地理位置資訊和附帶文字的混合訊息,其宣告為:
public class AVIMLocationMessage extends AVIMTypedMessage {
public String getText();
public void setText(String text);
public Map<String, Object> getAttrs();
public void setAttrs(Map<String, Object> attr);
public AVGeoPoint getLocation();
public void setLocation(AVGeoPoint location);
}
與文字訊息類似,地理位置訊息只是增加了一個 AVGeoPoint 的 Location 屬性。要傳送位置訊息的示例程式碼為:
AVIMLocationMessage message = new AVIMLocationMessage();
message.setText("快點過來!");
message.setLocation(new AVGeoPoint(15.9, 56.4));
conversation.sendMessage(message, new AVIMConversationCallback() {
@Override
public void done(AVException e) {
if (null != e) {
// 出錯了。。。
e.printStackTrace();
} else {
Logger.d("message sent");
}
}
});
接收到這樣的訊息之後,開發者可以獲取到具體的地理位置資料。
如何接收富媒體訊息
新版 LeanCloud IM SDK 內部封裝了對富媒體訊息的支援,所有富媒體訊息都是從 AVIMTypedMessage 派生出來的。傳送的時候可以直接呼叫 conversation.sendMessage()
函式。在接收端,我們也專門增加了一類回撥介面 AVIMTypedMessageHandler,其定義為:
public class AVIMTypedMessageHandler<T extends AVIMTypedMessage> extends MessageHandler<T> {
@Override
public void onMessage(T message, AVIMConversation conversation, AVIMClient client);
@Override
public void onMessageReceipt(T message, AVIMConversation conversation, AVIMClient client);
}
開發者可以編寫自己的訊息處理 handler,然後呼叫 AVIMMessageManager.registerMessageHandler(Class<? extends AVIMMessage> clazz, MessageHandler<?> handler)
函式來註冊目標 handler。
接收端對於富媒體訊息的通知處理的示例程式碼如下:
class MsgHandler extends AVIMTypedMessageHandler<AVIMTypedMessage> {
@Override
public void onMessage(AVIMTypedMessage message, AVIMConversation conversation, AVIMClient client) {
// 請按自己需求改寫
switch(message.getMessageType()) {
case AVIMReservedMessageType.TextMessageType:
AVIMTextMessage textMsg = (AVIMTextMessage)message;
Logger.d("收到文字訊息:" + textMsg.getText() + ", msgId:" + textMsg.getMessageId());
break;
case AVIMReservedMessageType.FileMessageType:
AVIMFileMessage fileMsg = (AVIMFileMessage)message;
Logger.id("收到檔案訊息。msgId=" + fileMsg.getMessageId() + ", url=" + fileMsg.getFileUrl() + ", size=" + fileMsg.getSize());
break;
case AVIMReservedMessageType.ImageMessageType:
AVIMImageMessage imageMsg = (AVIMImageMessage)message;
Logger.id("收到圖片訊息。msgId=" + imageMsg.getMessageId() + ", url=" + imageMsg.getFileUrl() + ", width=" + imageMsg.getWidth() + ", height=" + imageMsg.getHeight());
break;
case AVIMReservedMessageType.AudioMessageType:
AVIMAudioMessage audioMsg = (AVIMAudioMessage)message;
Logger.id("收到音訊訊息。msgId=" + audioMsg.getMessageId() + ", url=" + audioMsg.getFileUrl() + ", duration=" + audioMsg.getDuration());
break;
case AVIMReservedMessageType.VideoMessageType:
AVIMVideoMessage videoMsg = (AVIMAudioMessage)message;
Logger.id("收到視訊訊息。msgId=" + videoMsg.getMessageId() + ", url=" + videoMsg.getFileUrl() + ", duration=" + videoMsg.getDuration());
break;
case AVIMReservedMessageType.LocationMessageType:
AVIMLocationMessage locMsg = (AVIMLocationMessage)message;
Logger.id("收到位置訊息。msgId=" + locMsg.getMessageId() + ", latitude=" + locMsg.getLocation().getLatitude() + ", longitude=" + locMsg.getLocation().getLongitude());
break;
}
}
@Override
public void onMessageReceipt(AVIMTypedMessage message, AVIMConversation conversation, AVIMClient client) {
// 請加入你自己需要的邏輯...
}
}
MsgHandler msgHandler = new MsgHandler();
AVIMMessageManager.registerMessageHandler(AVIMTypedMessage.class, msgHandler);
LeanCloud IM SDK 內部訊息分發的邏輯是這樣的:
- 對於收到的任一新訊息,SDK 內部都會先解析訊息的型別,根據型別找到開發者為這一型別註冊的處理 handler,然後逐一呼叫這些 handler 的 onMessage 函式。
- 如果沒有找到專門處理這一型別訊息的 handler,就會轉交給 defaultHandler 處理。
這樣一來,在開發者為 TypedMessage(及其子類) 指定了專門的 handler,也指定了全域性的 defaultHandler 了的時候,如果傳送端傳送的是通用的 AVIMMessage 訊息,那麼接受端就是 AVIMMessageManager.registerDefaultMessageHandler()中指定的 handler 被呼叫;如果傳送的是 AVIMTypedMessage(及其子類)的訊息,那麼接受端就是 AVIMMessageManager.registerMessageHandler()中指定的 handler 被呼叫。
如何擴充套件自己的富媒體訊息
繼承於 AVIMTypedMessage,開發者也可以擴充套件自己的富媒體訊息。其要求和步驟是:
- 實現新的訊息型別,繼承自 AVIMTypedMessage。這裡需要注意兩點:
- 在 class 上增加一個 @AVIMMessageType(type=123) 的 Annotation,具體訊息型別的值(這裡是
123
)由開發者自己決定(LeanCloud 內建的訊息型別使用負數,所有正數都預留給開發者擴充套件使用)。 - 在訊息內部屬性上要增加 @AVIMMessageField(name=””) 的 Annotation,name 為可選欄位在宣告欄位屬性,同時自定義的欄位要有對應的 getter/setter 方法。
- 在 class 上增加一個 @AVIMMessageType(type=123) 的 Annotation,具體訊息型別的值(這裡是
- 呼叫
AVIMMessageManager.registerAVIMMessageType(Class<? extends AVIMTypedMessage> messageType)
函式進行型別註冊 - 呼叫
AVIMMessageManager.registerMessageHandler(Class<? extends AVIMMessage> clazz, MessageHandler<?> handler)
函式進行訊息處理 handler 註冊。
AVIMTextMessage 的原始碼如下,可供參考:
@AVIMMessageType(type = -1)
public class AVIMTextMessage extends AVIMTypedMessage {
@AVIMMessageField(name = "_lctext")
String text;
@AVIMMessageField(name = "_lcattrs")
Map<String, Object> attrs;
public String getText() {
return this.text;
}
public void setText(String text) {
this.text = text;
}
public Map<String, Object> getAttrs() {
return this.attrs;
}
public void setAttrs(Map<String, Object> attr) {
this.attrs = attr;
}
}
群組聊天
與前面的單聊類似,群組聊天也需要先建立一個對話(AVIMConversation),然後傳送、接收新的訊息。
建立群組
和單聊類似,建立一個多人聊天的群組也是很簡單的。例如:
Map<String, Object> attr = new HashMap<String, Object>();
attr.put("type", ConversationType_Group);
imClient.createConversation(clientIds, attr, new AVIMConversationCreatedCallback() {
@Override
public void done(AVIMConversation conversation, AVException e) {
if (null != conversation) {
// 成功了!
Intent intent = new Intent(currentActivity, ChatActivity.class);
Intent.putExtra(“conversation”, conversation);
currentActivity.startActivity(intent);
}
}
});
成功之後,我們就可以進入聊天介面了。
加入群組
如果是其他人,需要主動加入到並非自己建立的群組裡面,該怎麼做到呢?
AVIMConversation 有一個 join 方法,可以用來主動加入一個群組,其宣告為:
void join(AVIMConversationCallback callback)
這裡引數的含義如下:
- callback – 結果回撥介面,在服務端操作結束之後呼叫,通知開發者成功與否
假定使用者 Jade 希望加入上面的群組,其示例程式碼為:
// 之前是 Jade 登入的程式碼
conversation.join(new AVIMConversationCallback(){
@Override
public void done(AVException e) {
if (null != e) {
// 出錯了:(
} else {
// 成功,此時可以進入聊天介面了。。。
Intent intent = new Intent(currentActivity, ChatActivity.class);
Intent.putExtra(“conversation”, conversation);
currentActivity.startActivity(intent);
}
}
});
往群組傳送訊息
傳送訊息非常簡單,與前面單聊的場景一樣。
我們會注意到,AVIMConversation 還有一個傳送訊息的方法:
public void sendMessage(final AVIMMessage message, final int messageFlag,
final AVIMConversationCallback callback)
而這裡 flag 的定義有如下三種型別:
- 暫態訊息(AVIMConversation.TRANSIENT_MESSAGE_FLAG)。這種訊息不會被自動儲存(以後在歷史訊息中無法找到它),也不支援延遲接收,離線使用者更不會收到推送通知,所以適合用來做控制協議。譬如聊天過程中「某某正在輸入中…」這樣的狀態資訊,就適合通過暫態訊息來傳送。
- 普通訊息(AVIMConversation.NONTRANSIENT_MESSAGE_FLAG)。這種訊息就是我們最常用的訊息型別,在 LeanCloud 雲端會自動儲存起來,支援延遲接收和離線推送,以後在歷史訊息中可以找到它。
- 待回執訊息(AVIMConversation.RECEIPT_MESSAGE_FLAG)。這也是一種普通訊息,只是訊息被對方收到之後 LeanCloud 服務端會傳送一個回執通知給傳送方(這就是 AVIMMessageHandler 中
public void onMessageReceipt(AVIMMessage message, AVIMConversation conversation, AVIMClient client)
函式被呼叫的時機)。
接收群組訊息
接收一個群組的訊息,與接收單聊的訊息也是一樣的。
成員管理
在查詢到聊天室成員之後,可以讓使用者邀請一些自己的朋友加入,作為管理員也可以剔除一些「可怕」的成員。
加入新成員的 API 如下:
void addMembers(final List<String> friendsList, final AVIMConversationCallback callback)
這裡各引數的含義如下:
- friendsList – 邀請加入的新成員 clientId 陣列
- callback – 結果回撥介面,在服務端操作結束之後呼叫,通知開發者成功與否
我們試著在剛才的對話中邀請幾個人:
// 假設需要邀請 Alex,Ben,Chad 三人加入對話
List<String> userIds = new ArrayList<String>();
userIds.add("Alex");
userIds.add("Ben");
userIds.add("Chad");
conversation.addMembers(userIds, new AVIMConversationCallback() {
@Override
public void done(AVException error) {
if (null != error) {
// 加入失敗,報錯.
error.printStackTrace();
} else {
// 發出邀請,此後新成員就可以看到這個對話中的所有訊息了。
Logger.d("invited.");
}
}
});
邀請成功以後,相關方收到通知的時序是這樣的:
操作者(管理員) 被邀請者 其他人
1, 發出請求 addMembers
2, 收到 onInvited 通知
3, 收到 onMemberJoined 通知 收到 onMemberJoined 通知 收到 onMemberJoined 通知
與加人類似,踢人的 API 宣告如下:
void kickMembers(final List<String> friendsList, final AVIMConversationCallback callback)
引數含義同上。我們試著把 Alex 踢出去:
List<String> userIds = new ArrayList<String>();
userIds.add("Alex");
conversation.kickMembers(userIds, new AVIMConversationCallback() {
@Override
public void done(AVException error) {
if (null != error) {
// 失敗,報錯.
error.printStackTrace();
} else {
// 成功。
Logger.d("kicked.");
}
}
});
踢人時,相關方收到通知的時序如下:
操作者(管理員) 被踢者 其他人
1, 發出請求 kickMembers
2, 收到 onKicked 通知
3, 收到 onMemberLeft 通知 收到 onMemberLeft 通知
注意!
如果邀請、踢人操作發生的時候,被邀請者/被踢者當前不線上,那麼通知訊息並不會被離線快取,所以他們再上線的時候將不會收到通知。
退出群組
任何成員,都可以主動退出一個群組。AVIMConversation 有一個 quit 方法,其宣告為:
void quit(AVIMConversationCallback callback)
這裡引數的含義如下:
- callback – 結果回撥介面,在服務端操作結束之後呼叫,通知開發者成功與否
退出群組之後,該群組內發生的任何事件或者訊息,都不會再發到當前使用者,當前使用者也不能往群組內傳送任何訊息。
假設使用者 Jade 又想退出上面的群組了,其示例程式碼為:
// 之前是 Jade 登入的程式碼
conversation.quit(new AVIMConversationCallback(){
@Override
public void done(AVException e) {
if (null != e) {
// 出錯了:(
} else {
// 成功,這下清靜了。。。
currentActivity.finish();
}
}
});
獲取歷史訊息
LeanMessage 會將非暫態訊息自動儲存在雲端,之後開發者可以通過 AVIMConversation 來獲取該對話的所有歷史訊息。獲取歷史訊息的 API 如下:
// 查詢當前對話的最新訊息,預設返回 100 條
void queryMessages(final AVIMHistoryMessageCallback callback);
// 查詢當前對話的最新訊息,返回 limit 指定的條數
void queryMessages(int limit, final AVIMHistoryMessageCallback callback);
// 前向查詢當前對話的歷史訊息,msgId/timestamp 指定訊息的起點,limit 指定需要的結果條數
void queryMessages(String msgId, long timestamp, int limit, final AVIMHistoryMessageCallback callback);
各引數含義如下:
- msgId – 本地已有的最舊一條訊息的 messageId
- timestamp - 本地已有的最舊一條訊息的 timestamp
- limit - 本次查詢希望的結果條數
- AVIMHistoryMessageCallback - 結果回撥介面,在操作結束之後呼叫
通過這一 API 拿到的訊息就是 AVIMMessage 或者 AVIMTypedMessage 例項陣列,開發者可以像之前收到新訊息通知一樣處理。示例程式碼如下:
String oldestMsgId;
long oldestMsgTimestamp;
conversation.queryMessages(oldestMsgId,oldestMsgTimestamp, limit, new AVIMHistoryMessageCallback(){
@Override
public void done(List<AVIMMessage> messages, AVException e) {
if (null != e) {
// 出錯了:(
} else {
// 成功,可以將訊息加入快取,同時更新 UI
}
}
});
注意:
翻頁載入獲取歷史訊息的時候,LeanCloud 雲端是從某條訊息開始,往前查詢開發者指定的 N 條訊息,返回給客戶端。為此,獲取歷史訊息需要傳入三個引數:起始訊息的 msgId,起始訊息的傳送時間戳,需要獲取的訊息條數。
啟用離線訊息推送(僅對 iOS 平臺使用者有效)
不管是單聊還是群聊,當使用者 A 發出訊息後,如果目標對話中有部分使用者當前不線上,LeanCloud 雲端可以提供離線推送的方式來提醒使用者。這一功能預設是關閉的,你可以在 LeanCloud 應用控制檯中開啟它。開啟方法如下:
- 登入 LeanCloud 應用控制檯,選擇正確的應用進入;
- 選擇最頂端的「訊息」服務,依次點選左側選單「實時訊息」->「設定」;
- 在右側「iOS 使用者離線時的推送內容」下填好你要推送出去的訊息內容,儲存;
這樣 iOS 平臺上的使用者就可以收到 Push Notification 了(當然,前提是應用本身申請到了 RemoteNotification 許可權,也將正確的推送證書上傳到了 LeanCloud 控制檯)。
群組訊息免打擾(僅對 iOS 平臺使用者有效)
不管是單聊還是群聊,對於發往普通的 Conversation 的普通訊息,如果接收方當前不線上,LeanCloud 雲端支援通過 Push Notification 的方式進行提醒。一般情況下這都是很好的,但是如果某個群組特別活躍,那離線使用者就會收到過多的推送,會形成不小的干擾。
對此 LeanCloud IM 服務也允許單個使用者來關閉/開啟某個對話的離線推送功能。
搜尋群組
不管是單聊,還是群聊,在 LeanCloud IM SDK 裡面都是對話(Conversation)。我們給對話設定瞭如下幾種屬性:
- conversationId,字串,對話 id,只讀,對話建立之後由 LeanCloud 雲端賦予一個全域性唯一的 id。
- creator,字串,對話建立者 id,只讀,標識對話建立者資訊
- members,陣列,對話參與者,這裡記錄了所有的參與者
- name,字串,對話的名字,optional,可用來對於群組命名
- attributes,Map/Dict,自定義屬性,optional,供開發者自己擴充套件用。
我們提供了專門的類,來搜尋特定的群組:通過 imClient.getQuery()
得到一個 AVIMConversationQuery
例項,然後呼叫 AVIMConversationQuery.whereXXX
系列方法來增加約束條件。
AVIMConversationQuery
的使用方法與 AVQuery 一樣,例如要搜尋當前登入使用者參與的所有群聊對話,其程式碼為
// 搜尋 Tom 參與的所有群組對話
List<String> clients = new ArrayList<String>();
clients.add("Tom");
AVIMConversationQuery conversationQuery = imClient.getQuery();
conversationQuery.containsMember(clients);
// 之前有常量定義:
// const int ConversationType_OneOne = 0;
// const int ConversationType_Group = 1;
conversationQuery.whereEqualTo("attr.type", ConversationType_Group);
conversationQuery.findInBackground(new AVIMConversationQueryCallback(){
@Override
public void done(List<AVIMConversation> conversations, AVException e) {
if (null != e) {
// 出錯了。。。
e.printStackTrace();
} else {
if (null != conversation) {
Logger.d("找到了符合條件的 " + conversations.size() + " 個對話");
} else {
Logger.d("沒有找到符合條件的對話");
}
}
}
});
注意:
這裡 conversationQuery.containsMember()
表示對話的成員中至少包含這些人員,可用來根據部分成員查詢對話;
與此類似的還有一個 conversationQuery.withMembers()
則表示有且僅有這些成員,用來根據所有成員查詢目標對話;conversationQuery.whereXXX()
系列方法可用來限定對話名稱和自定義屬性,這裡要強調的一點是,對於自定義屬性的約束條件,屬性名一定要以 attr
開頭,如上例所示,限定額外的 type
條件的時候需要指定的屬性名是 attr.type
。具體可以參看其標頭檔案。
開放聊天室
開放聊天室(也叫「暫態」對話)可以用於很多地方,譬如彈幕、直播等等。在 LeanCloud IM SDK 中,開放聊天室是一類特殊的群組,它也支援建立、加入/踢出成員等操作,訊息記錄會被儲存並可供獲取;與普通群組不一樣的地方具體體現為:
- 不支援查詢成員列表,你可以通過相關 API 查詢線上人數;
- 不支援離線訊息、離線推送通知等功能;
- 沒有成員加入、離開的通知;
- 一個使用者一次登入只能加入一個開放聊天室,加入新的開放聊天室後會自動離開原來的聊天室;
- 加入後半小時內斷網重連會自動加入原聊天室,超過這個時間則需要重新加入;
建立開放聊天室
和普通的群組類似,建立一個開放聊天室也是很簡單的,只是在 AVIMClient.createConversation(conversationMembers, name, attributes, isTransient, callback)
中我們需要傳入 isTransient=true
選項。例如:
Map<String, Object> attr = new HashMap<String, Object>();
attr.put("type", ConversationType_Group);
imClient.createConversation(clientIds, name, attr, true, new AVIMConversationCreatedCallback() {
@Override
public void done(AVIMConversation conversation, AVException e) {
if (null != conversation) {
// 成功了,進入聊天室
Intent intent = new Intent(currentActivity, ChatActivity.class);
Intent.putExtra(“conversation”, conversation);
currentActivity.startActivity(intent);
}
}
});
建立成功之後,我們就可以進入聊天介面了。開放聊天室的其他操作,都與普通群組操作一樣。
加入開放聊天室
只要應用層不做限制,任何終端使用者都可以加入開放聊天室,這部分邏輯與之前的加入群組一樣。
同樣的,離開任何「對話」(不論普通還是「暫態」),呼叫 AVIMConversation.quit(callback)
函式即可,這裡不再贅述。
查詢線上人數
對於開放聊天室來說,與普通群組有很大一點不同,就是沒有了參與使用者列表,取而代之的是可以查詢實時線上人數。AVIMConversation.getMemberCount()
函式可以完成這一功能,其宣告如下:
void getMemberCount(AVIMConversationMemberCountCallback callback)
引數含義說明如下:
- callback 是結果回撥介面,在收到服務端返回結果之後被呼叫,開發者可以得到實際的人數或者出錯資訊。
這部分的示例程式碼如下:
conversation.getMemberCount(new AVIMConversationMemberCountCallback(){
@Override
public void done(Integer memberCount, AVException e) {
if (null != e) {
// 出錯了:(
} else {
// 成功,此時 memberCount 的數值就是實時線上人數
}
}
});
簽名和安全
為了滿足開發者對許可權和認證的要求,LeanCloud 還設計了操作簽名的機制。我們可以在 LeanCloud 應用控制檯中的「設定」->「應用選項」->「聊天推送」下面勾選「聊天服務簽名認證」來啟用簽名(強烈推薦這樣做)。啟用後,所有的使用者登入、對話建立/加入、邀請成員、踢出成員等操作都需要驗證簽名,這樣開發者就可以對訊息進行充分的控制。
客戶端這邊究竟該如何使用呢?我們只需要實現 SignatureFactory 介面,然後在使用者登入之前,把這個介面的例項賦值給 AVIMClient 即可(AVIMClient.setSignatureFactory(factory)
)。
設定了 signatureFactory 之後,對於需要鑑權的操作,LeanCloud IM SDK 與伺服器端通訊的時候都會帶上應用自己生成的 Signature 資訊,LeanCloud 雲端會使用 app 的 masterKey 來驗證資訊的有效性,保證聊天渠道的安全。
對於 SignatureFactory 介面,我們只需要實現這兩個函式即可:
/**
* 實現一個基礎簽名方法 其中的簽名演算法會在SessionManager和AVIMClient(V2)中被使用
*/
public Signature createSignature(String peerId, List<String> watchIds) throws SignatureException;
/**
* 實現AVIMConversation相關的簽名計算
*/
public Signature createConversationSignature(String conversationId, String clientId,
List<String> targetIds, String action) throws SignatureException;
createSignature
函式會在使用者登入的時候被呼叫,createConversationSignature
會在對話建立/加入、邀請成員、踢出成員等操作時被呼叫。
你需要做的就是按照前文所述的簽名演算法實現簽名,其中 Signature
宣告如下:
public class Signature {
public List<String> getSignedPeerIds();
public void setSignedPeerIds(List<String> signedPeerIds);
public String getSignature();
public void setSignature(String signature);
public long getTimestamp();
public void setTimestamp(long timestamp);
public String getNonce();
public void setNonce(String nonce);
}
其中四個屬性分別是:
- signature 簽名
- timestamp 時間戳,單位秒
- nonce 隨機字串 nonce
- signedPeerIds 放行的 clientId 列表,v2 中已經廢棄不用
下面的程式碼展示了基於 LeanCloud 雲程式碼進行簽名時,客戶端的實現片段,你可以參考它來完成自己的邏輯實現:
public class KeepAliveSignatureFactory implements SignatureFactory {
@Override
public Signature createSignature(String peerId, List<String> watchIds) {
Map<String,Object> params = new HashMap<String,Object>();
params.put("self_id",peerId);
params.put("watch_ids",watchIds);
try{
Object result = AVCloud.callFunction("sign",params);
if(result instanceof Map){
Map<String,Object> serverSignature = (Map<String,Object>) result;
Signature signature = new Signature();
signature.setSignature((String)serverSignature.get("signature"));
signature.setTimestamp((Long)serverSignature.get("timestamp"));
signature.setNonce((String)serverSignature.get("nonce"));
return signature;
}
}catch(AVException e){
throw (SignatureFactory.SignatureException) e;
}
return null;
}
@Override
public Signature createConversationSignature(String convId, String peerId, List<String> targetPeerIds,String action){
Map<String,Object> params = new HashMap<String,Object>();
params.put("self_id",peerId);
params.put("group_id",convId);
params.put("group_peer_ids",targetPeerIds);
params.put("action",action);
try{
Object result = AVCloud.callFunction("group_sign",params);
if(result instanceof Map){
Map<String,Object> serverSignature = (Map<String,Object>) result;
Signature signature = new Signature();
signature.setSignature((String)serverSignature.get("signature"));
signature.setTimestamp((Long)serverSignature.get("timestamp"));
signature.setNonce((String)serverSignature.get("nonce"));
return signature;
}
}catch(AVException e){
throw (SignatureFactory.SignatureException) e;
}
return null;
}
}
LeanCloud IM SDK 專注做好底層的通訊服務,有更多可以定製化的地方,譬如說:
- 賬戶系統和 IM 系統是分離的;
- 訊息變成離線推送的時候,推送內容開發者是可以定製的;
- 通過 web hook,開發者可以對訊息進行更多處理;
- 聊天過程中通過訊息鑑權機制,開發者可以有更多控制;
因為缺少 UI 元件,實事求是地講在新使用者接入成本可能稍高,但是在業務規模擴大、產品需求變多之後,相信大家會越來越喜歡 LeanCloud 這種自由靈活的使用體驗,以及穩定迅捷的服務質量。