Android 应用APP加入聊天功能
简介
自去年 leancloud 发布实时通信(im)服务之后,基于用户反馈和工程师对需求的消化和对业务的提炼,上周正式发布了「实时通信 2.0 」。设计理念依然是「灵活、解耦、可组合、可定制」,具体可以参考《实时通信开发指南》,了解 leancloud 实时通信的基本概念和模型。
下载和安装
可以到 leancloud 官方下载点下载 leancloud im sdk v2 版本。将下载到的 jar 包加入工程即可。
一对一的文本聊天
我们先从最简单的环节入手,看看怎么用 leancloud im sdk v2 实现一对一文本聊天。
初始化
和 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>
接下来我们需要完成用户登录。
登录
假定聊天发起方名叫 tom,为直观起见,我们使用用户名来作为 clientid 登录聊天系统(leancloud 云端只要求 clientid 在应用内唯一即可,具体用什么数据由应用层决定),代码如下:
avimclient imclient = avimclient.getinstance("tom");
imclient.open(new imclientcallback(){
@override
public void done(avimclient client, avexception e) {
if (e) {
// 出错了,可能是网络问题无法连接 leancloud 云端,请检查网络之后重试。
// 此时聊天服务不可用。
} else {
// 成功登录,可以开始进行聊天了。
};
}
});
建立对话
假定我们要跟「bob」这个用户进行聊天,我们先创建一个对话,代码如下:
// 下面的代码包含了实际应用中的所有逻辑:查询->创建「对话」。
// 先查询一下是否已经存在与「bob」的私聊对话,可以先忽略这部分代码
list<string> clientids = new arraylist<string>();
clientids.add("tom");
clientids.add("bob");
avimconversationquery conversationquery = imclient.getquery();
conversationquery.withmembers(clientids);
// 之前有常量定义:
// int conversationtype_oneone = 0; // 两个人之间的单聊
// int conversationtype_group = 1; // 多人之间的群聊
conversationquery.whereequalto("attr.type", conversationtype_oneone);
conversationquery.findinbackground(new avimconversationquerycallback(){
@override
public void done(list<avimconversation> conversations, avexception e) {
if (null != e) {
// 出错了。。。
} else if (null != conversations && conversations.size() > 0){
// 已经有一个和 bob 的对话存在,继续在这一对话中聊天
...
} else {
// 不曾和 bob 聊过,新建一个对话。!!**这里是重点**!!
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) {
// 成功了!
}
}
});
}
}
});
如何查询「对话」
如你所见,我们创建一个对话的时候,指定了成员(tom 和 bob)和一个额外的属性({type: 0})。这些数据保存到云端后,你在 控制台 -> 存储 -> 数据 里面会看到,_conversation 表中增加了一条记录,新记录的 m 属性值为["tom", "bob"],attr 属性值为{"type":0}。如你所料,m 属性就是对应着成员列表,attr 属性就是用户增加的额外属性值(以对象的形式存储)。
与 avobject 的检索方法一样,要检索这样的对话,我们需要通过 imclient.getquery() 得到一个 avimconversationquery 实例,然后调用 conversationquery.withmembers() 来限定成员列表,调用 conversationquery.whereequalto() 来限定额外的 attr 属性。按照 avquery 的惯例,限定额外的 type 条件的时候需要指定的属性名是 attr.type。
发送消息
建立好对话之后,要发送消息是很简单的:
avimmessage message = new avimmessage();
message.setcontent("hello");
conversation.sendmessage(message, new avimconversationcallback() {
@override
public void done(avexception e) {
if (null != e) {
// 出错了。。。
} else {
}
}
});
好了,这样一条消息就发送过去了。但是问题来了,对于「bob」而言,他怎么才能收到别人发给他的消息呢?
消息接收
在 bob 这一端,要能接收到消息,需要如下几步:
1,进行初始化和登录,代码与发送端并无二致;
2,实现自己的 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) {
// 新消息到来了。在这里增加你自己的处理代码。
...
}
}
avimmessagemanager.registerdefaultmessagehandler(new custommessagehandler());
几个主要的回调接口
从上面的例子中可以看到,要接收到别人给你发送的消息,需要实现自己的 avimmessagehandler 类。从 v2 版开始,leancloud im sdk 大量采用回调来反馈操作结果,但是对于一些被动的消息通知,则还是采用接口来实现的,包括:
当前网络出现变化
对话中有新的消息
对话中有新成员加入
对话中有成员离开
被邀请加入某对话
被踢出对话
leancloud im sdk 内部使用了三种接口来响应这些事件。
网络事件响应接口(avimclienteventhandler)
主要用来处理网络变化事件,主要函数为:
/**
* 实现本方法以处理网络断开事件
*
* @param client
* @since 3.0
*/
public abstract void onconnectionpaused(avimclient client);
/**
* 实现本方法以处理网络恢复事件
*
* @since 3.0
* @param client
*/
public abstract void onconnectionresume(avimclient client);
在网络中断的情况下,所有的消息收发和对话操作都会出现问题。
通过 avimclient.setclienteventhandler(avimclienteventhandler handler) 可以设定全局的 clienteventhandler。
对话成员变化响应接口(avimconversationeventhandler)
主要用来处理对话中成员变化的事件,主要函数为:
/**
* 实现本方法以处理聊天对话中的参与者离开事件
*
* @param client
* @param conversation
* @param members 离开的参与者
* @param kickedby 离开事件的发动者,有可能是离开的参与者本身
* @since 3.0
*/
public abstract void onmemberleft(avimclient client,
avimconversation conversation, list<string> members, string kickedby);
/**
* 实现本方法以处理聊天对话中的参与者加入事件
*
* @param client
* @param conversation
* @param members 加入的参与者
* @param invitedby 加入事件的邀请人,有可能是加入的参与者本身
* @since 3.0
*/
public abstract void onmemberjoined(avimclient client,
avimconversation conversation, list<string> members, string invitedby);
/**
* 实现本方法来处理当前用户被踢出某个聊天对话事件
*
* @param client
* @param conversation
* @param kickedby 踢出你的人
* @since 3.0
*/
public abstract void onkicked(avimclient client, avimconversation conversation,
string kickedby);
/**
* 实现本方法来处理当前用户被邀请到某个聊天对话事件
*
* @param client
* @param conversation 被邀请的聊天对话
* @param operator 邀请你的人
* @since 3.0
*/
public abstract void oninvited(avimclient client, avimconversation conversation,
string operator);
通过 avimmessagemanager.setconversationeventhandler(avimconversationeventhandler handler) 可以设置全局的 conversationeventhandler。
消息响应接口(messagehandler)
主要用来处理新消息到达事件,主要的函数为:
// 收到新的消息
@override
public abstract void onmessage(avimmessage message, avimconversation conversation);
// 自己发送的消息已经被对方接收
@override
public abstract void onmessagereceipt(avimmessage message, avimconversation conversation, avimclient client);
通过 avimmessagemanager.registerdefaultmessagehandler(messagehandler handler) 可以设置全局的 messagehandler。
我们实现这三类接口,就可以处理所有的通知消息了(注意:leancloud im sdk 内部实现了一个空的 avimmessagehandler,你可以从这里派生出进行实际处理的 handler)。
支持富媒体的聊天消息
上面的代码演示了如何发送简单文本信息,但是现在的交互方式已经越来越多样化,图片、语音、视频已是非常普遍的消息类型。v2 版的 leancloud im sdk 已经可以很好地支持这些富媒体消息,具体说明如下:
基类:avimtypedmessage
所有富媒体消息的基类,其声明为
//sdk定义的消息类型,leancloud sdk 自身使用的类型是负数,所有正数留给开发者自定义扩展类型使用,0 作为「没有类型」被保留起来。
enum avimreservedmessagetype {
unsupportedmessagetype(0),
textmessagetype(-1),
imagemessagetype(-2),
audiomessagetype(-3),
videomessagetype(-4),
locationmessagetype(-5),
filemessagetype(-6);
};
public abstract class avimtypedmessage extends avimmessage {
public avimtypedmessage();
public int getmessagetype();
@override
public final string getcontent();
@override
public final void setcontent(string content);
}
文本消息(avimtextmessage)
avimtypedmessage 子类,表示一般的文本消息,其声明为
@avimmessagetype(type = -1)
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);
}
要发送文本消息,示例代码为:
avimtextmessage message = new avimtextmessage();
message.settext("hello");
conversation.sendmessage(message, new avimconversationcallback() {
@override
public void done(avexception e) {
if (null != e) {
// 出错了。。。
} else {
}
}
});
图像消息(avimimagemessage)
avimtypedmessage 子类,支持发送图片和附带文本的混合消息,其声明为:
public class avimimagemessage extends avimfilemessage {
public avimimagemessage();
public avimimagemessage(string localpath) throws filenotfoundexception, ioexception;
public avimimagemessage(file localfile) throws filenotfoundexception, ioexception;
public avimimagemessage(avfile file);
/**
* 获取文件的metadata
*
* @return
*/
@override
public map<string, object> getfilemetadata();
/**
* 获取图片的高
*
* @return
*/
public int getheight();
/**
* 获取图片的宽度
*
* @return
*/
public int getwidth();
}
发送图片消息的示例代码为:
string localimagepath;
try {
avimimagemessage message = new avimimagemessage(localimagepath);
conversation.sendmessage(message, new avimconversationcallback() {
@override
public void done(avexception e) {
if (null != e) {
// 出错了。。。
} else {
}
}
});
} catch (exception ex) {
}
接收到这样消息之后,开发者可以获取到若干图片元数据(width,height,图片 size,图片 format)和一个包含图片数据的 avfile 对象。
音频消息(avimaudiomessage)
avimtypedmessage 子类,支持发送语音和附带文本的混合消息,其声明为:
public class avimaudiomessage extends avimfilemessage {
public avimaudiomessage();
public avimaudiomessage(string localpath) throws filenotfoundexception, ioexception;
public avimaudiomessage(file localfile) throws filenotfoundexception, ioexception;
public avimaudiomessage(avfile file);
/**
* 获取文件的metadata
*
* @return
*/
@override
public map<string, object> getfilemetadata();
/**
* 获取音频的时长
*
* @return
*/
public double getduration();
}
发送音频消息的示例代码为:
string localaudiopath;
try {
avimaudiomessage message = new avimaudiomessage(localaudiopath);
conversation.sendmessage(message, new avimconversationcallback() {
@override
public void done(avexception e) {
if (null != e) {
// 出错了。。。
} else {
}
}
});
} catch (exception ex) {
}
接收到这样消息之后,开发者可以获取到若干音频元数据(时长 duration、音频 size,音频 format)和一个包含音频数据的 avfile 对象。
视频消息(avimvideomessage)
avimtypedmessage 子类,支持发送视频和附带文本的混合消息,其声明为:
public class avimvideomessage extends avimfilemessage {
public avimvideomessage();
public avimvideomessage(string localpath) throws filenotfoundexception, ioexception;
public avimvideomessage(file localfile) throws filenotfoundexception, ioexception;
public avimvideomessage(avfile file);
/**
* 获取文件的metadata
*
* @return
*/
@override
public map<string, object> getfilemetadata();
/**
* 获取时长
*
* @return
*/
public double getduration();
}
发送视频消息的示例代码为:
string localvideopath;
try {
avimvideomessage message = new avimvideomessage(localvideopath);
conversation.sendmessage(message, new avimconversationcallback() {
@override
public void done(avexception e) {
if (null != e) {
// 出错了。。。
} else {
}
}
});
} catch (exception ex) {
}
接收到这样消息之后,开发者可以获取到若干视频元数据(时长 duration、视频 size,视频 format)和一个包含视频数据的 avfile 对象。
地理位置消息(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);
}
要发送位置消息的示例代码为:
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) {
// 出错了。。。
} else {
}
}
});
接收到这样的消息之后,开发者可以获取到具体的地理位置数据。
如何接收富媒体消息
新版 leancloud im sdk 内部封装了对富媒体消息的支持,所有富媒体消息都是从 avimtypedmessage 派生出来的。发送的时候可以直接调用 conversation.sendmessage 函数。在接收端,我们也专门增加了一类回调接口:
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。
leancloud im sdk 内部消息分发的逻辑是这样的:对于收到的任一新消息,sdk 内部都会先解析消息的类型,根据类型找到开发者为这一类型注册的处理 handler,然后逐一调用这些 handler 的 onmessage 函数。如果没有找到专门处理这一类型消息的 handler,就会转交给 defaulthandler 处理。
这样一来,在开发者为 typedmessage(及其子类) 指定了专门的 handler,也指定了全局的 defaulthandler 了的时候,如果发送端发送的是通用的 avimmessage 消息,那么接受端就是 avimmessagemanager.registerdefaultmessagehandler()中指定的 handler 被调用;如果发送的是 avimtypedmessage(及其子类)的消息,那么接受端就是 avimmessagemanager.registermessagehandler()中指定的 handler 被调用。
接收端对于富媒体消息的通知处理代码片段如下:
class msghandler extends avimtypedmessagehandler {
@override
public void onmessage(avimtypedmessage message, avimconversation conversation, avimclient client) {
getinstance().onmessage(conversation, message);
}
@override
public void onmessagereceipt(avimtypedmessage message, avimconversation conversation, avimclient client) {
getinstance().onmessagedelivered(message);
}
}
msghandler msghandler = new msghandler();
avimmessagemanager.registermessagehandler(avimtypedmessage.class, msghandler);
如何扩展自己的富媒体消息
继承于 avimtypedmessage,开发者也可以扩展自己的富媒体消息,这一部分属于高阶内容,大家有兴趣可以参看这篇文档,这里不再赘述。
群组聊天
与前面的单聊类似,群组聊天也需要先建立一个对话(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) {
// 成功了!
}
}
});
成功之后,我们就可以进入聊天界面了。
往群组发送消息
发送消息非常简单,与前面单聊的场景一样。
我们会注意到,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 如下:
list<string> userids = new arraylist<string>();
userids.add("a");
userids.add("b");
userids.add("c");
conversation.addmembers(userids, new avimconversationcallback() {
@override
public void done(avexception error) {
if (null != error) {
// 加入失败,报错.
} else {
// 发出邀请,此后新成员就可以看到这个对话中的所有消息了。
}
}
});
邀请成功以后,通知的流程是这样的:
操作者(管理员) 被邀请者 其他人
1, 发出请求 addmembers
2, 收到 oninvited 通知
3, 收到 onmemberjoined 通知 收到 onmemberjoined 通知 收到 onmemberjoined 通知
相应地,踢人时的调用 api 是:
list<string> userids = new arraylist<string>();
userids.add("a");
conversation.kickmembers(userids, new avimconversationcallback() {
@override
public void done(avexception error) {
if (null != error) {
// 失败,报错.
} else {
// 成功。
}
}
});
踢人的通知流程如下:
操作者(管理员) 被踢者 其他人
1, 发出请求 kickmembers
2, 收到 onkicked 通知
3, 收到 onmemberleft 通知 收到 onmemberleft 通知
注意!
如果邀请、踢人操作发生的时候,被邀请者/被踢者当前不在线,那么通知消息并不会被离线缓存,所以他们再上线的时候将不会收到通知。
获取历史消息
leanmessage 会将非暂态消息自动保存在云端,之后开发者可以通过 avimconversation 来获取该对话的所有历史消息。获取历史消息的 api 如下:
string oldestmsgid;
long oldestmsgtimestamp;
conversation.querymessages(oldestmsgid,oldestmsgtimestamp, limit, new avimhistorymessagecallback(){
@override
public void done(list<avimmessage> messages, avexception e) {
if (null != e) {
// 出错了:(
} else {
// 成功
}
}
});
注意:
获取历史消息的时候,leancloud 云端是从某条消息开始,往前查找开发者指定的 n 条消息,返回给客户端。为此,获取历史消息需要传入三个参数:起始消息的 msgid,起始消息的发送时间戳,需要获取的消息条数。
通过这一 api 拿到的消息就是 avimmessage 或者 avimtypedmessage 实例数组,开发者可以像之前收到新消息通知一样处理。
启用离线消息推送(仅对 ios 平台用户有效)
不管是单聊还是群聊,当用户 a 发出消息后,如果目标对话中有部分用户当前不在线,leancloud 云端可以提供离线推送的方式来提醒用户。这一功能默认是关闭的,你可以在 leancloud 应用控制台中开启它。开启方法如下:
登录 leancloud 应用控制台,选择正确的应用进入;
选择最顶端的「消息」服务,依次点击左侧菜单「实时消息」->「设置」;
在右侧「ios 用户离线时的推送内容」下填好你要推送出去的消息内容,保存;
这样 ios 平台上的用户就可以收到 push notification 了(当然,前提是应用本身申请到了 remotenotification 权限,也将正确的推送证书上传到了 leancloud 控制台)。
群组消息免打扰(仅对 ios 平台用户有效)
不管是单聊还是群聊,对于发往普通的 conversation 的普通消息,如果接收方当前不在线,leancloud 云端支持通过 push notification 的方式进行提醒。一般情况下这都是很好的,但是如果某个群组特别活跃,那离线用户就会收到过多的推送,会形成不小的干扰。
对此 leancloud im 服务也允许单个用户来关闭/打开某个对话的离线推送功能。调用 api 如下:
if (open) {
[_conversation mutewithcallback:^(bool succeeded, nserror *error) {
...
}];
} else {
[_conversation unmutewithcallback:^(bool succeeded, nserror *error) {
...
}];
}
搜索群组
不管是单聊,还是群聊,在 leancloud im sdk 里面都是对话(conversation)。我们给对话设置了如下几种属性:
conversationid,字符串,对话 id,只读,对话创建之后由 leancloud 云端赋予一个全局唯一的 id。
creator,字符串,对话创建者 id,只读,标识对话创建者信息
members,数组,对话参与者,这里记录了所有的参与者
name,字符串,对话的名字,optional,可用来对于群组命名
attributes,map/dict,自定义属性,optional,供开发者自己扩展用。
我们提供了专门的类,来搜索特定的群组。例如要搜索当前登录用户参与的所有群聊对话,其代码为
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) {
// 出错了。。。
} else {
// done!
}
}
});
avimconversationquery 中设置条件的方法与 avquery 类似,具体可以参看其头文件。这里要强调的一点是,对于自定义属性的约束条件,属性名一定要以 attr 开头。
开放聊天室
开放聊天室(也叫暂态对话)可以用于很多地方,譬如弹幕、直播等等。在 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) {
// 成功了!
}
}
});
加入成功之后,我们就可以进入聊天界面了。开放聊天室的其他操作,都与普通群组操作一样。
查询在线人数
通过 avimconversation.getmembercount() 方法可以实时查询开放聊天室的在线人数。示例代码如下:
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)中被使用
*
* @param peerid
* @param watchids
* @return
* @throws signatureexception 如果签名计算中间发生任何问题请抛出本异常
*/
public signature createsignature(string peerid, list<string> watchids) throws signatureexception;
/**
* 实现avimconversation相关的签名计算
*
* @param conversationid
* @param clientid
* @param targetids 操作所对应的数据
* @param action 操作
* @return
* @throws signatureexception 如果签名计算中间发生任何问题请抛出本异常
*/
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 这种*灵活的使用体验,以及稳定迅捷的服务质量。
以上所述就是本文的全部内容了,希望大家能够喜欢。
请您花一点时间将文章分享给您的朋友或者留下评论。我们将会由衷感谢您的支持!