本文将为您介绍如何快速跑通音视频通话 Demo。跟随本文档,您可以在 10 分钟内跑通 Demo,并最终体验一个包含完备 UI 界面的 1v1 视频交友功能。

前提条件
环境准备
安装 Android Studio。
两台以上 Android 5.0 的设备。
开通服务

跑通 Demo
步骤 1: 下载 Demo
git clone https://github.com/Tencent-RTC/video-chat-solution.git
步骤 2: 配置 Demo
1. 配置 SDKAppID 和 SecretKey (必须):打开
video-chat-solution/blob/main/android/app/src/main/java/io/trtc/uikit/demo/debug/GenerateTestUserSig.java 文件,将开通服务时获取到的 SDKAppID 和 SDKSecretKey 填入其中:

2. 配置美颜(可选):Demo 默认集成了腾讯基础美颜,需配置 License 方可生效。请参考 License 指引 获取 LicenseURL 和 LicenseKey,并将其填入
video-chat-solution/android/app/src/main/java/io/trtc/uikit/demo/debug/GenerateTestUserSig.java 文件中。

步骤 3: 运行 Demo
1. 导入推荐测试用户:为了方便体验,请运行项目
video-chat-solution/tools/import_users_to_video_chat.py 脚本(需填入您的 AppID 和 SecretKey)。该脚本将自动注册 6 个推荐测试账号,登录后即可在“交友”模块中看到他们。

2. 编译运行:请选择设备并运行 Demo。


3. Demo 运行成功:完成登录后,展示如下界面。
推荐用户列表 | 用户主页 | 消息列表 | 个人设置界面 |
![]() | ![]() | ![]() | ![]() |
步骤 4: 视频与聊天互动
1. 与推荐用户互动:您可以使用另一台设备 B 登录 “推荐用户” 的账号(以 UserID: VideoChatlinxiaoyu 为例),完成 1v1 聊天与视频互动。
设备 A : 登录账号 (UserID: Richard) 并关注推荐用户 “林小雨”,并进行聊天和视频互动:
登录账号 “Richard” | 关注推荐用户“林小雨” | 与“林小雨”视频通话 | 通话余额不足 | 与“林小雨”聊天 |
![]() | ![]() | ![]() | ![]() | ![]() |
设备 B : 登录账号(UserID: VideoChatlinxiaoyu) ,和 Richard 进行聊天与视频互动。
登录账号 “林小雨” | 进入设置界面 | 调整美颜 | 接听 “Richard” 的通话 | 与 “Richard”聊天 |
![]() | ![]() | ![]() | ![]() | ![]() |
2. 添加好友互动:您也可以在两个设备上登录不同的账号,通过“添加好友”功能进行互动。
选择“联系人”栏目 | 搜索目标用户并添加好友 | 在“联系人”选择该用户进行聊天互动 | ||
![]() | ![]() | ![]() | ![]() | ![]() |
快速接入
为方便您快速上线,您可以直接集成 Demo 中的 UI 组件完成 1v1 聊天、通话等核心功能 。
步骤 1: 集成组件
1. 从 GitHub 仓库下载 VideoChat Demo 源码,并拷贝
video-chat 和 tuikit 组件复制到您的工程目录下:

2. 在
settings.gradle 中添加以下代码,完成功能接入。// 引入上层应用模块include ':app'// 引入 1v1 视频交友界面模块include ':video-chat'// 引入美颜功能模块include ':tebeautykit'project(':tebeautykit').projectDir = file("./tuikit/tebeautykit")// 引入 IM 组件公共模块include ':timcommon'project(':timcommon').projectDir = file("./tuikit/timcommon")// 引入会话功能模块 (基础功能模块)include ':tuiconversation'project(':tuiconversation').projectDir = file("./tuikit/tuiconversation")// 引入聊天功能模块 (基础功能模块)include ':tuichat'project(':tuichat').projectDir = file("./tuikit/tuichat")// 引入关系链功能模块 (基础功能模块)include ':tuicontact'project(':tuicontact').projectDir = file("./tuikit/tuicontact")// 引入搜索功能模块(需要购买旗舰版或企业版套餐)include ':tuisearch'project(':tuisearch').projectDir = file("./tuikit/tuisearch")
步骤 2: 完成登录
登录鉴权后才能正常使用组件的功能。调用 LoginStore 的 login 接口,传入上文获取的 sdkAppID、userID、userSig 进行登录鉴权:
import io.trtc.tuikit.atomicxcore.api.CompletionHandlerimport io.trtc.tuikit.atomicxcore.api.login.LoginStoreLoginStore.shared.login(context,sdkAppID, // Int,控制台获取userID, // StringuserSig, // String,控制台或服务端生成object : CompletionHandler {override fun onSuccess() {// 登录成功,可跳转会话列表或聊天页}override fun onFailure(code: Int, desc: String) {// 登录失败,可弹框报错}})
注意:
步骤 3: 构建推荐用户列表界面


1. 添加界面:在您的项目中添加 “推荐的人列表 (MeetPage)”:
import com.tencent.qcloud.tuikit.videochat.page.meet.MeetPageclass MeetPageFragment : Fragment() {override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)addMeetPage()}private fun addMeetPage() {val meetPage = MeetPage(requireContext())val container = findViewById<FrameLayout>(R.id.meet_container)container.addView(meetPage, MATCH_PARENT, MATCH_PARENT)}}
2. 输入数据:您可以通过
setPageUsers 输入推荐用户列表,并监听上拉刷新(onLoadMoreRequested)在尾部追加用户数据、下拉刷新回调(onRefreshRequested)全量更新用户数据。import com.tencent.qcloud.tuikit.videochat.page.meet.MeetPagemeetPage.setListener(object : MeetPage.Listener {// 下拉刷新时触发该回调override fun onRefreshRequested() {// 全量更新用户列表fetchUsers { users, hasMore ->// 输入用户列表meetPage.setPageUsers(users, hasMore)}}// 上拉刷新时触发该回调。override fun onLoadMoreRequested() {// 尾部追加新用户列表fetchNextPage { users, hasMore ->// 输入用户列表meetPage.setPageUsers(users, hasMore)}}})
setPageUsers 详细说明:
fun setPageUsers(users: List<V2TIMUserFullInfo>, hasMore: Boolean)
参数 | 类型 | 说明 |
users | ||
hasMore | Boolean | 表示是否还有下一页: true: 还有下一页,滚到底会继续触发 onLoadMoreRequested() false:已无更多数据,滚到底不再触发回调 |
3. 切换布局(可选):MeetPage 提供了“卡片”和“列表”两种布局模式,您可以通过
setLayoutTemplate 选择您喜欢的布局。import com.tencent.qcloud.tuikit.videochat.page.meet.MeetPageclass MeetPageFragment : Fragment() {override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)addMeetPage()}private fun addMeetPage() {val meetPage = MeetPage(requireContext())// 设置布局meetPage.setLayoutTemplate(MeetPage.Template.LIST) // 设置布局为列表布局(默认)// meetPage.setLayoutTemplate(MeetPage.Template.GRID) // 设置布局为卡片布局val container = findViewById<FrameLayout>(R.id.meet_container)container.addView(meetPage, MATCH_PARENT, MATCH_PARENT)}}
setLayoutTemplate 详细说明:
fun setLayoutTemplate(template: MeetPage.Template)
参数 | 类型 | 说明 |
template | MeetPage.Template | 布局的类型: MeetPage.Template.LIST :列表布局。 MeetPage.Template.GRID :卡片布局。 |
步骤 4: 构建会话列表界面
会话列表只需要创建
ConversationPage 对象加入到自己的布局中即可。会话列表会从数据库中读取最近聊天记录,当用户点击聊天记录时,ConversationPage 会默认跳转到聊天界面。
示例代码如下:
val conversationPage = ConversationPage()// R.id.container 为您的布局中容纳会话列表 Fragment 的 FrameLayoutsupportFragmentManager.beginTransaction().add(R.id.container, conversationPage).commit()
步骤 5: 构建聊天界面
聊天界面用于展示和发送消息。您可通过如下方法构建界面:


示例代码如下:
Bundle param = new Bundle();param.putInt(TUIConstants.TUIChat.CHAT_TYPE, V2TIMConversation.V2TIM_C2C);val userId = "jack" // 对方的 UserIdparam.putString(TUIConstants.TUIChat.CHAT_ID, userId);TUICore.startActivity("TUIC2CChatActivity", param);
步骤 6: 音视频通话
在社交娱乐场景中,由于通话需求复杂多样,我们推荐您使用 AtomicxCore SDK 来灵活定制您的通话场景。以 video-chat 模块的通话界(VideoCallPage)为例,它基于 SDK 的 CallStore 模块构建,内部已深度集成了美颜、关注操作以及对方信息展示等核心 UI 元素。您既可以将其作为源码参考,也可以直接接入项目中,从而快速搭建出专属的通话界面。

发起视频通话示例代码:您可通过 CallStore.calls 发起视频通话,并在成功回调中拉起通话界面。
// 通过 AtomicxCore SDK 的通话模块 CallStore.calls 发起视频通话。CallStore.shared.calls(list, mediaType, params, object : CompletionHandler {override fun onFailure(code: Int, desc: String) {completion?.onFailure(code, desc)}override fun onSuccess() {completion?.onSuccess()// 通话成功,拉起 VideoCallPage 通话界面val intent = Intent(context, VideoCallPage::class.java)intent.flags = Intent.FLAG_ACTIVITY_NEW_TASKcontext.startActivity(intent)}})
AtomicxCore SDK 通话模块核心 API 说明:
模块 | 功能描述 |
通话视图核心组件。自动监听 CallStore 数据并完成画面渲染,同时提供布局切换、头像与图标配置等 UI 定制化能力。 | |
通话生命周期管理:拨打电话、接通电话、拒接电话、挂断电话。实时获取参与通话人员音视频状态,通话计时、通话记录等数据。 | |
音视频设备控制:麦克风(开关 / 音量)、摄像头(开关 / 切换 / 画质)、屏幕共享,设备状态实时监听。 |
更多功能
您可以接入以下功能提高您 App 的核心竞争力,为您的用户带来更佳体验。
常见问题
为什么 Demo 的美颜不生效?
Demo 默认集成了美颜功能,但需要 License 鉴权。请您检查
video-chat-solution/android/app/src/main/java/io/trtc/uikit/demo/debug/GenerateTestUserSig.java 中的 LicenseURL、LicenseKey 是否正确。获取可参考 License 指引。
如何自定义 UI ?
若您的 UI 定制需求极高,我们推荐您:
1. 通话模块:您可以使用 AtomicxCore SDK (示例代码可参考:无 UI 接入)完成通话模块的界面定制,其核心模块如下:
模块 | 说明 |
登录管理类,用于处理用户登录、登出及用户信息管理等业务逻辑。 LoginStore 提供了一套完整的登录管理 API,包括用户登录、登出、设置个人信息等功能。 通过该类,可以管理用户的登录状态和用户资料。 | |
通话视图核心组件。自动监听 CallStore 数据并完成画面渲染,同时提供布局切换、头像与图标配置等 UI 定制化能力。 | |
通话生命周期管理:拨打电话、接通电话、拒接电话、挂断电话。实时获取参与通话人员音视频状态,通话计时、通话记录等数据。 | |
音视频设备控制:麦克风(开关 / 音量)、摄像头(开关 / 切换 / 画质)、屏幕共享,设备状态实时监听。 |
2. 聊天互动模块可参考:IM SDK 无 UI 接入。
如何自定义聊天通话消息上屏?
当通话状态发生改变时,IM 将下发通话状态变更的消息,信令协议如下:
问题 | 说明 |
如何判断 “发起通话” | actionType = 1 且 cmd = 'videoCall' 或 'audioCall' |
如何判断 “已接听” | actionType = 3 |
如何判断 “通话已结束”,“通话时长是多少” | actionType = 1 且 cmd = 'hangup',通话时长取 call_end 字段,单位:秒 |
如何判断 “取消通话” | actionType = 2 |
如何判断 “拒绝通话” | actionType = 4 |
如何判断对端忙线产生的 “拒绝通话” | actionType = 4 且 cmd = "line_busy" |
如何被叫判断 “超时未接听” | actionType = 5 |
含 UI 集成:您可以找到
tuikit/TUIChat/tuichat/src/main/java/com/tencent/qcloud/tuikit/tuichat/bean/CallModel.java 源码,修改 getContentForSimplifyAppearance 函数完成自定义通话状态消息上屏。/*** 定制 1v1 通话状态消息上屏文案** 修改位置:CallModel.java → getContentForSimplifyAppearance()* 只需修改 participantType == CALL_PARTICIPANT_TYPE_C2C 分支内的文案即可。** 以下字段在方法内可直接使用:* - protocolType:通话协议类型(发起/接听/拒绝/取消/挂断/超时/忙线)* - participantRole:当前用户角色(主叫 CALLER / 被叫 CALLEE)* - streamMediaType:媒体类型(语音 VOICE / 视频 VIDEO)* - duration:通话时长(秒)*/// =================== 示例:定制 1v1 通话上屏文案 ===================// 在 getContentForSimplifyAppearance() 方法中,// 找到 if (participantType == CALL_PARTICIPANT_TYPE_C2C) 分支,修改如下:if (participantType == CALL_PARTICIPANT_TYPE_C2C) {// 判断媒体类型,用于拼接文案前缀val mediaPrefix = if (streamMediaType == CALL_STREAM_MEDIA_TYPE_VIDEO) {"[视频通话]" // 填写您的自定义视频通话文案} else {"[语音通话]" // 填写您的自定义语音通话文案}val isCaller = (participantRole == CALL_PARTICIPANT_ROLE_CALLER)// display 为显示的通话状态上屏消息display = when (protocolType) {// 通话被拒绝(actionType = 4,不含 line_busy)CALL_PROTOCOL_TYPE_REJECT -> {if (isCaller) {"$mediaPrefix 对方已拒绝"} else {"$mediaPrefix 已拒绝"}}// 通话已取消(actionType = 2,主叫取消)CALL_PROTOCOL_TYPE_CANCEL -> {if (isCaller) {"$mediaPrefix 已取消"} else {"$mediaPrefix 对方已取消"}}// 通话结束(actionType = 1,cmd = "hangup")// duration 字段为通话时长,单位:秒CALL_PROTOCOL_TYPE_HANGUP -> {val formattedDuration = DateTimeUtil.formatSecondsTo00(duration)"$mediaPrefix 通话时长 $formattedDuration"}// 超时未接听(actionType = 5)CALL_PROTOCOL_TYPE_TIMEOUT -> {if (isCaller) {"$mediaPrefix 对方无应答"} else {"$mediaPrefix 未接听"}}// 忙线拒绝(actionType = 4,顶层 JSON 包含 "line_busy" 字段)CALL_PROTOCOL_TYPE_LINE_BUSY -> {if (isCaller) {"$mediaPrefix 对方忙线"} else {"$mediaPrefix 忙线未接听"}}// 发起通话(actionType = 1,cmd = "videoCall" 或 "audioCall")CALL_PROTOCOL_TYPE_SEND -> {"$mediaPrefix 发起通话"}// 已接听(actionType = 3)CALL_PROTOCOL_TYPE_ACCEPT -> {"$mediaPrefix 已接听"}else -> {context.getString(R.string.invalid_command)}}}
无 UI 集成:您可以设置 IM 消息监听器 实现通话状态消息上屏,示例代码如下:
// 设置消息监听器V2TIMManager.getMessageManager().addAdvancedMsgListener(advancedMsgListener)/*** 收到新消息* @param msg 消息*/override fun onRecvNewMessage(msg: V2TIMMessage) {// 获取信令信息,非信令消息返回 nullval signalingInfo = V2TIMManager.getSignalingManager().getSignalingInfo(msg)?: return// 只处理 1v1 单聊(groupID 为空表示单聊,非空表示群聊)if (!signalingInfo.groupID.isNullOrEmpty()) {return}// 解析信令的 data 字段(JSON 格式)val gson = Gson()val jsonData: Map<String, Any> = try {gson.fromJson(signalingInfo.data, object : TypeToken<Map<String, Any>>() {}.type)} catch (e: Exception) {return}// 校验 businessID,确认是通话信令消息val businessId = jsonData["businessID"] as? Stringif (businessId != "av_call" && businessId != "tuikit_calling") {return}// 提取 data 子对象中的 cmdval dataMap = jsonData["data"] as? Map<*, *>val cmd = dataMap?.get("cmd") as? String// 根据 actionType 判断通话状态when (signalingInfo.actionType) {// ========== actionType = 1:发起邀请 ==========V2TIMSignalingInfo.SIGNALING_ACTION_TYPE_INVITE -> {when (cmd) {// 发起视频通话"videoCall" -> {val mediaType = "video"Log.i(TAG, "发起通话 | 类型: $mediaType, 主叫: ${msg.sender}")}// 发起语音通话"audioCall" -> {val mediaType = "audio"Log.i(TAG, "发起通话 | 类型: $mediaType, 主叫: ${msg.sender}")}// 挂断(通话结束)"hangup" -> {// 通话时长从顶层 call_end 字段获取,单位:秒val duration = jsonData["call_end"]?.toString()?.toDoubleOrNull()?.toInt() ?: 0Log.i(TAG, "通话结束 | 时长: ${duration}秒")}}}// ========== actionType = 2:取消通话(主叫取消) ==========V2TIMSignalingInfo.SIGNALING_ACTION_TYPE_CANCEL_INVITE -> {Log.i(TAG, "通话已取消 | 主叫: ${msg.sender}")}// ========== actionType = 3:接听通话(被叫接受) ==========V2TIMSignalingInfo.SIGNALING_ACTION_TYPE_ACCEPT_INVITE -> {Log.i(TAG, "通话已接听")}// ========== actionType = 4:拒绝通话(被叫拒绝) ==========V2TIMSignalingInfo.SIGNALING_ACTION_TYPE_REJECT_INVITE -> {// 判断是否为忙线拒绝:顶层 JSON 包含 "line_busy" 字段if (jsonData.containsKey("line_busy")) {Log.i(TAG, "对方忙线拒绝")} else {Log.i(TAG, "通话被拒绝")}}// ========== actionType = 5:超时未接听 ==========V2TIMSignalingInfo.SIGNALING_ACTION_TYPE_INVITE_TIMEOUT -> {Log.i(TAG, "超时未接听")}}}
提高通话触达率?
1. 接入离线推送提高通话触达率:
中国大陆:接入 Notification
海外:接入 FCM
2. 提供推送控制:根据我们的经验,超过 90% 的用户接不到通话通知的原因是手动关闭了应用的通知权限。为确保通话可达率,我们强烈建议:
审慎发送通知:避免向用户推送低质量或无关联的通知,减少用户因骚扰而关闭通知权限的可能性。
提供精细控制:借鉴微信等优秀应用的做法,在应用设置内为用户提供独立的开关,分别控制“Notification”和“FCM Data Message”,而非引导用户去系统设置中一刀切地关闭所有通知。



















