房间管理
功能简介
本文档适用于开发以下平台的应用:iOS、Android、macOS、Windows、Web。
ZIM SDK 提供多人房间聊天功能,支持用户向房间内发送文本消息或自定义消息,实现了多人在线交流、同步分享。
多人房间聊天功能可应用于小班课或者会议室等场景,房间成员数量上限请参考 计费说明。
实现流程
用户可以通过以下两种方式,创建房间并进入房间。
房间内的用户,可以通过 sendRoomMessage 接口,向房间内发送消息,详情请参考 收发房间消息。
如果 roomID 已存在:
如果 roomID 不存在:
创建房间、加入房间
以下流程中,我们以客户端 A 创建并加入房间,客户端 B 加入房间为例。
1. 注册回调
所有进入房间的用户,都需注册 onRoomMemberJoined、 onRoomMemberLeft 和 onRoomStateChanged 回调,用于监听房间其他成员的变动和接收房间连接状态发生改变的事件通知。
注册 onRoomMemberJoined 回调示例源码:
@Override
public void onRoomMemberJoined(ZIM zim, ArrayList<ZIMUserInfo> memberList, String roomID) {
super.onRoomMemberJoined(zim, memberList, roomID);
}
1
void onRoomMemberJoined(ZIM * /*zim*/, const std::vector<ZIMUserInfo> & /*memberList*/,
const std::string & /*roomID*/) override{
}
1
- (void)zim:(ZIM *)zim roomMemberJoined:(NSArray<ZIMUserInfo * > *)memberList roomID:(NSString *)roomID {
}
1
ZIMEventHandler.onRoomMemberJoined = (ZIM zim, List<ZIMUserInfo> memberList, String roomID){
};
1
zim.on('roomMemberJoined', (zim, data) => {
// to do something
});
1
注册 onRoomMemberLeft 回调示例源码:
@Override
public void onRoomMemberLeft(ZIM zim, ArrayList<ZIMUserInfo> memberList, String roomID) {
super.onRoomMemberLeft(ZIM zim, ArrayList<ZIMUserInfo> memberList, String roomID);
}
1
void onRoomMemberLeft(ZIM * /*zim*/, const std::vector<ZIMUserInfo> & /*memberList*/,
const std::string & /*roomID*/) override{
}
1
- (void)zim:(ZIM *)zim roomMemberLeft:(NSArray<ZIMUserInfo * > *)memberList roomID:(NSString *)roomID {
}
1
ZIMEventHandler.onRoomMemberLeft = (ZIM zim, List<ZIMUserInfo> memberList, String roomID) {
};
1
zim.on('roomMemberLeft', (zim, data) => {
// to do something
});
1
注册 onRoomStateChanged 回调示例源码:
@Override
public void onRoomStateChanged(ZIM zim, ZIMRoomState state, ZIMRoomEvent event, JSONObject extendedData, String roomID) {
super.onRoomStateChanged(zim, state, event, extendedData, roomID);
}
1
void onRoomStateChanged(ZIM * /*zim*/, ZIMRoomState /*state*/, ZIMRoomEvent /*event*/,
const std::string & /*extendedData*/,
const std::string & /*roomID*/)override{
}
1
- (void)zim:(ZIM *)zim
roomStateChanged:(ZIMRoomState)state
event:(ZIMRoomEvent)event
extendedData:(NSDictionary *)extendedData
roomID:(NSString *)roomID{
}
1
ZIMEventHandler.onRoomStateChanged = (ZIM zim, ZIMRoomState state, ZIMRoomEvent event,
Map extendedData, String roomID){
};
1
zim.on('roomStateChanged', (zim, data) => {
// to do something
});
1
2. 创建房间
客户端 A 登录后,创建一个房间,可以调用 createRoom 接口,传入 ZIMRoomInfo 信息,即可创建并加入房间。同时可以通过错误码 ZIMError
的参数值,判断是否创建成功。相关错误码请查看 常见错误码。
创建成功后会收到 onRoomStateChanged 的通知回调, ZIMRoomState 为 connected, ZIMRoomEvent 为 success 。
- “roomID”、“roomName” 支持开发者自定义规则生成。建议开发者将 “roomID” 设置为一个有意义的值,可将其与自己的业务账号系统进行关联。
- 调用 createRoom 接口创建房间后,会直接加入房间,无需再调用 joinRoom 接口加入房间。
// roomID 最大 32 字节的字符串。仅支持数字,英文字符 和 '!', '#', '$', '%', '&', '(', ')', '+', '-', ':', ';', '<', '=', '.', '>', '?', '@', '[', ']', '^', '_', '{', '}', '|', '~'。
// roomName 最大 64 字节的字符串,无特殊字符限制。
ZIMRoomInfo zimRoomInfo = new ZIMRoomInfo();
zimRoomInfo.roomID = roomID;
zimRoomInfo.roomName = roomName;
zim.createRoom(zimRoomInfo, new ZIMRoomCreatedCallback() {
@Override
public void onRoomCreated(ZIMRoomFullInfo zimRoomFullInfo, ZIMError error) {
if (error.code == ZIMErrorCode.SUCCESS) {
// 创建成功
} else {
// 创建失败
}
}
});
1
// 设置房间配置。
ZIMRoomInfo roomInfo = ZIMRoomInfo();
roomInfo.roomID = '房间ID';
roomInfo.roomName = '房间名称';
// 创建房间。
ZIM
.getInstance()
.createRoom(roomInfo)
.then((value) {
// 当房间创建成功时触发此操作。
})
.catchError((onError) {
// 当房间创建失败时触发此操作。
});
1
// 设置房间配置。
ZIMRoomInfo *roomInfo = [[ZIMRoomInfo alloc]init];
roomInfo.roomID = @"" ;
roomInfo.roomName = @"" ;
// 创建房间。
[zim createRoom:roomInfo callback:^(ZIMRoomFullInfo * _Nonnull roomInfo, ZIMError * _Nonnull errorInfo) {
// 在这里实现创建房间的事件回调。
}];
1
// roomID 最大 32 字节的字符串。仅支持数字,英文字符 和 '!', '#', '$', '%', '&', '(', ')', '+', '-', ':', ';', '<', '=', '.', '>', '?', '@', '[', ']', '^', '_', '{', '}', '|', '~'。
// roomName 最大 64 字节的字符串,无特殊字符限制。
ZIMRoomInfo room_info;
room_info.roomID = room_id;
room_info.roomName = room_name;
zim_->createRoom(room_info, [=](ZIMRoomFullInfo room_info, zim::ZIMError error_info) {
global_main_dialog_->PostUiThread([=] {});
if (error_info.code != 0)
{
ShowMsg(L"创建房间失败,房间ID: %s, 错误码: %d", room_info.baseInfo.roomID, error_info.code);
}
else
{
ShowMsg(L"创建房间成功,房间ID: %s", room_info.baseInfo.roomID);
}
});
1
var roomInfo = { roomID: '', roomName: '' };
zim.createRoom(roomInfo)
.then(function ({ roomInfo }) {
// 操作成功。
})
.catch(function (err) {
// 操作失败。
});
1
3. 加入房间
客户端 B 加入房间,可以调用 joinRoom 接口,传入由 A 创建的房间 roomID,即可加入房间。同时可以根据错误码 ZIMError
的参数值,判断用户是否加入成功。相关错误码请查看 常见错误码。
加入成功后会收到 onRoomStateChanged 的通知回调, ZIMRoomState 为 connected , ZIMRoomEvent 为 success 。
zim.joinRoom(roomID, new ZIMRoomJoinedCallback() {
@Override
public void onRoomJoined(ZIMRoomFullInfo zimRoomFullInfo, ZIMError error) {
if (error.code == ZIMErrorCode.SUCCESS) {
// 加入房间成功。
} else {
// 加入房间失败。
}
}
});
1
ZIM
.getInstance()
.joinRoom('房间ID')
.then((value) {
//成功加入房间时触发此代码块。
})
.catchError((onError) {
//无法加入房间时触发此代码块。
});
1
[zim joinRoom:RoomID callback:^(ZIMRoomFullInfo * _Nonnull roomInfo, ZIMError * _Nonnull errorInfo) {
//在这里实现加入房间的事件回调。
}];
1
zim_->joinRoom(roomId, [=](ZIMRoomFullInfo room_info, zim::ZIMError error_info) {
global_main_dialog_->PostUiThread([=] {});
if (error_info.code != 0)
{
ShowMsg(L"加入房间失败,房间ID:%s,错误代码:%d", room_info.baseInfo.roomID, error_info.code);
}
else
{
ShowMsg(L"成功加入房间,房间ID:%s,房间名称:%s", room_info.baseInfo.roomID, room_info.baseInfo.roomName);
}
});
1
var roomID = '';
zim.joinRoom(roomID)
.then(function ({ roomInfo }) {
// 操作成功。
})
.catch(function (err) {
// 操作失败。
});
1
4. 房间成员变动通知
当房间有其他成员加入时,可以使用 回调接口 onRoomMemberJoined 方法,实现对其他房间成员加入的监听。
例如,当客户端 B 加入由 A 创建的房间时,A 将收到房间内成员变动的通知。
zim.setEventHandler(new ZIMEventHandler() {
// 加入房间通知
@Override
public void onRoomMemberJoined(ZIM zim, ArrayList<ZIMUserInfo> memberList, final String roomID) {
// 通过该通知收到加入房间的用户信息
}
});
1
ZIMEventHandler.onRoomMemberJoined = (memberList, roomID) {
//实现新成员加入房间的事件回调。
};
1
- (void)zim:(ZIM *)zim
roomMemberJoined:(NSArray<ZIMUserInfo * > *)memberList
roomID:(NSString *)roomID{
//这里写房间成员加入后的自定义代码
}
1
void onRoomMemberJoined(ZIM* zim, const std::vector<ZIMUserInfo>& member_list, const std::string& room_id)
{
for (auto member : member_list)
{
ShowMsg(L"用户: (%s%s) 进入房间: %s", member.userID, member.userName, room_id);
}
}
1
// 当有新的房间成员加入房间时,用于接收事件通知的回调函数。它还会返回刚刚加入房间的用户信息。
zim.on('roomMemberJoined', function (zim, { roomID, memberList }) {
console.log(roomID, memberList);
});
1
进入房间
以下流程中,我们以客户端 X 创建并进入房间,客户端 Y 直接进入房间为例。
- 注册 onRoomMemberJoined、 onRoomMemberLeft 和 onRoomStateChanged 回调,详情请参考 创建房间、加入房间 - 1. 注册回调。
- 客户端 X 登录后,调用 enterRoom 接口,传入 ZIMRoomInfo 信息,进入房间;如果传入的 roomID 不存在,将会自动创建一个房间并进入该房间。
- 客户端 Y 登录后,调用 enterRoom 接口,传入由 X 创建的房间 roomID,直接进入房间。
- 进入房间成功后会收到 onRoomStateChanged 的通知回调, ZIMRoomState 为 connected , ZIMRoomEvent 为 success。
- 房间内的用户,同样可以使用 的回调接口 onRoomMemberJoined 方法,实现对房间内成员加入的监听。
ZIMRoomInfo zimRoomInfo = new ZIMRoomInfo();
zimRoomInfo.roomID = roomID;
zimRoomInfo.roomName = roomName;
ZIMRoomAdvancedConfig config = new ZIMRoomAdvancedConfig();
zim.enterRoom(zimRoomInfo, config, new ZIMRoomEnteredCallback() {
@Override
public void onRoomEntered(ZIMRoomFullInfo zimRoomFullInfo, ZIMError error) {
if (error.code == ZIMErrorCode.SUCCESS) {
// 进入成功
} else {
// 进入失败
}
}
});
1
// 设置房间的配置信息。
ZIMRoomInfo roomInfo = ZIMRoomInfo();
roomInfo.roomID = '房间ID';
roomInfo.roomName = '房间名称';
ZIMRoomAdvancedConfig advancedConfig = ZIMRoomAdvancedConfig();
// 直接加入房间。
ZIM.getInstance().enterRoom(roomInfo, advancedConfig).then((value) {
// 当操作成功时触发此处代码。
}).catchError((onError){
// 当操作失败时触发此处代码。
});
1
// 设置房间的配置
ZIMRoomInfo *roomInfo = [[ZIMRoomInfo alloc]init];
roomInfo.roomID = @"" ;
roomInfo.roomName = @"" ;
ZIMRoomAdvancedConfig *config = [[ZIMRoomAdvancedConfig alloc] init];
// 直接加入房间
[zim enterRoom:roomInfo config:config callback:^(ZIMRoomFullInfo * _Nonnull roomInfo, ZIMError * _Nonnull errorInfo) {
// 实现加入房间的事件回调
}];
1
ZIMRoomInfo room_info;
room_info.roomID = room_id;
room_info.roomName = room_name;
ZIMRoomAdvancedConfig config;
zim_->enterRoom(room_info, config, [=](ZIMRoomFullInfo room_info, zim::ZIMError error_info) {
global_main_dialog_->PostUiThread([=] {});
if (error_info.code != 0)
{
ShowMsg(L"加入房间失败。房间ID:%s,错误代码:%d", room_info.baseInfo.roomID, error_info.code);
}
else
{
ShowMsg(L"成功加入房间。房间ID:%s", room_info.baseInfo.roomID);
}
});
1
var roomInfo = { roomID: '', roomName: '' };
zim.enterRoom(roomInfo)
.then(function ({ roomInfo }) {
// 操作成功。
})
.catch(function (err) {
// 操作失败。
});
// 当有新的房间成员加入房间时,接收事件通知的回调函数。它还返回刚刚加入房间的用户信息。
zim.on('roomMemberJoined', function (zim, { roomID, memberList }) {
console.log(roomID, memberList);
});
1
切换房间
如果用户想要从一个房间切换至另一个房间,可以调用 switchRoom 接口,传入原房间的 ID(fromRoomID
)以及目标房间信息(toRoomInfo
,含房间 ID 和房间名称),即可切换房间。
然而,可能因目标房间不存在而导致房间切换失败。如需避免这种失败,也可以在调用 switchRoom 接口时,传入 isCreateWhenRoomNotExisted
为 true
(允许 ZIM 在房间不存在时创建新房间)和 config (用于配置新房间)。如此,当 ZIM 判断目标房间不存在时,就会根据 toRoomInfo
和 config
(如有)创建新房间用于切换。
房间切换成功后,原房间内的其他用户可通过 的回调接口 onRoomMemberLeft ,得知有用户离开房间;目标房间内的其他用户可通过 的回调接口 onRoomMemberJoined ,得知有用户进入了房间。
String fromRoomID = "fromRoomID";
ZIMRoomInfo toRoomInfo = new ZIMRoomInfo();
roomInfo.roomID = "toRoomID";
roomInfo.roomName = "toRoomName";
// 如果房间不存在时,是否创建房间。
// 只有此为 true 且切换到的房间不存在时,toRoomInfo 中的 toRoomName 以及 config 才有意义
boolean isCreateWhenRoomNotExisted = true;
// 进阶配置,用于创建房间
ZIMRoomAdvancedConfig config = new ZIMRoomAdvancedConfig();
config.roomAttributes.put("key1", "value1");
config.roomDestroyDelayTime = 90;
// 切换房间
zim.switchRoom(fromRoomID, toRoomInfo, isCreateWhenRoomNotExisted, config, new ZIMRoomSwitchedCallback() {
@Override
public void onRoomSwitched(ZIMRoomFullInfo roomInfo, ZIMError errorInfo) {
if (errorInfo.code == ZIMErrorCode.SUCCESS) {
// 切换房间成功,开发者可以从 roomInfo 中获取切换后房间的信息
}
}
});
1
NSString *fromRoomID = @"fromRoomID";
ZIMRoomInfo *toRoomInfo = [[ZIMRoomInfo alloc] init];
toRoomInfo.roomID = @"toRoomID";
toRoomInfo.roomName = @"toRoomName";
// 如果房间不存在,是否创建一个新的房间。
// 只有当这个值为true并且目标房间不存在时,toRoomName和toRoomInfo中的config才有意义。
BOOL isCreateWhenRoomNotExisted = YES;
// 创建房间的高级配置
ZIMRoomAdvancedConfig *config = [[ZIMRoomAdvancedConfig alloc] init];
[config insertRoomAttribute:@"key1" value:@"value1"];
config.roomDestroyDelayTime = 90;
// 切换房间
[zim switchRoomFromRoomID:fromRoomID
toRoomInfo:toRoomInfo
isCreateWhenRoomNotExisted:isCreateWhenRoomNotExisted
config:config
callback:^(ZIMRoomFullInfo *_Nonnull roomInfo, ZIMError *_Nonnull errorInfo) {
if (errorInfo.code == 0) {
// 房间切换成功,您可以从roomInfo中获取有关切换后的房间的信息
}
}];
1
std::string fromRoomID = "fromRoomID";
auto toRoomInfo = zim::ZIMRoomInfo("toRoomID", "toRoomName");
// 当房间不存在时是否创建房间。
// 只有当此值为true且目标房间不存在时,toRoomName和toRoomInfo中的config才有意义。
bool isCreateWhenRoomNotExisted = true;
// 创建房间的高级配置
auto config = zim::ZIMRoomAdvancedConfig();
config.roomAttributes.emplace("key1", "value1");
config.roomDestroyDelayTime = 90;
// 切换房间
zim->switchRoom(
fromRoomID, toRoomInfo, isCreateWhenRoomNotExisted, config,
[=](const zim::ZIMRoomFullInfo &roomInfo, const zim::ZIMError &errorInfo) {
if (errorInfo.code == zim::ZIMErrorCode::ZIM_ERROR_CODE_SUCCESS) {
// 房间切换成功,您可以从roomInfo中获取有关切换后房间的信息
}
});
1
const fromRoomID = 'fromRoomID';
const toRoomInfo = { roomID: 'toRoomID', roomName: 'toRoomName' };
const isCreateWhenRoomNotExisted = true;
// 创建房间的高级配置
const to_room_attr = {
key1: 'value1', // 将此代码替换为实际的键值对
key2: 'value2', // 另一个键值对
// 可以添加更多的键值对
};
// 设置房间销毁延迟时间(以秒为单位)
const to_room_delay_destroy_time = 90;
const config = {
roomAttributes: to_room_attr,
roomDestroyDelayTime: to_room_delay_destroy_time,
};
zim.switchRoom(fromRoomID, toRoomInfo, isCreateWhenRoomNotExisted, config)
.then(function (res) {
// 操作成功
})
.catch(function (err) {
// 操作失败
});
1
离开房间
用户如果想要离开房间,可以调用 leaveRoom 接口,传入房间的 roomID,即可退出此房间;房间内的其他用户可以通过 的回调接口 onRoomMemberLeft ,收到成员变动通知。
离开房间成功后会收到 onRoomStateChanged 的通知回调, ZIMRoomState 为 disconnected , ZIMRoomEvent 为 success 。
离开房间后,将不能收到房间内的消息。
// 离开房间
zim.leaveRoom(roomID, new ZIMRoomLeftCallback() {
@Override
public void onRoomLeft(String roomID, ZIMError errorInfo) {
}
});
1
zim.setEventHandler(new ZIMEventHandler() {
// 离开房间通知
@Override
public void onRoomMemberLeft(ZIM zim, ArrayList<ZIMUserInfo> memberList, final String roomID) {
// 通过该通知收到离开房间的用户信息
}
});
1
ZIM
.getInstance()
.leaveRoom('roomID')
.then((value) {
//当成功离开房间时触发此代码块。
})
.catchError((onError) {
//当离开房间失败时触发此代码块。
});
1
ZIMEventHandler.onRoomMemberLeft = (memberList, roomID) {
//当房间成员离开时,在此处填写自定义代码。
};
1
[zim leaveRoom:roomID callback:^(ZIMError * _Nonnull errorInfo) {
//实现房间成员离开时的事件回调。
}];
1
- (void)zim:(ZIM *)zim
roomMemberLeft:(NSArray<ZIMUserInfo * > *)memberList
roomID:(NSString *)roomID{
//在房间成员离开时填写自定义代码。
}
1
zim_->leaveRoom(room_id, [=](zim::ZIMError error_info) {
global_main_dialog_->PostUiThread([=] {
ShowMsg(L"退出房间结果:%d", error_info.code);
});
});
1
void onRoomMemberLeft(ZIM* zim, const std::vector<ZIMUserInfo>& member_list, const std::string& room_id)
{
for (auto member : member_list)
{
ShowMsg(L"用户: (%s,%s) 退出房间: %s", member.userID, member.userName, room_id);
}
}
1
var roomID = '';
zim.leaveRoom(roomID)
.then(function ({ roomID }) {
// 操作成功。
})
.catch(function (err) {
// 操作失败。
});
1
// 当现有房间成员离开房间时,用于接收事件通知的回调函数。它还返回刚刚离开房间的用户的信息。
zim.on('roomMemberLeft', function (zim, { roomID, memberList }) {
console.log(roomID, memberList);
});
1
当所有成员离开房间后,房间将自动销毁。ZIM SDK 支持通过 config 设置房间延迟销毁,最大延迟为 3 小时。
退出所有房间
调用 leaveAllRoom 接口,即可立即离开当前加入的所有房间,并返回离开的房间列表。如果用户多端登录,则用户在一端调用此接口后,仅会退出在该端上加入的房间,在其他端加入的房间不受影响。
ZIM.getInstance().leaveAllRoom(new ZIMRoomAllLeftCallback() {
@Override
public void onRoomAllLeft(ArrayList<String> roomIDList, ZIMError errorInfo) {
if(errorInfo.code == ZIMErrorCode.SUCCESS){
//roomIDList 为离开的房间列表
}else{
//根据官网错误码表处理
}
}
});
1
try{
ZIMRoomAllLeftResult result = await ZIM.getInstance()!.leaveAllRoom();
result.roomIDs; // 离开的房间列表
} catch (PlatformException onError) {
onError.code;
onError.message;
}
1
[[ZIM getInstance] leaveAllRoom:^(NSArray<NSString *> * _Nonnull roomIDList, ZIMError * _Nonnull errorinfo) {
if(errorinfo.code == ZIMErrorCodeSuccess){
// `roomIDList` 包含用户已离开的所有房间的ID。
}else{
// 根据相应的错误代码表处理错误。
}
}];
1
zim->leaveAllRoom([](const std::vector<std::string>& roomIDList, const ZIMError& errorInfo) {
if (errorInfo.errorCode == 0) {
// `roomIDList` 包含用户已离开的所有房间的ID。
}else{
// 根据相应的错误代码表处理错误。
}
});
1
var roomIDs = [];
zim.leaveAllRoom(roomIDs)
.then(function ({ roomIDs }) {
// 操作成功。
})
.catch(function (err) {
// 操作失败。
});
1
房间内的其他用户可以通过 的回调接口 onRoomMemberLeft ,收到成员变动通知。
退出所有房间成功后,用户会根据所离开房间的数量,多次收到 onRoomStateChanged 的通知回调, ZIMRoomState 为 disconnected , ZIMRoomEvent 为 success 。
离开房间后,将不能收到房间内的消息。
销毁房间
开发者可以在后台调用 ZIM 服务端接口进行 销毁房间。房间被销毁后,用户通过 onRoomStateChanged 收到房间状态( ZIMRoomState )从 connected 转变为 disconnected 的事件通知。根据 ZIM SDK 版本不同,回调中描述导致房间连接状态发生变更的事件( ZIMRoomEvent )如下所示:
- 当 ZIM SDK 版本小于 2.13.0 时,事件为 kickedOut 。
- 当 ZIM SDK 版本为 2.13.0 及之后版本时,事件为 roomNotExist 。
网络中断对房间生命周期的影响
ZIM 房间类似于在线聊天室、网络中断会导致房间进入重连状态并抛出 onRoomStateChanged 。详情请参考 场景 4 : ZIM 房间断网 。