1v1 视频交友

最近更新时间:2026-06-25 09:55:33

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


前提条件

环境准备

安装 Android Studio
两台以上 Android 5.0 的设备。

开通服务

请参见 开通服务,获取 SDKAppID 和 SDKSecretKey ,在后续的步骤(配置 Demo)中会用到。


跑通 Demo

步骤 1: 下载 Demo

您可以直接从 GitHub 下载 VideoChat 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 文件,将开通服务时获取到的 SDKAppIDSDKSecretKey 填入其中:



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-chattuikit 组件复制到您的工程目录下:



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.CompletionHandler
import io.trtc.tuikit.atomicxcore.api.login.LoginStore

LoginStore.shared.login(
context,
sdkAppID, // Int,控制台获取
userID, // String
userSig, // String,控制台或服务端生成
object : CompletionHandler {
override fun onSuccess() {
// 登录成功,可跳转会话列表或聊天页
}
override fun onFailure(code: Int, desc: String) {
// 登录失败,可弹框报错
}
}
)
注意:
在正式的生产环境中,建议在您的服务端生成 UserSig,在需要时由 App 向业务服务器发起请求获取动态 UserSig 进行鉴权。详见 服务端生成 UserSig

步骤 3: 构建推荐用户列表界面




1. 添加界面:在您的项目中添加 “推荐的人列表 (MeetPage)”:
import com.tencent.qcloud.tuikit.videochat.page.meet.MeetPage

class 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.MeetPage

meetPage.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
用户列表,V2TIMUserFullInfo 为包含用户的 ID、昵称、头像、性别等重要信息的结构体。
hasMore
Boolean
表示是否还有下一页:
true: 还有下一页,滚到底会继续触发 onLoadMoreRequested()
false:已无更多数据,滚到底不再触发回调
3. 切换布局(可选):MeetPage 提供了“卡片”和“列表”两种布局模式,您可以通过 setLayoutTemplate 选择您喜欢的布局。
import com.tencent.qcloud.tuikit.videochat.page.meet.MeetPage

class 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 的 FrameLayout
supportFragmentManager.beginTransaction()
.add(R.id.container, conversationPage)
.commit()

步骤 5: 构建聊天界面

聊天界面用于展示和发送消息。您可通过如下方法构建界面:



示例代码如下:
Bundle param = new Bundle();
param.putInt(TUIConstants.TUIChat.CHAT_TYPE, V2TIMConversation.V2TIM_C2C);
val userId = "jack" // 对方的 UserId
param.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_TASK
context.startActivity(intent)
}
})
AtomicxCore SDK 通话模块核心 API 说明:
模块
功能描述
通话视图核心组件。自动监听 CallStore 数据并完成画面渲染,同时提供布局切换、头像与图标配置等 UI 定制化能力。
CallStore
通话生命周期管理:拨打电话、接通电话、拒接电话、挂断电话。实时获取参与通话人员音视频状态,通话计时、通话记录等数据。
音视频设备控制:麦克风(开关 / 音量)、摄像头(开关 / 切换 / 画质)、屏幕共享,设备状态实时监听。

更多功能

您可以接入以下功能提高您 App 的核心竞争力,为您的用户带来更佳体验。
功能
描述
接入文档
通话计费
我们为您提供了精准的服务端通话状态回调,您可以监听通话开始和通话结束的服务端回调完成通话计费功能。
接入美颜
我们提供了两种美颜特效方案:基础美颜(内置)和高级美颜(需额外集成和付费)。您可以根据自己的需求选择合适的方案。
接入美声
TRTC 音频美声功能的实现方法和应用场景,帮助开发者为用户打造丰富多彩的音频互动体验。
接入 AI 实时翻译
应用出海必备功能,帮助您为用户打破语言壁垒。
音视频内容审核
1v1 社交娱乐应用必备功能,帮助您审核音视频媒体流、文字内容信息是否违规,防止应用被下架。

常见问题

为什么 Demo 的美颜不生效?

Demo 默认集成了美颜功能,但需要 License 鉴权。请您检查 video-chat-solution/android/app/src/main/java/io/trtc/uikit/demo/debug/GenerateTestUserSig.java 中的 LicenseURLLicenseKey 是否正确。获取可参考 License 指引


如何自定义 UI ?

若您的 UI 定制需求极高,我们推荐您:
1. 通话模块:您可以使用 AtomicxCore SDK (示例代码可参考:无 UI 接入)完成通话模块的界面定制,其核心模块如下:
模块
说明
登录管理类,用于处理用户登录、登出及用户信息管理等业务逻辑。 LoginStore 提供了一套完整的登录管理 API,包括用户登录、登出、设置个人信息等功能。 通过该类,可以管理用户的登录状态和用户资料。
通话视图核心组件。自动监听 CallStore 数据并完成画面渲染,同时提供布局切换、头像与图标配置等 UI 定制化能力。
CallStore
通话生命周期管理:拨打电话、接通电话、拒接电话、挂断电话。实时获取参与通话人员音视频状态,通话计时、通话记录等数据。
音视频设备控制:麦克风(开关 / 音量)、摄像头(开关 / 切换 / 画质)、屏幕共享,设备状态实时监听。
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) {
// 获取信令信息,非信令消息返回 null
val 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? String
if (businessId != "av_call" && businessId != "tuikit_calling") {
return
}

// 提取 data 子对象中的 cmd
val 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() ?: 0
Log.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”,而非引导用户去系统设置中一刀切地关闭所有通知。


联系我们

如果您在使用过程中,有什么建议或者意见,可以 联系我们,感谢您的反馈。