实现语聊房

  • Android

更新时间不可用

了解语聊房在不同平台上的语聊房方式,包括接入流程、关键配置和平台差异。

示例项目

本文介绍如何集成语聊 UIKit 来实现语聊房间的创建和销毁、用户进房和退房等功能。API-Examples-AUIKit 是极简语聊项目。你可以参考该项目了解本文提供的集成步骤和代码逻辑。

准备开发环境

前提条件

创建项目

如需创建新项目,在 Android Studio 里,依次选择 Phone and Tablet > Empty Activity,创建 Android 项目。

注意

创建项目后,Android Studio 会自动开始同步 gradle,稍等片刻至同步成功后再进行下一步操作。

集成依赖

本节介绍如何集成语聊项目所需的依赖库:

  1. 下载声网提供的 AUIVoiceRoom 仓库:

    Shell
    git clone git@github.com:AgoraIO-Community/AUIVoiceRoom.git
  2. 将 AUIVoiceRoom/Android/asceneskit 文件夹复制到与你的 app 文件夹同层级的位置。

  3. 将 AUIVoiceRoom/Android/app-voice/src/main/java/io/agora/app/voice/kit/AUIVoiceRoomUIKit.kt 文件复制到你的 <your_app>/app/src/main/java/<package_name> 文件夹中

  4. 在 <your_app>/setting.gradle.kts 文件中添加如下行:

    Kotlin
    dependencyResolutionManagement {
        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")
  5. 在 <your_app>/app/build.gradle.kts 文件中添加如下行:

    Kotlin
    dependencies {
        ...
    
        // 添加 asceneskit 依赖
        implementation(project(":asceneskit"))
    }
  6. 点击 Sync Now 按钮,等待 gradle 同步完成。

配置权限

在 <your_app>/app/Manifests/AndroidManifest.xml 文件中添加如下行,以完成如下设置:

  • 设置允许 App 访问网络、录音等权限。
  • (可选)如果你的后端服务 Host URL 不是 https 开头,设置允许 App 使用非加密的网络通信。
  • 声明 MainActivity 活动,即 App 的主要活动。
  • 声明 RoomActivity 活动,即 App 中语聊房房间的详情界面活动。同时,指定语聊房的窗口保持原始大小和布局,不受到软键盘的影响。
XML
<?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 主题:

XML
<resources>
    <!-- 将 VoiceRoomApp 改成你实际的项目名 -->
    <style name="Theme.VoiceRoomApp" parent="Theme.VoiceRoom.Light" />
</resources>

(可选)防止代码混淆

在 <your_app>/app/proguard-rules.pro 文件中添加如下行,以防止声网 SDK 的代码被混淆:

Kotlin
-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 引擎实例。如需使用其中任一一个实例,请通过该结构体进行传入,如果未传入,系统内部会自动创建。

Kotlin
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 默认使用 Jetpack Compose 构建 UI,但依然兼容 XML 布局。本文以 XML 布局为例进行演示。

参考如下步骤添加创建房间按钮:

  1. 新建 <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>
  2. 在 MainActivity.kt 文件中,添加如下高亮的几行,设置当前 Activity 使用 XML 布局,并为创建房间按钮设置点击事件的监听器。

    Kotlin
    class 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 {
    
            }
        }
    }
  3. 在上一步的 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 创建房间详情页

参考如下步骤创建房间详情页:

  1. 新建 <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" />
  2. 在 MainActivity.kt 文件所位于的目录下,新建 RoomActivity.kt 文件,并在该文件中添加如下行,从而实现一个房间详情页的活动类 RoomActivity。

    RoomActivity 通过接收传递的房间信息,在 onCreate 方法中显示房间的详细信息。通过使用静态方法 launch,可以方便地启动 RoomActivity 并传递房间信息。

    Kotlin
    class 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。
Kotlin
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 添加按钮:加入房间

参考如下步骤添加加入房间按钮:

  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="创建房间"/>
    
        <!-- highlight-start -->
        <Button
            android:id="@+id/btnJoinRoom"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="12dp"
            android:text="加入房间"/>
        <!-- highlight-end -->
    
    </LinearLayout>
  2. 在 findViewById<Button>(R.id.btnCreateRoom).setOnClickListener {...} 函数后添加如下高亮的几行,为加入房间按钮设置点击事件的监听器。

    Kotlin
    class 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,让听众进入到房间详情页。

Kotlin
// 添加点击加入房间按钮的事件
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。
Kotlin
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 系统键盘主动退出房间(听众)或销毁房间(房主)。

Kotlin
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 方法取消注册房间事件观测器。这样,在房间销毁时,观测器将不再接收房间相关事件。

Kotlin
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 函数的代码补全。

Kotlin
private fun destroyRoom() {
    // 退出或销毁房间
    AUIVoiceRoomUIKit.destroyRoom(roomInfo.roomId)
    // 取消注册房间事件观察者
    AUIVoiceRoomUIKit.unRegisterRespObserver(roomInfo.roomId, roomManagerRespObserver)
}

注意事项

声网在示例项目中提供的后端服务 Host URL(https://service.shengwang.cn/uikit-v2)仅供测试和快速集成语聊房使用,请勿用于商业用途。如需商用,请自行部署后端服务,并将客户端代码中的声网后端服务 Host URL 更新为你的服务 Host URL,详见部署后端服务。

下一步

在创建、进入房间后,你可以参考如下业务流程图开发后续的麦位管理、聊天、礼物等功能。