public class CustomConversationEventHandler extends AVIMConversationEventHandler {
@Override
public void onInvited(AVIMClient client, AVIMConversation conversation, String invitedBy) {
// 当前 ClientId(Jerry) 被邀请到对话,执行此处逻辑
}
}
AVIMMessageManager.registerDefaultMessageHandler(new CustomMessageHandler());
var jerry =await realtime.CreateClientAsync("Jerry");
jerry.OnInvited += (sender, args) =>
{
var invitedBy = args.InvitedBy;
var conversationId = args.ConversationId;
};
public static class CustomMessageHandler extends AVIMMessageHandler{
//接收到消息后的处理逻辑
@Override
public void onMessage(AVIMMessage message,AVIMConversation conversation,AVIMClient client){
if(message instanceof AVIMTextMessage){
Log.d(((AVIMTextMessage)message).getText());// Jerry,起床了
}
}
}
AVIMMessageManager.registerDefaultMessageHandler(new CustomMessageHandler());
jerry.OnMessageReceived += Jerry_OnMessageReceived;
private void Jerry_OnMessageReceived(object sender, AVIMMessageEventArgs e)
{
if (e.Message is AVIMTextMessage)
{
var textMessage = (AVIMTextMessage)e.Message;
// textMessage.ConversationId 是该条消息所属于的对话 Id
// textMessage.TextContent 是该文本消息的文本内容
// textMessage.FromClientId 是消息发送者的 client Id
}
}
而当 Tom 创建了对话之后,Jerry 会这一端会立即触发 INVITED(更多关于 INVITED 的描述在成员变更的事件通知总结),此时客户端可以做出一些 UI 展示,引导用户加载聊天的界面,紧接着 Tom 发送了消息,则会触发 Jerry MESSAGE,这个时候就可以直接在聊天界面的消息列表里面渲染这条消息了。
AVIMClient jerry = AVIMClient.getInstance("Jerry");
final AVIMConversation conv = client.getConversation("CONVERSATION_ID");
conv.addMembers(Arrays.asList("Mary"), new AVIMConversationCallback() {
@Override
public void done(AVIMException e) {
// 添加成功
}
});
var tom = await realtime.CreateClientAsync();
var conversation = await tom.GetConversationAsync("CONVERSATION_ID");
await tom.InviteAsync(conversation, "Mary");
而 Jerry 添加如下代码也能随时知道当前对话还有谁加进来了:
var { Event } = require('leancloud-realtime');
// Jerry 登录
realtime.createIMClient('Jerry').then(function(jerry) {
// 有用户被添加至某个对话
jerry.on(Event.MEMBERS_JOINED, function membersjoinedEventHandler(payload, conversation) {
console.log(payload.members, payload.invitedBy, conversation.id);
});
});
}).catch(console.error);
var AV = require('leancloud-storage');
var { ImageMessage } = require('leancloud-realtime-plugin-typed-messages');
var fileUploadControl = $('#photoFileUpload')[0];
var file = new AV.File('avatar.jpg', fileUploadControl.files[0]);
file.save().then(function() {
var message = new ImageMessage(file);
message.setText('发自我的 Ins');
message.setAttributes({ location: '旧金山' });
return conversation.send(message);
}).then(function() {
console.log('发送成功');
}).catch(console.error.bind(console));
var AV = require('leancloud-storage');
var { ImageMessage } = require('leancloud-realtime-plugin-typed-messages');
// 从网络链接直接构建一个图像消息
var file = new AV.File.withURL('萌妹子', 'http://pic2.zhimg.com/6c10e6053c739ed0ce676a0aff15cf1c.gif');
file.save().then(function() {
var message = new ImageMessage(file);
message.setText('萌妹子一枚');
return conversation.send(message);
}).then(function() {
console.log('发送成功');
}).catch(console.error.bind(console));
// Tom 发了一张图片给 Jerry
AVFile *file = [AVFile fileWithURL:[self @"http://ww3.sinaimg.cn/bmiddle/596b0666gw1ed70eavm5tg20bq06m7wi.gif"]];
AVIMImageMessage *message = [AVIMImageMessage messageWithText:@"萌妹子一枚" file:file attributes:nil];
[conversation sendMessage:message callback:^(BOOL succeeded, NSError *error) {
if (succeeded) {
NSLog(@"发送成功!");
}
}];
AVFile file =new AVFile("萌妹子","http://ww3.sinaimg.cn/bmiddle/596b0666gw1ed70eavm5tg20bq06m7wi.gif", null);
AVIMImageMessage m = new AVIMImageMessage(file);
m.setText("萌妹子一枚");
// 创建一条图片消息
conv.sendMessage(m, new AVIMConversationCallback() {
@Override
public void done(AVIMException e) {
if (e == null) {
// 发送成功
}
}
});
即时通讯开发指南 · 基础入门
使用场景和解决的需求
针对如下应用场景,即时通讯都有对应的解决方案:
而上述只是一些比较经典和流行的场景,即时通讯不局限于上述用法和场景,我们提供了许多易用性较高的接口支持开发者可以拓展更多的场景和模式。
即时通讯要解决的核心需求就是:
即时通讯使得开发者可以在不编写服务端的代码的情况下,仅使用客户端 SDK 即可实现在线聊天/多人群组/私聊/消息群发等实时通讯的功能,如下时序图简单的介绍了一下即时通讯的业务流程:
首先,如果您还没有下载对应开发环境(语言)的 SDK,请查看如下内容,选择一门语言,查看下载和安装的文档:
安装与初始化
阅前准备
建议先阅读即时通讯服务总览之后,再阅读本文效果最佳。
一对一单聊
我们从最基本的一对一私聊开始接入即时通讯模块,首先我们需要明确一个需求:
首先我们需要介绍一下在即时通讯服务中的
IMClient
对象:1.创建
IMClient
在 iOS 和 Android SDK 中使用如下代码创建出一个
IMClient
:示例代码中
clientId
被设置为 Tom, 这里我们需要明确一下clientId
的约束:注: JavaScript 和 C#(Unity3D) SDK 创建
IMClient
成功同时意味着连接也已经建立,而 iOS 和 Android SDK 则需要额外下一步2.建立连接2.建立连接
建立连接的含义是:
对应的 SDK 方法如下:
注:JavaScript 和 C#(Unity3D) SDK 建立连接成功之后,会返回一个
IMClient
。推荐使用的方式:在用户登录之后用当前的用户名当做
clientId
来创建IMClient
并建立连接。我们推荐在连接创建成功之后订阅客户端事件与网络状态响应,针对网络的异常情况作出应有的 UI 展示,以确保应用的健壮性。
3.创建对话
Conversation
对话(
Conversation
) 是即时通讯抽象出来的概念,它是客户端之间互发消息的载体,可以理解为一个通道,所有在这个对话内的成员都可以在这个对话内收发消息:Tom 已经建立了连接,因此他需要创建一个
Conversation
来发送消息给 Jerry:createConversation
这个接口会直接创建一个对话,并且该对话会被存储在_Conversation
表内,可以打开控制台->存储查看数据。createConversation
的参数详解:members
: 字符串数组,必要参数,对话的初始成员(clientId)列表,默认包含当前 clientIdname
: 字符串,可选参数,对话的名字,如果不传默认值为 nullunique
: bool 类型,可选参数,是否唯一对话,当其为 true 时,如果当前已经有相同成员的对话存在则返回该对话,否则会创建新的对话members
: 字符串数组,必要参数,对话的初始成员(clientId)列表,默认包含当前 clientIdname
: 字符串,可选参数,对话的名字,如果不传默认值为 nullisSystem
: bool 值,可选参数,是否是服务号isTransient
:bool 值,可选参数,是否为聊天室isUnique
: bool 类型,可选参数,是否唯一对话,当其为 true 时,如果当前已经有相同成员的对话存在则返回该对话,否则会创建新的对话options
:字典类型,可选参数,额外的自定义属性创建对话之后,可以获取对话的内置属性,云端会为每一个对话生成一个全局唯一的 ID 属性:
Conversation.id
,它是查询对话和获取对话时常用的匹配字段。4.发送消息
对话已经创建成功了,接下来 Tom 可以发出第一条文本消息了:
conversation.send
接口实现的功能就是向对话中发送一条消息。Jerry 只要在线他就会收到消息,至此 Jerry 还没有登场,那么他怎么接收消息呢?
5.接收消息
我们在另一个设备上启动应用,然后使用 Jerry 当做
clientId
创建AVIMClient
和 建立连接(参照前两章节 Tom 的示例代码):紧接着让 Jerry 开始订阅对话加入的事件通知:
为 Jerry 添加了加入对话的事件订阅之后,继续为 Jerry 订阅消息接收的事件:
而当 Tom 创建了对话之后,Jerry 会这一端会立即触发
INVITED
(更多关于INVITED
的描述在成员变更的事件通知总结),此时客户端可以做出一些 UI 展示,引导用户加载聊天的界面,紧接着 Tom 发送了消息,则会触发 JerryMESSAGE
,这个时候就可以直接在聊天界面的消息列表里面渲染这条消息了。对应的时序图如下:
多人群聊
多人群聊与单聊十分接近,在一个现有对话上加入更多的成员即可转化为群聊:
1.创建多人群聊对话
多人群聊的对话可以通过如下两种方式实现:
首先我们实现第一种方式: 向现有对话中添加更多成员,将其转化成一个群聊。
Tom 继续添加 Mary 到对话中:
而 Jerry 添加如下代码也能随时知道当前对话还有谁加进来了:
其中 payload 参数包含如下内容:
members
: 字符串数组, 被添加的用户 clientId 列表invitedBy
字符串, 邀请者 clientId其中
AVIMOnInvitedEventArgs
参数包含如下内容:InvitedBy
: 改操作的发起者JoinedMembers
:此次加入对话的包含的成员列表ConversationId
:被操作的对话如下时序图可以简单的概括上述流程:
而 Mary 如果在另一台设备上登录了,Ta 可以参照一对一单聊中 Jerry 的做法监听
INVITED
事件,就可以自己被邀请到了一个对话当中。而重新创建一个对话,并在创建的时候指定至少为 2 的成员数量的方式如下:
2.群发消息
群聊和单聊一样,对话中的成员都会接收到对话内产生的消息。
Tom 向群聊对话发送了消息:
而 Jerry 和 Mary 都会有
Event.MESSAGE
事件触发,利用它来接收群聊消息,这一点与一对一单聊没区别。将他人踢出对话
刚才的代码演示的是添加新成员,而对应的删除成员的代码如下:
Tom 又把 Mary 踢出对话了:
执行了这段代码之后会触发如下流程:
加入对话
紧接着 William 通过
Conversation.id
他自己主动加入对话:注意,如下代码运行的前提有几个前提:
IMClient
的实例,并且确保已经与云端建立连接。590aa654fab00f41dda86f51
是一个在_Conversation
表里面真是存在的对话 Id。执行了这段代码之后会触发如下流程:
其他人则通过订阅
MEMBERS_JOINED
来接收 William 加入对话的通知:退出对话
Jerry 不想继续呆在这个对话里面了,他选择退出对话:
执行了这段代码之后会触发如下流程:
而其他人需要通过订阅
MEMBERS_LEFT
来接收 Jerry 离开对话的事件通知:成员变更的事件通知总结
前面的时序图和代码针对成员变更的操作做了逐步的分析和阐述,为了确保开发者能够准确的使用事件通知,如下表格做了一个统一的归类和划分:
假设 Tom 和 Jerry 已经在对话内了:
MEMBERS_JOINED
MEMBERS_JOINED
INVITED
MEMBERS_LEFT
MEMBERS_LEFT
KICKED
MEMBERS_JOINED
MEMBERS_JOINED
MEMBERS_JOINED
MEMBERS_LEFT
MEMBERS_LEFT
MEMBERS_LEFT
其他对话类型(聊天模式)
即时通讯服务提供的功能就是让一个客户端与其他客户端进行在线的消息互发,对应不同的使用场景除去刚才前两章节介绍的一对一单聊和多人群聊之外,即时通讯也支持但不限于如下中流行的通讯模式:
关于上述的几种场景对应的实现,请参阅进阶功能#对话类型。
对话
一个聊天应用在首页往往会展示当前用户加入的,最活跃的几个对话。
获取对话列表
上述代码默认返回最近活跃的 10 个对话,若要更改返回对话的数量,请设置 limit 值。
根据关键字查询
在某些场景下,我们需要根据对话的一些特性来做查找,比如我要查找名字里包含 NBA 的对话:
更多查询方式
关于上述的几种场景对应的实现,请参阅进阶功能#对话的查询
消息
消息的类型有很多种,使用最多的就是文本消息,其次是图像消息,还有一些短语音/短视频消息,文本消息和其他消息类型有本质的区别:
图像消息
发送图像文件
我们从发送一张图片消息的生命周期的时序图来了解整个过程:
图解:
对应的代码并没有时序图那样复杂,因为调用 send 接口的时候,SDK 会自动上传图像,不需要开发者再去关心这一步:
发送图像链接
除了上述这种从本地直接发送图片文件的消息之外,在很多时候,用户可能从网络上或者别的应用中拷贝了一个图像的网络连接地址,当做一条图像消息发送到对话中,这种需求可以用如下代码来实现:
接收图像消息
对话中的其他成员修改一下接收消息的事件订阅逻辑,根据消息类型来做不同的 UI 展现:
多媒体消息
与图像消息一样,其他多媒体消息都拥有如下两种用法:
AVFile
存储在云端,然后发送云端链接到对话当中,而在接收方的客户端只要对链接做一些处理,根据消息的类型不同做不同的 UI 展现,例如语音消息可以做成一个小按钮,用户点击之后就播放语音,而视频消息则是一张视频截图,用户点击之后播放视频,诸如此类。AVFile
表内,而是直接将其转化成一个消息,发送出去其他类型消息
更多消息类型请点击进阶功能#消息类型。
消息记录
消息记录默认会在云端保存 180 天, SDK 提供了多种方式来获取到本地。开发者可以付费来延长这一期限,请联系 support@leancloud.rocks。另外可以参考 对话的有效期。你也随时可以通过 REST API 将聊天记录同步到自己的服务器上。
iOS 和 Android SDK 分别提供了内置的消息缓存机制,减少客户端对云端消息记录的查询次数,并且在离线情况下,也能查询到准确的消息记录。
获取对话的消息记录
如下代码可以获取一个对话最近的 10 条消息,注意,返回结果是按照时间由新到旧排序的:
而如果想继续拉取更早的消息记录,可以使用如下代码:
按照消息类型获取
如下代码的功能是:获取所有的图像消息:
如要获取更多图像消息,可以效仿前一章节中的示例代码,继续查询可以获取更多的图像消息。
更多的获取方式
更多消息类型请点击进阶功能#消息记录
客户端事件与网络状态响应
注意:在网络中断的情况下,所有的消息收发和对话操作都会失败。开发者应该监听与网络状态相关的事件并更新 UI,以免影响用户的使用体验。
当网络连接出现中断、恢复等状态变化时,SDK 会派发以下事件:
DISCONNECT
:与服务端连接断开,此时聊天服务不可用。OFFLINE
:网络不可用。ONLINE
:网络恢复。SCHEDULE
:计划在一段时间后尝试重连,此时聊天服务仍不可用。RETRY
:正在重连。RECONNECT
:与服务端连接恢复,此时聊天服务可用。在 AVIMClientDelegate 上会有如下事件通知:
imClientPaused:(AVIMClient *)imClient
指网络连接断开事件发生,此时聊天服务不可用。imClientResuming:(AVIMClient *)imClient
指网络断开后开始重连,此时聊天服务依然不可用。imClientResumed:(AVIMClient *)imClient
指网络连接恢复正常,此时聊天服务变得可用。AVIMClientEventHandler
上会有如下事件通知:onConnectionPaused()
指网络连接断开事件发生,此时聊天服务不可用。onConnectionResume()
指网络连接恢复正常,此时聊天服务变得可用。onClientOffline()
指单点登录被踢下线的事件。AVRealtime
上会有如下事件通知:OnDisconnected
指网络连接断开事件发生,此时聊天服务不可用。OnReconnecting
指网络正在尝试重连,此时聊天服务不可用。OnReconnected
指网络连接恢复正常,此时聊天服务变得可用。OnReconnectFailed
指重连失败,此时聊天服务不可用。断线重连
目前 SDK 默认内置了断线重连的功能,从客户端与云端建立连接成功开始,只要没有调用退出登录的接口,SDK 会一直尝试和云端保持长连接,此时
IMClient
的状态可以通过 网络状态响应接口得到。注意:用户如果自行实现了重连逻辑可能会报出 1001 错误。
退出登录与断开连接
更多文档