实现语聊房
本文介绍如何集成语聊 UIKit 来实现语聊房间的创建和销毁、用户进房和退房等功能。
示例项目
API-Examples-AUIKit
是极简语聊项目。你可以参考该项目了解本文提供的集成步骤和代码逻辑。
准备开发环境
前提条件
- Android Studio 3.5 及以上。本文以 Android Studio Giraffe | 2022.3.1 和 JBR 17.0.6 为例。
- Android 手机,版本 Android 5.0(API Level 21)及以上。
- 可以访问互联网的计算机。确保你的网络环境没有部署防火墙,否则无法正常使用声网服务。
- 开通服务。
创建项目
如需创建新项目,在 Android Studio 里,依次选择 Phone and Tablet > Empty Activity,创建 Android 项目。
创建项目后,Android Studio 会自动开始同步 gradle,稍等片刻至同步成功后再进行下一步操作。
集成依赖
本节介绍如何集成语聊项目所需的依赖库:
-
下载声网提供的 AUIVoiceRoom 仓库:
Shellgit clone git@github.com:AgoraIO-Community/AUIVoiceRoom.git
-
将
AUIVoiceRoom/Android/asceneskit
文件夹复制到与你的app
文件夹同层级的位置。 -
将
AUIVoiceRoom/Android/app-voice/src/main/java/io/agora/app/voice/kit/AUIVoiceRoomUIKit.kt
文件复制到你的<your_app>/app/src/main/java/<package_name>
文件夹中 -
在
<your_app>/setting.gradle.kts
文件中添加如下行:KotlindependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
// 添加 jitpack 仓库,拉取 auikit 库
maven { url = java.net.URI.create("https://www.jitpack.io") }
}
}
// 项目名需为你的 Android Studio 项目的真实名称
rootProject.name = "VoiceRoomApp"
include(":app")
// 添加 asceneskit 库到项目里
include(":asceneskit") -
在
<your_app>/app/build.gradle.kts
文件中添加如下行:Kotlindependencies {
...
// 添加 asceneskit 依赖
implementation(project(":asceneskit"))
} -
点击 Sync Now 按钮,等待 gradle 同步完成。
配置权限
在 <your_app>/app/Manifests/AndroidManifest.xml
文件中添加如下行,以完成如下设置:
- 设置允许 App 访问网络、录音等权限。
- (可选)如果你的后端服务 Host URL 不是 https 开头,设置允许 App 使用非加密的网络通信。
- 声明
MainActivity
活动,即 App 的主要活动。 - 声明
RoomActivity
活动,即 App 中语聊房房间的详情界面活动。同时,指定语聊房的窗口保持原始大小和布局,不受到软键盘的影响。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- 允许 App 访问互联网 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 允许 App 录音 -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- 允许 App 修改音频设置 -->
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<!-- 允许 App 访问网络状态 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- 允许 App 访问 Wi-Fi 状态 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- 允许 App 拨打电话 -->
<uses-permission android:name="android.permission.CALL_PHONE" />
<!-- 允许 App 使用非加密的网络通信-->
<!-- 仅在你的后端服务 host url 不是 https 开头时需要设置该权限 -->
<application
android:usesCleartextTraffic="true">
<!-- 声明 MainActivity 活动 -->
<activity
android:name=".MainActivity">
</activity>
<!-- 声明 RoomActivity 活动 -->
<activity
android:name=".RoomActivity"
android:windowSoftInputMode="adjustNothing" />
android:screenOrientation="portrait" />
</application>
</manifest>
配置主题
在 <your_app>/app/src/main/res/values/themes.xml
文件中添加如下行配置语聊房 UI 主题:
<resources>
<!-- 将 VoiceRoomApp 改成你实际的项目名 -->
<style name="Theme.VoiceRoomApp" parent="Theme.VoiceRoom.Light" />
</resources>
(可选)防止代码混淆
在 <your_app>/app/proguard-rules.pro
文件中添加如下行,以防止声网 SDK 的代码被混淆:
-keep class io.agora.**{*;}
-dontwarn javax.**
-dontwarn com.google.devtools.build.android.**
实现语聊房
AUIVoiceRoomUIKit
基于声网 RTC、RTM 引擎、AUIKit 等模块封装,帮助你轻松管理语聊房。
你可以使用 AUIVoiceRoomUIKit
类中的 API 实现语聊房间的创建和销毁、用户进房和退房等功能。
1. 初始化 AUIVoiceRoomUIKit
在 MainActivity.kt
文件中添加 onCreate
函数。在该函数中调用 AUIVoiceRoomUIKit
类的 init
方法并传入如下参数,以初始化 AUIVoiceRoomUiKit
:
-
AUICommonConfig
:基础配置,包含如下参数:context
:应用程序的上下文。owner
:设置你的业务的用户信息。- 声网项目的 App ID。
- 声网项目的 App 证书。
- 用于 HTTP 基本认证的
Authorization
。 - 环信 IM 服务的 App Key。
- 环信 IM 服务的 Client ID。
- 环信 IM 服务的 Client Secret。
- 语聊后端服务的 Host URL。
App ID 等参数取值可参考配置示例项目。
-
AUIAPIConfig
:声网 API 实例结构体,包含 RTC 引擎和 RTM 引擎实例。如需使用其中任一一个实例,请通过该结构体进行传入,如果未传入,系统内部会自动创建。
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 初始化 AUIVoiceRoomUIKit
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 {
// 生成一个随机的用户 ID
userId = (Random().nextInt(1000) + 10000).toString()
// 设置用户名
userName = "User-$userId"
// 设置用户头像
userAvatar = "https://accktvpic.oss-cn-beijing.aliyuncs.com/pic/sample_avatar/sample_avatar_1.png"
}
},
AUIAPIConfig()
)
}
}
2. 房主创建并加入房间
本节展示如何让房主创建并加入语聊房。
2.1 添加按钮:创建房间
Android Studio Giraffe 默认使用 Jeckpack Compose 构建 UI,但是依然兼容 XML 布局。本文以 XML 布局来做演示。
参考如下步骤添加创建房间按钮:
-
新建
<your_app>/app/src/main/res/layout/main_activity.xml
文件并在该文件中添加如下行,以增加一个创建房间按钮,用于让房主点击创建:XML<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<Button
android:id="@+id/btnCreateRoom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:text="创建房间"/>
</LinearLayout> -
在
MainActivity.kt
文件中,添加如下高亮的几行,设置当前 Activity 使用 XML 布局,并为创建房间按钮设置点击事件的监听器。Kotlinclass MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 设置使用 XML 布局
setContentView(R.layout.main_activity)
// 初始化 AUIVoiceRoomUIKit
AUIVoiceRoomUIKit.init(
...
)
// 添加点击创建房间按钮的事件
findViewById<Button>(R.id.btnCreateRoom).setOnClickListener {
}
}
} -
在上一步的
setOnClickListener
函数中添加如下高亮行,创建roomInfo
(房间信息)参数,启动RoomActivity
活动。在后续步骤中会创建
RoomActivity
并添加代码逻辑,用RoomActivity
来显示房间详情界面,同时调用创建房间逻辑。Kotlin// 添加点击创建房间按钮的事件
findViewById<Button>(R.id.btnCreateRoom).setOnClickListener {
// 随机生成房间名
val roomName = "${Random().nextInt(100) + 1000}"
// 创建房间
val roomInfo = AUIRoomInfo()
roomInfo.owner = AUIRoomContext.shared().currentUserInfo
roomInfo.roomId = UUID.randomUUID().toString()
roomInfo.thumbnail = "https://accktvpic.oss-cn-beijing.aliyuncs.com/pic/sample_avatar/sample_avatar_1.png"
roomInfo.roomName = roomName
roomInfo.micSeatCount = 8
roomInfo.micSeatStyle = MicSeatType.EightTag.value.toString()
// 跳转到房间详情页
RoomActivity.launch(this@MainActivity, roomInfo, isCreate = true)
}
2.2 创建房间详情页
参考如下步骤创建房间详情页:
-
新建
<your_app>/app/src/main/res/layout/room_activity.xml
文件并在该文件中添加如下行,以添加语聊房的详情页面。XML<?xml version="1.0" encoding="utf-8"?>
<io.agora.asceneskit.voice.AUIVoiceRoomView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/roomView"
android:layout_width="match_parent"
android:layout_height="match_parent" /> -
在
MainActivity.kt
文件所位于的目录下,新建RoomActivity.kt
文件,并在该文件中添加如下行,从而实现一个房间详情页的活动类RoomActivity
。RoomActivity
通过接收传递的房间信息,在onCreate
方法中显示房间的详细信息。通过使用静态方法launch
,可以方便地启动RoomActivity
并传递房间信息。Kotlinclass RoomActivity : FragmentActivity() {
companion object {
const val EXTRA_ROOM_INFO = "RoomInfo"
const val EXTRA_IS_CREATE = "isCreate"
// 启动 RoomActivity
fun launch(context: Context, roomInfo: AUIRoomInfo, isCreate: Boolean){
// 创建一个 Intent 对象,指定启动 RoomActivity 的目标上下文和 RoomActivity 类名
val intent = Intent(context, RoomActivity::class.java)
// 将房间信息作为额外的数据添加到 Intent 中,使用 EXTRA_ROOM_INFO 作为键名
intent.putExtra(EXTRA_ROOM_INFO, roomInfo)
// 将是否创建房间信息作为额外的数据添加到 Intent 中,使用 EXTRA_IS_CREATE 作为键名
intent.putExtra(EXTRA_IS_CREATE, isCreate)
// 使用目标上下文启动 RoomActivity
context.startActivity(intent)
}
}
// 房间信息
private val roomInfo by lazy {
// 从 Intent 中获取传递的房间信息
intent.getSerializableExtra(EXTRA_ROOM_INFO) as AUIRoomInfo
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 设置使用 XML 布局
setContentView(R.layout.room_activity)
// 初始化房间详情页布局
val roomView = findViewById<AUIVoiceRoomView>(R.id.roomView)
roomView.setFragmentActivity(this@RoomActivity)
// 是否创建房间信息
val isCreate = intent.getBooleanExtra(EXTRA_IS_CREATE, false)
}
}
2.3 启动房间
在 RoomActivity
的 onCreate
函数中增加如下高亮的几行。调用 AUIVoiceRoomUIKit
类的 createRoom
方法并传入以下参数,以启动语聊房:
roomInfo
:房间信息。包含如下:roomId
:房间 ID,每个房间的唯一标识。roomName
:房间名称。不同房间的名称可以重名。owner
:房主信息。
roomConfig
:房间配置,包含房间名、Token 等配置。你可以使用AUIVoiceRoomUIKit.generateToken
方法生成 Token,也可以使用自己的 Token 后台服务生成 Token。roomView
:房间的 UI View。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 设置使用 XML 布局
setContentView(R.layout.room_activity)
// 初始化房间详情页布局
val roomView = findViewById<AUIVoiceRoomView>(R.id.roomView)
roomView.setFragmentActivity(this@RoomActivity)
// 是否创建房间信息
val isCreate = intent.getBooleanExtra(EXTRA_IS_CREATE, false)
// 检查并申请运行时权限
PermissionHelp(this).checkMicPerm(
// 获取到权限
granted = {
// 生成 Token
AUIVoiceRoomUIKit.generateToken(roomInfo.roomId,
onSuccess = { roomConfig ->
// 启动房间
if (isCreate) {
// 房主创建并加入房间
AUIVoiceRoomUIKit.createRoom(roomInfo, roomConfig, roomView) { error, _ ->
if (error != null) {
// 创建并加入房间失败
finish()
}
}
}
},
onFailure = {
// 生成 Token 失败
finish()
}
)
},
unGranted = {
// 没有获取到权限
finish()
}
)
}
3. (可选)听众加入房间
本节展示如何让听众加入已存在的房间。
3.1 添加按钮:加入房间
参考如下步骤添加加入房间按钮:
-
在
main_activity.xml
中添加如下高亮的几行,添加一个加入房间的按钮,以用于让听众点击加入房间。XML<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<Button
android:id="@+id/btnCreateRoom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:text="创建房间"/>
<Button
android:id="@+id/btnJoinRoom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:text="加入房间"/>
</LinearLayout> -
在
findViewById<Button>(R.id.btnCreateRoom).setOnClickListener {...}
函数后添加如下高亮的几行,为加入房间按钮设置点击事件的监听器。Kotlinclass MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
...
// 初始化 AUIVoiceRoomUIKit
AUIVoiceRoomUIKit.init(
...
)
// 添加点击创建房间按钮的事件
findViewById<Button>(R.id.btnCreateRoom).setOnClickListener {
...
}
// 添加点击加入房间按钮的事件
findViewById<Button>(R.id.btnJoinRoom).setOnClickListener {
// 弹出房间名输入弹窗
val input = EditText(this@MainActivity)
input.hint = "Room Name"
AlertDialog.Builder(this@MainActivity)
.setTitle("Launch Room")
.setView(input)
.setPositiveButton("Confirm") { dialog: DialogInterface, i: Int ->
...
}
.setNegativeButton("Cancel") { dialog: DialogInterface, i: Int ->
dialog.dismiss()
}
.show()
}
}
}
3.2 获取房间列表
在 findViewById<Button>(R.id.btnJoinRoom).setOnClickListener {
代码中添加如下高亮的几行,调用 AUIVoiceRoomUIKit
类的 getRoomList
方法并传入如下参数,以获取语聊房间列表:
lastCreateTime
:房间列表的起始时间(毫秒)。例如,1681879844085。pageSize
:每一页房间列表所展示的房间数量。
如果在房间列表中找到匹配的房间名,并获取到对应的 roomInfo
信息,则执行 launch
函数启动 RoomActivity
,让听众进入到房间详情页。
// 添加点击加入房间按钮的事件
findViewById<Button>(R.id.btnJoinRoom).setOnClickListener {
// 弹出房间名输入弹窗
val input = EditText(this@MainActivity)
input.hint = "Room Name"
AlertDialog.Builder(this@MainActivity)
.setTitle("Launch Room")
.setView(input)
.setPositiveButton("Confirm") { dialog: DialogInterface, i: Int ->
// 确认加入房间
val roomName = input.text.toString()
if (TextUtils.isEmpty(roomName)) {
Toast.makeText(this@MainActivity, "Room name is empty", Toast.LENGTH_SHORT).show()
dialog.dismiss()
return@setPositiveButton
}
// 查询房间信息
AUIVoiceRoomUIKit.getRoomList(0, 50,
success = { list ->
val roomInfo = list.findLast { it.roomName == roomName }
if (roomInfo == null) {
Toast.makeText(this@MainActivity, "The room is unavailable. RoomName=$roomName", Toast.LENGTH_SHORT).show()
dialog.dismiss()
return@getRoomList
}
// 跳转到房间详情页
RoomActivity.launch(this@MainActivity, roomInfo, false)
},
failure = {
Toast.makeText(
this@MainActivity,
"Get room list failed. ${it.code} - ${it.message}",
Toast.LENGTH_SHORT
).show()
dialog.dismiss()
}
)
}
.setNegativeButton("Cancel") { dialog: DialogInterface, i: Int ->
dialog.dismiss()
}
.show()
}
3.3 听众加入房间
在中 AUIVoiceRoomUIKit.generateToken(
代码中添加如下高亮的几行代码。在判断用户为听众时,调用 AUIVoiceRoomUIKit
类的 launchRoom
方法并传入以下参数,以让听众加入房间:
roomInfo
:房间信息。包含如下:roomId
:房间 ID,每个房间的唯一标识。roomName
:房间名称。不同房间的名称可以重名。owner
:房主信息。
roomConfig
:房间配置,包含房间名、Token 等配置。你可以使用AUIVoiceRoomUIKit.generateToken
方法生成 Token,也可以使用自己的 Token 后台服务生成 Token。roomView
:房间的 UI View。
AUIVoiceRoomUIKit.generateToken(roomInfo.roomId,
onSuccess = { roomConfig ->
// 启动房间
if (isCreate) {
// 房主创建并加入房间
AUIVoiceRoomUIKit.createRoom(roomInfo, roomConfig, roomView) { error, _ ->
if (error != null) {
// 创建并加入房间失败
finish()
}
}
}
else {
// 听众加入房间
AUIVoiceRoomUIKit.launchRoom(roomInfo, roomConfig, roomView) { error, _ ->
if (error != null) {
// 加入房间失败
finish()
}
}
}
},
onFailure = {
// 生成 Token 失败
finish()
}
)
4. (可选)退出或销毁房间
本节展示如何退出并销毁房间。
在语聊房中,房主可以主动销毁房间,房间销毁时听众会被动退出房间。房间存在时,听众可以主动退出房间,也可以被房主踢出房间。下面从主动和被动的角度展示代码逻辑:
4.1 主动退出或销毁房间
在 RoomActivity
中添加如下高亮的几行,即可实现通过点击按钮或使用 Android 系统键盘主动退出房间(听众)或销毁房间(房主)。
class RoomActivity : FragmentActivity() {
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 设置使用 XML 布局
setContentView(R.layout.room_activity)
// 初始化房间详情页布局
val roomView = findViewById<AUIVoiceRoomView>(R.id.roomView)
roomView.setFragmentActivity(this@RoomActivity)
// 点击退出房间按钮时
roomView.setOnShutDownClick {
// 主动退出房间
destroyRoom()
finish()
}
// 是否创建房间信息
val isCreate = intent.getBooleanExtra(EXTRA_IS_CREATE, false)
// 检查并申请运行时权限
PermissionHelp(this).checkMicPerm(
...
)
}
// 使用 Android 系统键盘返回主页时
override fun onBackPressed() {
// 主动退出房间
destroyRoom()
super.onBackPressed()
}
}
4.2 被动退出房间
在 RoomActivity
中添加如下高亮的几行。通过 AUIVoiceRoomUIKit
类的 registerRespObserver
方法注册房间事件观测器。在观测器中添加 onRoomDestroy
回调来监听房间销毁事件。当房间销毁事件发生时,执行 destroyRoom
函数。
在 destroyRoom
函数中,调用 AUIVoiceRoomUIKit
类的 unRegisterRespObserver
方法取消注册房间事件观测器。这样,在房间销毁时,观测器将不再接收房间相关事件。
class RoomActivity : FragmentActivity() {
...
// 房间信息
private val roomInfo by lazy {
// 从 Intent 中获取传递的房间信息
intent.getSerializableExtra(EXTRA_ROOM_INFO) as AUIRoomInfo
}
// 房间事件观察者
private val roomManagerRespObserver = object : AUIVoiceRoomObserver {
override fun onRoomDestroy(roomId: String) {
super.onRoomDestroy(roomId)
// 房间被销毁
destroyRoom()
finish()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
...
// 检查并申请运行时权限
PermissionHelp(this).checkMicPerm(
...
)
// 注册房间事件观察者
AUIVoiceRoomUIKit.registerRespObserver(roomInfo.roomId, roomManagerRespObserver)
}
// 系统键盘返回主动退出房间时
override fun onBackPressed() {
destroyRoom()
super.onBackPressed()
}
private fun destroyRoom() {
// 取消注册房间事件观察者
AUIVoiceRoomUIKit.unRegisterRespObserver(roomInfo.roomId, roomManagerRespObserver)
}
}
4.3 补全销毁逻辑
在主动退出或销毁房间、被动退出房间的情况下,都会执行 destroyRoom
函数。
在 destroyRoom
函数中,添加如下高亮的几行。调用 AUIVoiceRoomUIKit
类的 destroyRoom
方法并传入房间 ID,以退出或销毁房间。至此,destroyRoom
函数的代码补全。
private fun destroyRoom() {
// 退出或销毁房间
AUIVoiceRoomUIKit.destroyRoom(roomInfo.roomId)
// 取消注册房间事件观察者
AUIVoiceRoomUIKit.unRegisterRespObserver(roomInfo.roomId, roomManagerRespObserver)
}
注意事项
声网在示例项目中提供的后端服务 Host URL(https://service.shengwang.cn/uikit-v2
)仅供测试和快速集成语聊房使用,请勿用于商业用途。如需商用,请自行部署后端服务,并将客户端代码中的声网后端服务 Host URL 更新为你的服务 Host URL,详见部署后端服务。
下一步
在创建、进入房间后,你可以参考如下业务流程图开发后续的麦位管理、聊天、礼物等功能。