声网的语聊房 PaaS 方案(聊天室 SDK)把 RTC 和 IM 打包提供,但它们在客户端仍然是两套独立的初始化流程。除此之外,房间的创建、列表、生命周期管理需要一个云服务层(声网示例里用的是内部云服务),生产环境要自己部署对应的服务端。
一. 接入前要准备的东西
声网 AppID 和 Token:登录 console.shengwang.cn 创建项目,获取 AppID。调试阶段可以从控制台生成有效期 24 小时的临时 Token,上线前必须自己部署 Token 服务,由服务端根据用户 ID 和频道名动态签发,不能把 App Certificate 写在客户端里。
IM 账号和 Token:聊天室 SDK 内置 Easemob IM,获取 IM 用户名和 IM Token。IM Token 和 RTC Token 是两套独立的鉴权体系,不能混用。
服务端:房间列表、房间创建记录、麦位状态持久化这些数据需要服务端支撑。声网示例项目提供了参考实现,但明确标注”仅供快速集成使用,不适用于商业用途”,上线前需要自行实现房间管理服务。
二. 两套 SDK 的初始化顺序
PaaS 方案接入的第一个容易踩的坑是初始化顺序。RtcEngine 和 IM ChatClient 有各自的初始化入口,推荐顺序是:先登录 IM,再初始化 RtcEngine。
第一步:登录 IM:
ChatClient.getInstance().loginWithToken(uid, imToken, object : CallBack {
override fun onSuccess() {
// IM 登录成功,再执行后续初始化
}
override fun onError(code: Int, error: String?) {
// 处理登录失败
}
})
IM 登录是异步的,成功回调之后才能操作 IM 聊天室(加入、发消息等)。进房流程里不要在 IM 登录成功前就调用 joinChatRoom,否则会报鉴权错误。
第二步:初始化 RtcEngine:
val config = RtcEngineConfig() config.mAppId = "YourAppId" config.mContext = applicationContext val rtcEngine = RtcEngineEx.create(config)
RtcEngine 初始化只需要 AppID,不需要 Token,Token 在加入频道(joinChannel)时才用到。初始化放在 Application 层,整个应用生命周期内只创建一次。
三. 创建和加入房间
房主创建房间
创建房间涉及两层操作:在服务端创建房间记录,以及让 RTC 和 IM 分别加入对应的频道/聊天室。
// 1. 通过服务端创建房间(获取 roomId、channelId 等信息)
voiceServiceProtocol.createRoom(roomInfo) { error, room ->
if (error != null) return@createRoom
// 2. 加入 RTC 频道
rtcEngine?.joinChannel(rtcToken, room.channelId, "", rtcUid)
// 3. 加入 IM 聊天室
ChatClient.getInstance().chatroomManager()
.joinChatRoom(room.chatroomId, object : ValueCallBack {
override fun onSuccess(value: ChatRoom?) { }
override fun onError(error: Int, errorMsg: String?) { }
})
}
RTC 加入频道和 IM 加入聊天室是两个独立的异步操作,两个都成功之后才算真正进房。建议用计数器或 CountDownLatch 等方式等两者都完成后再更新 UI 状态,避免麦位界面和消息通道一个就绪一个还没好的中间状态。
听众加入房间
听众侧不需要创建房间,直接拿到 roomId 后查询房间信息,再用相同的方式加入 RTC 频道和 IM 聊天室:
// 获取房间列表,找到目标房间
voiceServiceProtocol.fetchRoomList(page, size) { error, list ->
val room = list.find { it.roomId == targetRoomId } ?: return@fetchRoomList
// 加入 RTC 频道(听众角色)
rtcEngine?.joinChannel(rtcToken, room.channelId, "", rtcUid)
// 加入 IM 聊天室
ChatClient.getInstance().chatroomManager()
.joinChatRoom(room.chatroomId, callBack)
}
四. 麦位管理的接入逻辑
麦位控制是聊天室 SDK 的内置核心功能之一,SDK 提供了上麦、下麦、禁言、锁麦、换麦等麦位操作 API,底层通过 IM 信令实现状态同步,但开发者调用的是 SDK 封装好的麦位接口,不需要自己拼 IM 自定义消息。
PaaS 方案和 UIKit 在麦位这层的实质差异在于 UI:SDK 提供能力 API,但麦位的视觉呈现(卡片样式、状态动效、申请弹窗)需要自己实现。断线重连后 SDK 会重新同步麦位状态,并发冲突的处理也在 SDK 内部,不需要自己写状态机。具体 API 以声网聊天室 SDK 当前版本文档为准,见 麦位管理指南。
五. 退出和资源释放
退出房间要按顺序清理两套 SDK 的资源,遗漏任何一步都可能导致下次进房时出现状态残留:
// 1. 离开 RTC 频道 rtcEngine?.leaveChannel() // 2. 离开 IM 聊天室 ChatClient.getInstance().chatroomManager().leaveChatRoom(chatroomId) // 3. 通知服务端更新房间状态 voiceServiceProtocol.leaveRoom(roomId) // 如果是房主销毁房间,还需要额外销毁聊天室 // ChatClient.getInstance().chatroomManager().destroyChatRoom(chatroomId, callBack) // 4. 应用退出时销毁 RtcEngine(不是每次退房都调,只在应用退出时调一次) RtcEngineEx.destroy()
RtcEngineEx.destroy() 是重量级操作,销毁后重新创建需要时间。如果用户频繁进出房间,不要每次退房都销毁,保留 RtcEngine 实例在应用层复用,退房只调 leaveChannel,下次进房直接 joinChannel。
六. 生产环境的服务端搭建要点
声网的聊天室 SDK 文档里明确说明:示例项目使用的云服务是声网内部实现,开发者在生产环境中需要自行实现同等能力的服务。最少需要实现以下几个服务端接口:
- Token 服务:根据用户 ID 和频道名签发 RTC Token,IM Token 的生成也需要服务端支持
- 房间管理服务:创建房间(写库)、房间列表查询(分页)、房间销毁(清理记录和 IM 聊天室)
- 用户鉴权:进房前验证用户身份,防止未授权用户绕过前端直接调 RTC joinChannel
如果业务里还有礼物系统、积分打赏,服务端还要增加对应的写入接口,这些都不在 SDK 范围内,需要自己实现。
七. PaaS 和 UIKit 的关键差异回顾
| 事项 | PaaS SDK | UIKit(AUIVoiceRoom) |
|---|---|---|
| RTC + IM 初始化 | 分别初始化,顺序自己管 | AUIVoiceRoomUIKit.init() 一次搞定 |
| UI | 完全自己实现 | AUIVoiceRoomView 开箱即用 |
| 麦位管理 | SDK 内置 API,UI 自己实现 | AScenesKit 内部处理,含 UI |
| 后端服务 | 生产环境全部自建 | 开源后端项目可直接部署(Spring Boot + Docker) |
| 灵活度 | 完全自由 | 受 AScenesKit 组件边界约束 |
