
语聊房 UIKit 的接入分两个阶段,体验完全不同。第一阶段是把 Demo 跑起来,通常半天到一天可以完成,引入依赖、填入 AppID 和 Token、挂载组件,界面就出来了。第二阶段是把它改成你要的样子,这一步需要花费较多的时间。例如哪些外观改配置就够,哪些布局需要动 UIKit 的源码,哪些功能点在 UIKit 的回调里可以接管,哪些根本不支持,边界不清楚,会在改造阶段反复踩坑。
声网的语聊房 UIKit 方案采用三层架构:最上面的应用层由开发者自己维护,中间的 AScenesKit(业务逻辑封装)和底层的 AUIKit(基础组件库)由声网维护。通常情况下只需要在应用层写业务代码,AScenesKit 和 AUIKit 直接引入即可。本文以Android示例,先走一遍接入主流程,再重点说定制层级的划分逻辑。
一. 接入前要准备的五样东西
声网 AppID 和 App Certificate:登录 console.shengwang.cn 创建项目,获取 AppID 和 App Certificate。Authentication 机制选 Security Mode(而非 Debug Mode),上线环境必须用安全模式。
RTM 服务:在控制台开通 RTM(Real-Time Messaging)服务,配置服务区域时同时开启 Storage 和 Lock 两个功能。需要注意的是:服务区域一旦设置无法修改,选区域前确认好业务覆盖范围。
RESTful API 凭证:在控制台 Settings → RESTful API 页面生成 Customer ID 和 Customer Secret。这组凭证只能下载一次,丢失后需要重新生成。
EasemobIM 账号:AUIVoiceRoom 的 IM 消息(弹幕、打赏通知、系统消息)走 Easemob IM,需要单独注册 Easemob 账号并开通应用,获取 IM App Key、Client ID、Client Secret。。
后端服务:Token 签发、房间管理、用户管理等服务端逻辑由 AUIKit 提供的开源后端项目支撑(Spring Boot + Redis + MongoDB)。联调阶段可以用声网测试地址,上线前要自己部署。Docker 部署方式见第四节。
二. Android 接入步骤
环境要求
- Android Studio 3.5 及以上(建议 Android Studio Giraffe 2022.3.1)
- Android 手机 API Level 21(Android 5.0)及以上
- JDK 已安装,网络无防火墙限制
第一步:引入 AScenesKit 模块
AUIVoiceRoom 不通过 Maven 发布,需要手动复制模块到项目里:
git clone git@github.com:AgoraIO-Community/AUIVoiceRoom.git
克隆完成后,将 AUIVoiceRoom/Android/asceneskit 文件夹整体复制到你项目的 app 同级目录下。同时把 app-voice/src/main/java/io/agora/app/voice/kit/AUIVoiceRoomUIKit.kt 复制到你项目的包名文件夹里。
在 settings.gradle.kts 里加上 jitpack 仓库和 asceneskit 模块引用:
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = java.net.URI.create("https://www.jitpack.io") }
}
}
include(":app")
include(":asceneskit")
在 app/build.gradle.kts 的 dependencies 里添加:
implementation(project(":asceneskit"))
点击 Sync Now 等待同步完成。
第二步:配置权限和主题
AndroidManifest.xml 中添加麦克风和网络权限:
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
房间页的 Activity 还需要配置:
<activity
android:name=".RoomActivity"
android:windowSoftInputMode="adjustNothing"
android:screenOrientation="portrait" />
values/themes.xml 里设置主题,继承 AUIKit 提供的默认主题:
<style name="Theme.VoiceRoomApp" parent="Theme.VoiceRoom.Light" />
proguard-rules.pro 中防止混淆:
-keep class io.agora.**{*;}
-dontwarn javax.**
第三步:初始化
在 MainActivity 的 onCreate 里调用一次初始化,传入所有凭证:
AUIVoiceRoomUIKit.init(
AUICommonConfig().apply {
context = applicationContext
host = "<your_host_url>" // 后端服务地址
appId = "<your_app_id>"
appCert = "<your_app_cert>"
basicAuth = "<your_http_basic_auth>"
imAppKey = "<your_easemob_app_key>"
imClientId = "<your_easemob_client_id>"
imClientSecret = "<your_easemob_client_secret>"
owner = AUIUserThumbnailInfo().apply {
userId = "your_user_id"
userName = "your_user_name"
userAvatar = "https://your_avatar_url"
}
},
AUIAPIConfig()
)
host 字段联调阶段可以填 https://service.shengwang.cn/uikit-v2,但这个地址只用于测试,商业上线前换成自己部署的后端地址。
第四步:创建房间(房主视角)
构建房间信息,调用 createRoom:
val roomInfo = AUIRoomInfo().apply {
owner = AUIRoomContext.shared().currentUserInfo
roomId = UUID.randomUUID().toString()
roomName = "your_room_name"
micSeatCount = 8
micSeatStyle = MicSeatType.EightTag.value.toString()
}
AUIVoiceRoomUIKit.generateToken(roomInfo.roomId,
onSuccess = { roomConfig ->
AUIVoiceRoomUIKit.createRoom(roomInfo, roomConfig, roomView) { error, _ ->
if (error != null) finish()
}
},
onFailure = { finish() }
)
注意先调 generateToken 拿到 roomConfig 再创建房间,Token 由后端签发,不要在客户端本地生成。
第五步:加入房间(听众视角)
听众侧用 launchRoom 代替 createRoom,其余流程相同:
AUIVoiceRoomUIKit.generateToken(roomInfo.roomId,
onSuccess = { roomConfig ->
AUIVoiceRoomUIKit.launchRoom(roomInfo, roomConfig, roomView) { error, _ ->
if (error != null) finish()
}
},
onFailure = { finish() }
)
第六步:退出和销毁房间
主动退出时调用 destroyRoom,同时反注册监听器:
private fun destroyRoom() {
AUIVoiceRoomUIKit.destroyRoom(roomInfo.roomId)
AUIVoiceRoomUIKit.unRegisterRespObserver(roomInfo.roomId, roomManagerRespObserver)
}
被动退出(房主销毁房间时听众被踢)要用 Observer 监听:
private val roomManagerRespObserver = object : AUIVoiceRoomObserver {
override fun onRoomDestroy(roomId: String) {
destroyRoom()
finish()
}
}
// onCreate 里注册
AUIVoiceRoomUIKit.registerRespObserver(roomInfo.roomId, roomManagerRespObserver)
页面的 onBackPressed 里也要调 destroyRoom,否则返回键退出后 RTC 连接不会断开。
三. 房间 UI 层:AUIVoiceRoomView
语聊房的整个界面是一个 AUIVoiceRoomView,在布局文件里直接声明:
<io.agora.asceneskit.voice.AUIVoiceRoomView
android:id="@+id/roomView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
在 Activity 的 onCreate 里拿到这个 View 后,调用 setFragmentActivity(this) 绑定 Fragment 容器,然后把它传给 createRoom 或 launchRoom,剩余的麦位界面、弹幕区、操作栏渲染全部由 AScenesKit 内部处理。
UI 定制从两个层面入手:轻量定制(颜色、文案、按钮显示)走 AUIKit 的配置对象;深度改造(麦位卡片样式、自定义动效)需要修改 AScenesKit 和 AUIKit 的源码,改后每次跟随上游更新都需要手动合并改动。
四. 后端服务部署
AUIKit 的后端项目(Spring Boot + Redis + MongoDB)通过 Docker Compose 部署:
# 在项目根目录创建 .env 文件,填入声网和 Easemob 凭证 # 然后启动 docker compose up -d --build # 验证是否正常 curl http://localhost:8080/health/check
本地调试时,把客户端的 host 换成 http://<your_local_ip>:8080。生产环境部署前要把 Redis 和 MongoDB 配置切换到生产实例,并在 API 网关层做鉴权和限流。
五. 定制层级:改什么、在哪改
UIKit 的定制能力分三个层次,从浅到深,每深一层工作量增加一个量级:
层次一:配置项定制
通过 UIKit 暴露的配置对象修改,不需要改组件源码。通常覆盖:主色调和背景色、麦位排列方式(几排几列)、上麦流程(直接上麦还是需要申请审批)、各操作按钮的显示/隐藏、提示文案替换。这层改动风险最低,UIKit 版本升级后配置通常兼容。
层次二:回调接管
UIKit 在关键操作节点暴露回调(上麦、下麦、发礼物、发弹幕),开发者可以在回调里插入自己的业务逻辑(比如上麦前检查用户等级、礼物发送前扣除积分、弹幕发送前过滤敏感词)。UIKit 处理 UI 状态,业务逻辑你来控制。这层需要了解 UIKit 的回调时序,搞清楚哪些回调是拦截型(可以阻止默认行为)、哪些是通知型(只告知结果)。
层次三:源码改造
UIKit 开源的前提下,直接修改组件源码来实现配置项和回调覆盖不了的效果。比如完全重新设计麦位卡片的样式、在底部操作栏插入自定义按钮、修改礼物动效的渲染逻辑。这层的代价是后续 UIKit 版本升级时,每次都要把自己改动的部分手动合并,升级成本随改动量线性增加。改动前要判断这部分是不是长期稳定的功能,频繁变动的业务逻辑不适合直接写在 UIKit 源码里。
六. 常见问题
麦位数量配置:创建房间时 micSeatCount 设置麦位总数,micSeatStyle 控制布局样式(如 EightTag 对应 8 麦位样式),这两个参数要匹配,否则界面渲染会出现空位或截断。
麦克风权限:createRoom 和 launchRoom 都要先检查麦克风权限,文档里用的是 PermissionHelp(this).checkMicPerm() 做运行时权限请求,监听器角色技术上不需要麦克风,但统一在进房前申请可以避免后续申请上麦时的体验断点。
