本文档介绍如何在使用实时音视频产品(ZEGO Express SDK)实现基本音频通话功能的基础上,结合即时通讯产品(ZIM SDK)实现语聊房功能。
本文基于 示例代码 来介绍的如何实现语聊房功能,因此文档中的部分方法为示例代码封装方法。
在开始之前,请确保完成以下步骤:
您可以通过本文档提供的 示例代码 实现以下效果:
主页 | 房主页面 | 听众页面 | 听众点击上麦请求 | 房主查看请求 |
---|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
房主可以单击右下角的“锁定图标”来更改房间模式。
如果您需要了解推流及拉流的相关接口使用,请参考 快速开始 - 实现音频通话文档。
在一个语聊房中:
除了实现上述逻辑外,语聊房还需要管理发言人麦位。发言人麦位管理功能通常可以使用 ZIM SDK 的 房间属性管理 特性实现。
该特性允许开发者客户端在房间中设置和同步自定义的房间属性。房间属性以键值对的方式存储在 ZEGO 服务器上,由 ZEGO 服务器解决写入冲突和其他问题,以确保数据一致性。
同时,开发者客户端对房间属性的修改会通过 ZEGO 服务器实时同步给房间中的所有其他听众。
key
的长度限制为 16 字节,value
的长度限制为 1024 字节。 开发者如果需要提高属性限制上限,请联系 ZEGO 技术支持。以用户 A 申请上麦为例,具体过程如下:
sequenceDiagram participant 用户 A participant sdk1 as ZIM SDK participant server as ZEGO 服务器 participant sdk2 as ZIM SDK participant 用户 B 用户 A ->> sdk1 : setRoomAttributes sdk1 ->> server: setRoomAttributes server ->> sdk2 : onRoomAttributesUpdated sdk2 ->> 用户 B : onRoomAttributesUpdated 用户 B ->> 用户 B : 麦位变动 Note over 用户 A, 用户 B: 聊天中
使用房间属性标志发言人麦位:
您可以使用语聊房中的麦位序号,作为房间属性的 Key,并使用 userID
作为房间属性的值,来表示房间的发言人麦位状态。
例如,如果 userID 为 “user123” 的用户位于 0 号麦位上,而 userID 为 “user456” 的用户位于 1 号麦位上,则其房间属性如下:
{
"0":"user123", // 表示 user123 位于 0 号麦位上
"1":"user456", // 表示 user456 位于 1 号麦位上
}
房间属性功能的设计,可以解决语聊房场景中,一些常见麦位管理问题:
功能 | 描述 | 用途 |
---|---|---|
所有者 |
第一个设置 Key 的听众将成为该 Key 的所有者。默认情况下,只有所有者可以修改 Key。 |
可用于避免抢占发言人麦位时的冲突。 |
自动删除 |
在设置 Key 时,可以将 Key 配置为“所有者离开房间后自动删除”。 |
可用于实现“发言人离线后发言人麦位自动更新”的功能,避免由于开发者客户端断开连接导致发言人麦位混乱的问题。 |
强制修改 |
支持忽略所有者并强制修改 Key 对应的值。 |
可用于实现“房主强制将发言人从麦位上移除”的功能。 |
组合操作 |
可以将对不同 Key-Value 的多个操作组合成一个组合操作,避免其他用户操作相关 Key-Value 时产生冲突。 |
可用于实现更改发言人麦位的功能。 |
在语聊房中,您可能需要支持房主修改房间模式:
房间模式可使用 Express SDK 的 setRoomExtraInfo 实现。“房间附加信息”与上述的“房间属性”类似,也存储在 ZEGO 服务器上,但是“房间附加信息”的使用更简单:
没有复杂的参数,只支持设置 Key-Value 字符串(Key 最大 10 字节,Value 最大 128 字节),更适合与房间绑定的简单业务操作,如房间模式、房间公告等。
您可以将任何业务字段封装成 JSON 协议,并将其设置为“房间附加信息”以实现房间模式等业务逻辑。
当房主调用 setRoomExtraInfo 方法时,房间内的用户可以通过 onRoomExtraInfoUpdate 接收到设置的房间附加信息
。
sequenceDiagram participant 用户 A participant sdk1 as ZEGO Express SDK participant server as ZEGO 服务器 participant sdk2 as ZEGO Express SDK participant 用户 B 用户 A ->> sdk1 : setRoomExtraInfo sdk1 ->> server: setRoomExtraInfo server ->> sdk2 : onRoomExtraInfoUpdate sdk2 ->> 用户 B : onRoomExtraInfoUpdate 用户 B ->> 用户 B : 房间模式已更改 Note over 用户 A, 用户 B: 房间模式已更新
基于房间请求信令实现上麦请求的过程,房间请求信令是一种管理网络中通信和连接的协议或消息。ZEGO 将所有房间请求信令功能打包到一个 SDK 中,为您提供了一个现成的实时房间请求信令 API。
ZIM SDK 提供了丰富的功能来发送和接收消息,详情可参考 收发消息。此处,您需要使用可自定义的房间请求消息:ZIMCommandMessage
此部分的完整示例代码,可参考 ZIMService.java。
通过调用以下方法在房间中发送房间请求信令(ZIMCommandMessage):
zim.sendMessage(commandMessage, mRoomID, ZIMConversationType.ROOM, config, new ZIMMessageSentCallback() {
// ...
@Override
public void onMessageSent(ZIMMessage message, ZIMError errorInfo) {
// ...
}
});
发送后成功后,房间中的其他用户将通过 onReceiveRoomMessage 回调接收房间请求信令。您可以通过以下方式监听此回调:
zim.setEventHandler(new ZIMEventHandler() {
@Override
public void onReceiveRoomMessage(ZIM zim, ArrayList<ZIMMessage> messageList, String fromRoomID) {
super.onReceiveRoomMessage(zim, messageList, fromRoomID);
// ...
}
});
此部分的完整示例代码,可参考 ZIMService.java 和 RoomRequest.java。
房间请求信令 JSON 编码
由于简单的 String 本身难以表达复杂信息,房间请求信令可以封装在 JSON 格式中,使您更方便地编写房间请求信令的协议内容。
以最简单的房间请求信令的 JSON 格式为例:{"action_type": 0}
,在这样一份 JSON 房间请求信令中,您可以使用 action_type 字段来表达不同的房间请求信令类型,例如:
{"action_type": RoomRequestAction.ACTION_REQUEST}
{"action_type": RoomRequestAction.ACTION_CANCEL}
{"action_type": RoomRequestAction.ACTION_ACCEPT}
{"action_type": RoomRequestAction.ACTION_REJECT}
此外,您还可以为房间请求信令扩展其他常见字段,例如 senderID
,receiverID
,extended_data
:
public class RoomRequest {
// ...
public String toString() {
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("action_type", actionType);
jsonObject.put("sender_id", sender);
jsonObject.put("receiver_id", receiver);
jsonObject.put("extended_data", extendedData);
jsonObject.put("request_id", requestID);
} catch (JSONException e) {
throw new RuntimeException(e);
}
return jsonObject.toString();
}
// ...
}
public @interface RoomRequestAction {
int ACTION_REQUEST = 0;
int ACTION_ACCEPT = 1;
int ACTION_REJECT = 2;
int ACTION_CANCEL = 3;
}
房间请求信令 JSON 解码
接收房间请求信令的用户,可以解码 JSON 房间请求信令,并根据其中的字段了解和处理具体的业务逻辑,例如:
zim.setEventHandler(new ZIMEventHandler() {
@Override
public void onReceiveRoomMessage(ZIM zim, ArrayList<ZIMMessage> messageList, String fromRoomID) {
super.onReceiveRoomMessage(zim, messageList, fromRoomID);
zimRoomService.onReceiveRoomMessage(zim, messageList, fromRoomID);
}
// ...
})
// ...
public void onReceiveRoomMessage(ZIM zim, ArrayList<ZIMMessage> messageList, String fromRoomID) {
try {
for (ZIMMessage zimMessage : messageList) {
if (zimMessage instanceof ZIMCommandMessage) {
ZIMCommandMessage commandMessage = (ZIMCommandMessage) zimMessage;
String message = new String(commandMessage.message, StandardCharsets.UTF_8);
JSONObject jsonObject = new JSONObject(message);
ZIMUserInfo currentUser = zimUserService.getCurrentUser();
if (jsonObject.has("action_type") && currentUser != null) {
String sender = jsonObject.getString("sender_id");
String receiver = jsonObject.getString("receiver_id");
int actionType = jsonObject.getInt("action_type");
if (currentUser.userID.equals(receiver)) {
// ...
}
}
}
}
} catch (JSONException e) {
// ...
}
}
扩展房间请求信令
基于这种模式,当您需要进行任何业务协议扩展时,只需扩展房间请求信令的 extended_data
字段,就可以轻松实现新的业务逻辑,例如:
其他扩展信息::
通过以下内容,可进一步了解麦位请求的实现,您将能够对您的业务进行扩展。
本文档中的演示是一个纯客户端 API + ZEGOCLOUD 解决方案。如果您有自己的业务服务端,并希望进行更多的逻辑扩展,您可以使用我们的 服务端 API 来传递房间信令,并结合您服务端的房间业务逻辑,以提高您的应用的可靠性。
sequenceDiagram participant 用户 A participant appServer as 开发者服务端 participant server as ZEGO 服务端 participant sdk as ZIM SDK participant 用户 B 用户 A ->> appServer : 发送麦位请求 appServer ->> appServer : 处理自己的业务逻辑 appServer ->> server: 用户 A 的麦位请求 server ->> sdk : 用户 A 的麦位请求 sdk ->> 用户 B : 用户 A 的麦位请求 Note over 用户 A, 用户 B: ...
如果您之前没有使用过 ZIM SDK,您可以阅读以下部分:
要导入 ZIM SDK,请执行以下操作:
配置 repositories 源
当您的 Android Gradle Plugin 为 v7.1.0 或以上版本:进入项目根目录,打开 “settings.gradle” 文件,在 “dependencyResolutionManagement” 中加入如下代码。
...
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
maven { url 'https://storage.zego.im/maven' }
mavenCentral()
google()
}
}
如果您在 “settings.gradle” 中找不到上述字段,可能是因为您的 Android Gradle Plugin 版本低于 v7.1.0。
若您的 Android Gradle Plugin 为 v7.1.0 以下版本:进入项目根目录,打开 “build.gradle” 文件,在 “allprojects” 中加入如下代码。
...
allprojects {
repositories {
maven { url 'https://storage.zego.im/maven' }
mavenCentral()
google()
}
}
声明依赖项:
进入 “app” 目录,打开 “build.gradle” 文件,在 “dependencies” 中添加 implementation 'im.zego:zim:x.y.z'
,请从 发布日志 查询 SDK 最新版本,并将 x.y.z
修改为具体的版本号。
...
dependencies {
...
implementation 'im.zego:zim:x.y.z'
}
导入 ZIM SDK:
import im.zego.zim.ZIM
在 SDK 中创建 ZIM 实例,一个实例对应的是一个用户,表示一个用户以客户端的身份登录系统。
ZIMAppConfig appConfig = new ZIMAppConfig();
appConfig.appID = yourAppID;
appConfig.appSign = yourAppSign;
ZIM.create(appConfig, application);
稍后,我们将为您提供如何使用 ZIM SDK 开发语聊房功能的详细说明。
在本文档描述的语聊房场景中,您需要使用 ZIM SDK
来实现发言人麦位管理功能,然后使用 Express SDK
实现语聊房功能。
为了使您的应用代码更加有条理,我们建议您通过使用以下方式来管理这些 SDK:
我们将在后续文档中使用 ZIMService
和 ExpressService
进行示例说明。
为 ZIM SDK
创建一个 ZIMService
类,它管理与 SDK 的交互并存储所需数据,完整代码请参考 ZIMService.java。
public class ZIMService {
// ...
public void initSDK(Application application, long appID, String appSign) {
zimProxy.create(application, appID, appSign);
// ...
}
}
class ZIMProxy {
private SimpleZIMEventHandler zimEventHandler;
public void create(Application application, long appID, String appSign) {
ZIMAppConfig zimAppConfig = new ZIMAppConfig();
zimAppConfig.appID = appID;
zimAppConfig.appSign = appSign;
ZIM.create(zimAppConfig, application);
zimEventHandler = new SimpleZIMEventHandler();
if (getZIM() != null) {
ZIM.getInstance().setEventHandler(zimEventHandler);
}
}
}
同样,为 Express SDK
创建一个 ExpressService
类,它管理与 SDK 的交互并存储所需数据,完整代码请参考 ExpressService.java 中的完整代码。
public class ExpressService {
// ...
public void initSDK(Application application, long appID, String appSign, ZegoScenario scenario) {
ZegoEngineConfig config = new ZegoEngineConfig();
config.advancedConfig.put("notify_remote_device_unknown_status", "true");
config.advancedConfig.put("notify_remote_device_init_status", "true");
ZegoExpressEngine.setEngineConfig(config);
engineProxy.createEngine(application, appID, appSign, scenario);
// ...
}
}
class ExpressEngineProxy {
private SimpleExpressEventHandler expressEventHandler;
public void createEngine(Application application, long appID, String appSign, ZegoScenario scenario) {
ZegoEngineProfile profile = new ZegoEngineProfile();
profile.appID = appID;
profile.appSign = appSign;
profile.scenario = scenario;
profile.application = application;
expressEventHandler = new SimpleExpressEventHandler();
ZegoExpressEngine.createEngine(profile, expressEventHandler);
}
}
您可以根据需求,向服务中添加相关 SDK 接口的方法。
例如,当您需要登录时,可以向 ZIMService
添加 connectUser
方法。
public class ZIMService {
// ...
public void connectUser(String userID, String userName, ZIMLoggedInCallback callback) {
ZIMUserInfo zimUserInfo = new ZIMUserInfo();
zimUserInfo.userID = userID;
zimUserInfo.userName = userName;
zim.login(zimUserInfo, new ZIMLoggedInCallback() {
@Override
public void onLoggedIn(ZIMError errorInfo) {
// ...
}
});
}
}
完整代码,请参考 ZEGOSDKManager.java。
public class ZEGOSDKManager {
public ExpressService expressService = new ExpressService();
public ZIMService zimService = new ZIMService();
private static final class Holder {
private static final ZEGOSDKManager INSTANCE = new ZEGOSDKManager();
}
public static ZEGOSDKManager getInstance() {
return Holder.INSTANCE;
}
public void initSDK(Application application, long appID, String appSign,ZegoScenario scenario) {
expressService.initSDK(application, appID, appSign,scenario);
zimService.initSDK(application, appID, appSign);
}
}
通过此方式,您已实现了一个单例类,来管理您需要的 SDK 服务。因此,您可以在项目的任何地方获取此类的实例,并使用它来执行与 SDK 相关的逻辑,例如:
ZEGOSDKManager.getInstance().initSDK(application,appID,appSign);
ZEGOSDKManager.getInstance().connectUser(userID,userName,callback);
稍后,我们将介绍如何基于此添加语聊房功能。
如需让听众上麦,请调用 setRoomAttributes 并将麦位序号作为 Key,听众的 userID
作为房间附加属性的值。如果设置成功,听众会成功上麦并可以开始推流。
完整的示例代码,请参考 RoomSeatService.java,其中主要示例代码如下。
听众上麦时,若将 ZIMRoomAttributesSetConfig 的isForce
属性设置为false
。当多个听众同时尝试占用同一个麦位时,服务器将接收到第一个请求并返回成功响应,将该 Key 的所有者设置为第一个发出请求的用户。其他用户的后续修改请求将失败。
public void takeSeat(int seatIndex, ZIMRoomAttributesOperatedCallback callback) {
ZEGOSDKUser localUser = ZEGOSDKManager.getInstance().expressService.getCurrentUser();
if (localUser == null || isTakeSeat) {
return;
}
isTakeSeat = true;
String key = String.valueOf(seatIndex);
String value = localUser.userID;
ZIMRoomAttributesSetConfig config = new ZIMRoomAttributesSetConfig();
config.isDeleteAfterOwnerLeft = true;
config.isForce = true;
config.isUpdateOwner = true;
// getInstance 为示例代码封装方法 https://github.com/ZEGOCLOUD/zegocloud_sdk_demo_android/blob/master/best_practice/app/src/main/java/com/zegocloud/demo/bestpractice/internal/ZEGOCallInvitationManager.java#L72
// setRoomAttributes 为示例代码封装方法 https://github.com/ZEGOCLOUD/zegocloud_sdk_demo_android/blob/master/best_practice/app/src/main/java/com/zegocloud/demo/bestpractice/internal/sdk/zim/ZIMProxy.java#L96
ZEGOSDKManager.getInstance().zimService.setRoomAttributes(key, value, config,
new ZIMRoomAttributesOperatedCallback() {
@Override
public void onRoomAttributesOperated(String roomID, ArrayList<String> errorKeys, ZIMError errorInfo) {
isTakeSeat = false;
if (callback != null) {
callback.onRoomAttributesOperated(roomID, errorKeys, errorInfo);
}
}
});
}
当发言人下麦时,可调用 deleteRoomAttributes 来删除发言人使用的麦位号,并停止推流。
完整的示例代码,请参考 RoomSeatService.java,其中主要示例代码如下。
public void leaveSeat(int seatIndex, ZIMRoomAttributesOperatedCallback callback) {
//getInstance 为示例代码封装方法 https://github.com/ZEGOCLOUD/zegocloud_sdk_demo_android/blob/master/best_practice/app/src/main/java/com/zegocloud/demo/bestpractice/internal/ZEGOCallInvitationManager.java#L72
//getCurrentUser 为示例代码封装方法 https://github.com/ZEGOCLOUD/zegocloud_sdk_demo_android/blob/dd8062399252502f3f91b46ae2147a43160f73b5/best_practice/app/src/main/java/com/zegocloud/demo/bestpractice/internal/sdk/express/ExpressService.java#L638
ZEGOSDKUser localUser = ZEGOSDKManager.getInstance().expressService.getCurrentUser();
if (localUser == null) {
return;
}
List<String> list = Collections.singletonList(String.valueOf(seatIndex));
//deleteRoomAttributes 为示例代码封装方法 https://github.com/ZEGOCLOUD/zegocloud_sdk_demo_android/blob/dd8062399252502f3f91b46ae2147a43160f73b5/best_practice/app/src/main/java/com/zegocloud/demo/bestpractice/internal/sdk/zim/ZIMService.java#L486
ZEGOSDKManager.getInstance().zimService.deleteRoomAttributes(list, new ZIMRoomAttributesOperatedCallback() {
@Override
public void onRoomAttributesOperated(String roomID, ArrayList<String> errorKeys, ZIMError errorInfo) {
if (callback != null) {
callback.onRoomAttributesOperated(roomID, errorKeys, errorInfo);
}
}
});
}
当房主需要将发言人从麦位上移除时,请调用 deleteRoomAttributes,并将 ZIMRoomAttributesDeleteConfig 的isForce
字段设置为 true
,强制清除相应发言人麦位的房间属性,从而将发言人从麦位上移除。
完整的示例代码,请参考 RoomSeatService.java,其中主要示例代码如下。
public void removeSpeakerFromSeat(String userID, ZIMRoomAttributesOperatedCallback callback) {
//getInstance 为示例代码封装方法 https://github.com/ZEGOCLOUD/zegocloud_sdk_demo_android/blob/master/best_practice/app/src/main/java/com/zegocloud/demo/bestpractice/internal/ZEGOCallInvitationManager.java#L72
//getCurrentUser 为示例代码封装方法https://github.com/ZEGOCLOUD/zegocloud_sdk_demo_android/blob/dd8062399252502f3f91b46ae2147a43160f73b5/best_practice/app/src/main/java/com/zegocloud/demo/bestpractice/internal/sdk/express/ExpressService.java#L638
ZEGOSDKUser localUser = ZEGOSDKManager.getInstance().expressService.getCurrentUser();
if (localUser == null) {
return;
}
for (LiveAudioRoomSeat seat : seatList) {
int seatIndex = seat.seatIndex;
ZEGOSDKUser seatUser = seat.getUser();
if (seatUser != null) {
String seatUserID = seatUser.userID;
if (Objects.equals(userID, seatUserID)) {
leaveSeat(seatIndex, callback);
break;
}
}
}
}
如果您不打算实现更换麦位的功能,请忽略此部分。
当发言人从一个麦位切换到另一个麦位时,例如,发言人 A 从 2 号麦位切换到 3 号麦位,他需要先删除与 2 号麦位对应的房间属性(离开 2 号麦位),然后将与 3 号麦位对应的房间属性的值设置为自己的 userID
(占用 3 号麦位)。这个过程涉及两个步骤。考虑以下极端情况:
当发言人 A 刚刚完成第一步(删除与 2 号麦位对应的房间属性并离开 2 号麦位)时,发言人 B 先于发言人 A 占用了 3 号麦位,导致发言人 A 成功离开了 2 号麦位,但无法占用 3 号麦位。
在这种情况下,发言人 A 失去了发言人麦位,显然不符合预期。
为了处理这种情况,您需要在发言人 A 完成两步操作之前,防止其他用户对相关发言人麦位进行操作。这可以通过使用组合操作的特性来实现:
// 1. 开始组合操作。
ZIMRoomAttributesBatchOperationConfig config = new ZIMRoomAttributesBatchOperationConfig();
config.isForce = true;
config.isDeleteAfterOwnerLeft = false;
config.isUpdateOwner = false;
zim.beginRoomAttributesBatchOperation(mRoomID, config);
// 2. 操作 1:离开 2 号麦位
List<String> keys = Collections.singletonList(String.valueOf(3));
ZIMRoomAttributesDeleteConfig config = new ZIMRoomAttributesDeleteConfig();
zim.deleteRoomAttributes(keys, mRoomID, config, callback);
// 3. 操作 2:占用 3 号麦位
String key = String.valueOf(2);
String value = localUser.userID;
ZIMRoomAttributesSetConfig config = new ZIMRoomAttributesSetConfig();
config.isDeleteAfterOwnerLeft = true;
config.isForce = false;
HashMap<String, String> attributes = new HashMap<>();
attributes.put(key, value);
zim.setRoomAttributes(attributes, mRoomID, config, callback);
// 4. 结束组合操作。
zim.endRoomAttributesBatchOperation(mRoomID, callback);
完整的参考代码如下:
public void switchSeat(int fromSeatIndex, int toSeatIndex, ZIMRoomAttributesBatchOperatedCallback callback) {
AudioRoomUser localUser = ZEGOSDKManager.getInstance().expressService.getLocalUser();
if (localUser == null) {
return;
}
if (!batchOperation) {
//beginRoomPropertiesBatchOperation 为示例代码封装方法 https://github.com/ZEGOCLOUD/zegocloud_sdk_demo_android/blob/dd8062399252502f3f91b46ae2147a43160f73b5/best_practice/app/src/main/java/com/zegocloud/demo/bestpractice/internal/sdk/zim/ZIMService.java#L472
ZEGOSDKManager.getInstance().zimService.beginRoomPropertiesBatchOperation();
batchOperation = true;
tryTakeSeat(toSeatIndex, null);
leaveSeat(fromSeatIndex, null);
//endRoomPropertiesBatchOperation 为示例代码封装方法 https://github.com/ZEGOCLOUD/zegocloud_sdk_demo_android/blob/dd8062399252502f3f91b46ae2147a43160f73b5/best_practice/app/src/main/java/com/zegocloud/demo/bestpractice/internal/sdk/zim/ZIMService.java#L479
ZEGOSDKManager.getInstance().zimService.endRoomPropertiesBatchOperation(
new ZIMRoomAttributesBatchOperatedCallback() {
@Override
public void onRoomAttributesBatchOperated(String roomID, ZIMError errorInfo) {
batchOperation = false;
if (callback != null) {
callback.onRoomAttributesBatchOperated(roomID, errorInfo);
}
}
});
}
}
我们将房间模式定义为:自由模式和请求模式。
房主可以调用示例代码中的自定义方法 setRoomExtraInfo,在自由模式和请求模式之间切换。
其中房间附加信息(roomExtraInfo)分为以下两种模式:
{"lockseat":false}
{"lockseat":true}
lockSeat
的具体定义,参考 示例代码
完整的示例代码,请参考 RoomSeatService.java,其中主要示例代码如下。
public void setHostAndLockSeat() {
JSONObject extraInfoValueJson = audioRoomExtraInfo.getExtraInfoValueJson();
try {
ZEGOSDKUser localUser = ZEGOSDKManager.getInstance().expressService.getCurrentUser();
JSONObject jsonObject = new JSONObject(extraInfoValueJson.toString());
jsonObject.put(EXTRA_INFO_VALUE_HOST, localUser.userID);
jsonObject.put(EXTRA_INFO_VALUE_LOCK_SEAT, true);
//setRoomExtraInfo 为示例代码封装方法 https://github.com/ZEGOCLOUD/zegocloud_sdk_demo_android/blob/dd8062399252502f3f91b46ae2147a43160f73b5/best_practice/app/src/main/java/com/zegocloud/demo/bestpractice/internal/sdk/express/ExpressService.java#L817
ZEGOSDKManager.getInstance().expressService.setRoomExtraInfo(EXTRA_INFO_KEY, jsonObject.toString());
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
发送和取消上麦请求的实现方式相似,只是房间请求信令的类型不同。此处以发送为例,演示具体实现方式。
在示例代码中,从听众的角度来看,上麦请求按钮已放置在屏幕的右下角,当点击按钮时,将执行以下操作。
action_type
定义为RoomRequestAction.ACTION_REQUEST
。room_request_type
添加到 extendedData
中,并将其标记为REQUEST_TAKE_SEAT
。ZIM SDK
的sendMessage 接口。)上麦请求
按钮将切换为取消上麦
。完整的示例代码,请参考 TakeSeatButton.java,其中主要示例代码如下。
@Override
protected void afterClick() {
super.afterClick();
// ...
RoomRequestExtendedData extendedData = new RoomRequestExtendedData();
extendedData.roomRequestType = RoomRequestType.REQUEST_TAKE_SEAT;
// sendRoomRequest 为示例代码封装方法 https://github.com/ZEGOCLOUD/zegocloud_sdk_demo_android/blob/dd8062399252502f3f91b46ae2147a43160f73b5/best_practice/app/src/main/java/com/zegocloud/demo/bestpractice/internal/sdk/zim/ZIMService.java#L719
ZEGOSDKManager.getInstance().zimService.sendRoomRequest(hostUser.userID, jsonObject.toString(),
new RoomRequestCallback() {
@Override
public void onRoomRequestSend(int errorCode, String requestID) {
if (errorCode == 0) {
mRequestID = requestID;
}
}
});
// ...
}
public void sendRoomRequest(String receiverID, String extendedData, RoomRequestCallback callback) {
if (zimProxy.getZIM() == null || currentRoom == null || currentUser == null) {
return;
}
RoomRequest roomRequest = new RoomRequest();
roomRequest.receiver = receiverID;
roomRequest.sender = currentUser.userID;
roomRequest.extendedData = extendedData;
roomRequest.actionType = RoomRequestAction.ACTION_REQUEST;
byte[] bytes = roomRequest.toString().getBytes(StandardCharsets.UTF_8);
ZIMCommandMessage commandMessage = new ZIMCommandMessage(bytes);
zimProxy.sendMessage(commandMessage, currentRoom.roomID, ZIMConversationType.ROOM, new ZIMMessageSendConfig(),
new ZIMMessageSentCallback() {
@Override
public void onMessageAttached(ZIMMessage message) {
}
@Override
public void onMessageSent(ZIMMessage message, ZIMError errorInfo) {
if (errorInfo.code == ZIMErrorCode.SUCCESS) {
roomRequest.requestID = String.valueOf(message.getMessageID());
roomRequestMap.put(roomRequest.requestID, roomRequest);
}
//...
}
});
}
public void updateUI() {
// getRoomRequestByRequestID 为示例代码封装方法 https://github.com/ZEGOCLOUD/zegocloud_sdk_demo_android/blob/dd8062399252502f3f91b46ae2147a43160f73b5/best_practice/app/src/main/java/com/zegocloud/demo/bestpractice/internal/sdk/zim/ZIMService.java#L928
RoomRequest roomRequest = ZEGOSDKManager.getInstance().zimService.getRoomRequestByRequestID(mRequestID);
if (roomRequest == null) {
setText("申请上麦");
} else {
setText("取消上麦");
}
setBackgroundResource(R.drawable.bg_cohost_btn);
setCompoundDrawablesWithIntrinsicBounds(R.drawable.liveaudioroom_bottombar_cohost, 0, 0, 0);
}
完整的示例代码,请参考 RoomRequestListDialog.java ,其中主要示例代码如下。
在收到听众的上麦请求后,将其添加到请求列表中。
//addEventHandler 为示例代码封装方法 https://github.com/ZEGOCLOUD/zegocloud_sdk_demo_android/blob/dd8062399252502f3f91b46ae2147a43160f73b5/best_practice/app/src/main/java/com/zegocloud/demo/bestpractice/internal/sdk/zim/ZIMProxy.java#L67
ZEGOSDKManager.getInstance().zimService.addEventHandler(incomingRoomRequestListener);
// ...
incomingRoomRequestListener = new IZIMEventHandler() {
public void onInComingRoomRequestReceived(String requestID, String extendedData) {
RoomRequestExtendedData data = RoomRequestExtendedData.parse(extendedData);
if (data != null && data.roomRequestType == roomRequestType) {
RoomRequest request = ZEGOSDKManager.getInstance().zimService.getRoomRequestByRequestID(requestID);
if (request != null) {
seatRequestAdapter.addItem(request.sender);
}
}
}
// ...
}
在请求列表中,房主可以选择点击接受或拒绝其上麦请求。
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
// ...
agree.setOnClickListener(v -> {
//acceptRoomRequest 为示例代码封装方法 https://github.com/ZEGOCLOUD/zegocloud_sdk_demo_android/blob/dd8062399252502f3f91b46ae2147a43160f73b5/best_practice/app/src/main/java/com/zegocloud/demo/bestpractice/internal/sdk/zim/ZIMService.java#L760
ZEGOSDKManager.getInstance().zimService.acceptRoomRequest(protocol, new RoomRequestCallback() {
@Override
public void onRoomRequestSend(int errorCode, String requestID) {
}
});
});
disagree.setOnClickListener(v -> {
//rejectRoomRequest 为示例代码封装方法 https://github.com/ZEGOCLOUD/zegocloud_sdk_demo_android/blob/dd8062399252502f3f91b46ae2147a43160f73b5/best_practice/app/src/main/java/com/zegocloud/demo/bestpractice/internal/sdk/zim/ZIMService.java#L813
ZEGOSDKManager.getInstance().zimService.rejectRoomRequest(protocol, new RoomRequestCallback() {
@Override
public void onRoomRequestSend(int errorCode, String requestID) {
}
});
});
}
您可以监听 Express SDK 的 onRemoteMicStateUpdate 回调通知,以确定远程推流设备的麦克风设备是否正常工作或关闭,并根据相应的状态初步了解设备问题的原因。
当从 CDN 拉流时,不会触发此回调。
您可以监听 Express SDK 的 onRemoteSoundLevelUpdate 回调通知,以获取发言人声音的音量级别,详情请参考 音量变化与音频频谱。
恭喜您,完成上述步骤后,您已经实现了语聊房功能。
联系我们
文档反馈