CallKit 集成指南
CallKit 集成指南
功能概述
环信 CallKit 是一套基于环信即时通讯 IM(基于 IM 4.16.0 及以上)和声网 RTC 结合开发的音视频 UI 库。使用环信 CallKit 之前,你需要将其集成到你的应用中。


推荐开发环境
- Android SDK: API Level 24 及以上
- Android Studio: 推荐最新版本
- Kotlin: 2.0.21
- JDK: 17
- Gradle 版本: 8.13
前提条件
在集成 CallKit 之前,你需要完成以下准备工作:
- 在 环信控制台 进行如下操作:
- 集成环信即时通讯 IM SDK。
确保已集成环信 IM SDK 并完成登录。
集成步骤
步骤 1 添加依赖
方式一:(推荐)Gradle 远程依赖
- 在 Project 工程根目录下的
settings.gradle.kts
文件内,添加mavenCentral()
仓库:
pluginManagement {
repositories {
...
mavenCentral()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
...
mavenCentral()
}
}
- 在 app(module) 目录的
build.gradle.kts
文件中添加以下依赖:
dependencies {
...
implementation("io.hyphenate:chat-call-kit:4.16.0")
}
方式二:本地源码集成
从 GitHub 获取音视频 CallKit 源码,克隆到本地。按照以下步骤集成:
- 在 Project 工程根目录下的
settings.gradle.kts
文件中添加如下代码:
include(":ease-call-kit")
// "../easemob-callkit-android" 要替换成你克隆的实际工程路径,后边要拼接 "/ease-call-kit"
project(":ease-call-kit").projectDir = File("../easemob-callkit-android/ease-call-kit")
- 在 app(module) 目录的
build.gradle.kts
文件中添加如下代码:
dependencies {
...
implementation(project(":ease-call-kit"))
}
步骤 2 初始化 CallKit
在应用启动时(通常在 Application
或主 Activity
中)初始化 CallKit:
- 初始化 IM SDK。CallKit 基于即时通讯 IM 作为信令通道,因此需先初始化 IM SDK。
- 填入你的应用的 App Key。
- 设置即时通讯 IM SDK 中的一些选项(
EMOptions
类),例如,是否自动登录。
- 初始化 CallKit。你可以自定义铃声和通话超时时间。
在整个应用生命周期中,初始化一次即可。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initCallKit()
}
private fun initCallKit() {
// 1. 初始化环信 IM SDK
val options = ChatOptions().apply {
appKey = "your_app_key" // 替换为你的 App Key
autoLogin = false
}
ChatClient.getInstance().init(this, options)
ChatClient.getInstance().setDebugMode(true)
// 2. 初始化 CallKit
val config = CallKitConfig().apply {
// (可选)配置自定义铃声
incomingRingFile = "assets://incoming_ring.mp3"
outgoingRingFile = "assets://outgoing_ring.mp3"
dingRingFile = "assets://ding.mp3"
// (可选)配置通话超时时间(秒)
callTimeout = 30 // 30秒
}
CallKitClient.init(this, config)
}
}
步骤 3 配置监听器
环信 CallKit 提供 CallKitListener
监听通话过程。你可以在应用初始化时设置监听器用于处理通话相关的回调:
class MainActivity : AppCompatActivity() {
private val callKitListener = object : CallKitListener {
// 通话结束
override fun onEndCallWithReason(reason: CallEndReason, callInfo: CallInfo?) {
runOnUiThread {
when (reason) {
CallEndReason.CallEndReasonHangup -> {
showToast("通话已挂断")
}
CallEndReason.CallEndReasonCancel -> {
showToast("通话已取消")
}
CallEndReason.CallEndReasonRemoteRefuse -> {
showToast("对方拒绝通话")
}
CallEndReason.CallEndReasonRemoteNoResponse -> {
showToast("对方无响应")
}
// ... 其他结束原因
}
}
}
// 通话错误
override fun onCallError(
errorType: CallKitClient.CallErrorType,
errorCode: Int,
description: String?
) {
runOnUiThread {
showToast("通话错误: $description")
}
}
// 收到通话邀请
override fun onReceivedCall(userId: String, callType: CallType, ext: JSONObject?) {
runOnUiThread {
val typeStr = when (callType) {
CallType.SINGLE_VIDEO_CALL -> "视频通话"
CallType.SINGLE_VOICE_CALL -> "语音通话"
CallType.GROUP_CALL -> "群组通话"
}
showToast("收到来自 $userId 的$typeStr")
}
}
// 远端用户加入
override fun onRemoteUserJoined(userId: String, callType: CallType, channelName: String) {
runOnUiThread {
showToast("$userId 加入通话")
}
}
// 远端用户离开
override fun onRemoteUserLeft(userId: String, callType: CallType, channelName: String) {
runOnUiThread {
showToast("$userId 离开通话")
}
}
// RTC 引擎创建(可用于私有化部署配置)
override fun onRtcEngineCreated(engine: RtcEngine) {
// 如需私有化部署,在此处配置
}
}
private fun initCallKit() {
// ... 初始化代码
// 设置监听器
CallKitClient.callKitListener = callKitListener
}
}
步骤 4 登录 IM
ChatClient.getInstance().loginWithToken(username, token, object : ChatCallback {
override fun onSuccess() {
runOnUiThread {
showToast("登录成功")
}
}
override fun onError(code: Int, error: String?) {
runOnUiThread {
showToast("登录失败: $error")
}
}
})
步骤 5 发起通话
发起一对一通话
你可以使用 startSingleCall
方法发起一对一通话,CallType
设置为 SINGLE_VIDEO_CALL
为视频通话,SINGLE_VOICE_CALL
为音频通话。


- 发起一对一视频通话
private fun startVideoCall() {
// 检查登录状态
if (!ChatClient.getInstance().isLoggedInBefore) {
showToast("请先登录")
return
}
val targetUserId = "peer_user_id" // 对方用户 ID
val ext = JSONObject().apply {
put("customKey", "customValue") // 可选的扩展信息
}
CallKitClient.startSingleCall(
CallType.SINGLE_VIDEO_CALL,
targetUserId,
ext // 可传 null
)
}
- 发起一对一语音通话
private fun startVoiceCall() {
val targetUserId = "peer_user_id"
CallKitClient.startSingleCall(
CallType.SINGLE_VOICE_CALL,
targetUserId,
null
)
}
发起群组通话
- 创建群组:要发起群组通话,你需要首先创建群组,在群组中添加用户,详见 即时通讯 IM Android SDK 文档 或 环信控制台文档。
- 发起群组通话:指定群组 ID 后,CallKit 会自动拉起群成员选择界面,界面显示群组中的所有成员(群主、管理员、普通成员),用户可以选择要邀请的成员,选中人数会实时显示。为了保证通话质量和性能,CallKit 限制群组通话最多支持 16 人 同时参与(包括发起者)。若选择的成员数量超过 16 人时,系统会自动提示 “人数超出最大限制16人” 并阻止发起通话。
- 通话扩展信息:
ext
会在CallKitListener#onReceivedCall
中回调给接收方。 - 通话中邀请他人:群组通话中,当前用户可以点击通话界面右上角的邀请按钮向其他用户发起邀请。
private fun startGroupCall() {
// 群组 ID
val groupId = "your_group_id"
val ext = JSONObject().apply {
put("meetingTitle", "项目讨论会")
}
// ext 可传 null
CallKitClient.startGroupCall(groupId, ext)
}

步骤 6 接听通话
当接收到通话邀请时,CallKit 会自动触发 onReceivedCall
回调:
- 弹出通话邀请界面。
- 播放来电铃声。
- 显示通话邀请通知(当 App 在后台时)。
被叫用户可选择接听、拒绝或挂断通话。



步骤 7 离线推送
为保证被叫用户 App 在离线时也能收到通话请求,用户需开启离线推送。关于如何开启离线推送,请参见 开启 Android Push。开启离线推送后,用户在离线情况下收到呼叫请求时,其手机通知页面会弹出一条通知消息,用户点击该消息可唤醒 App 并进入振铃页面。
关于离线推送场景方案,请参见 Android 端离线推送文档。

进阶功能
用户信息
默认情况下,音视频通话时,对于用户信息,CallKit 会显示默认图像和用户 ID;对于群信息,CallKit 会根据群组 ID 从 SDK 中拉取群信息来对应显示群组名称和群图像。
如果要在一对一通话界面显示自定义用户头像和昵称,群聊通话显示自定义群图像和群名称,你可以通过 CallInfoProvider
实现自定义用户信息。
class MyCallInfoProvider : CallInfoProvider {
override fun asyncFetchUsers(
userIds: List<String>,
onValueSuccess: OnValueSuccess<List<CallKitUserInfo>>
) {
// 异步获取用户信息
GlobalScope.launch {
val userInfos = mutableListOf<CallKitUserInfo>()
userIds.forEach { userId ->
// 从你的用户系统获取用户信息
val userInfo = getUserFromApi(userId)
userInfos.add(
CallKitUserInfo().apply {
this.userId = userId
this.nickName = userInfo.nickname
this.avatar = userInfo.avatar
}
)
}
// 回调用户信息
onValueSuccess.onSuccess(userInfos)
}
}
override fun asyncFetchGroupInfo(
groupId: String,
onValueSuccess: OnValueSuccess<CallKitGroupInfo>
) {
// 异步获取群组信息
GlobalScope.launch {
val groupInfo = getGroupFromApi(groupId)
val callKitGroupInfo = CallKitGroupInfo().apply {
this.groupID = groupId
this.groupName = groupInfo.name
this.groupAvatar = groupInfo.avatar
}
onValueSuccess.onSuccess(callKitGroupInfo)
}
}
private suspend fun getUserFromApi(userId: String): UserInfo {
// 实现你的用户信息获取逻辑
return UserInfo(userId, "昵称", "头像URL")
}
private suspend fun getGroupFromApi(groupId: String): GroupInfo {
// 实现你的群组信息获取逻辑
return GroupInfo(groupId, "群组名称", "群组头像URL")
}
}
// 设置用户信息提供者
CallKitClient.callInfoProvider = MyCallInfoProvider()
自定义视频分辨率
环信 CallKit 中默认设置的分辨率为 1280x720。网络连接不稳定时,声网 RTC SDK 会主动降低分辨率或帧率。
若要修改本地摄像头视频采集的分辨率,可以在创建声网 RTC 引擎时进行配置。
private val callKitListener = object : CallKitListener {
override fun onRtcEngineCreated(engine: RtcEngine) {
val configuration= VideoEncoderConfiguration()
//例如,修改分辨率为 1280x720
configuration.dimensions= VD_1280x720
rtcEngine?.setVideoEncoderConfiguration(configuration)
}
// ... 其他回调
}
更多其他配置可以参考 声网 RTC 文档。
声网 RTC 私有化部署
若使用私有化的声网服务,可以在声网 RTC 引擎创建时进行配置。
详情可以参考 声网 RTC 文档。
private val callKitListener = object : CallKitListener {
override fun onRtcEngineCreated(engine: RtcEngine) {
// 私有化部署配置
val configuration = LocalAccessPointConfiguration().apply {
// 将 `111.111.111.111` 替换为你的私有化地址
ipList = arrayListOf("111.111.111.111")
// 将 `ap.xxx.agora.local` 替换为你的域名
verifyDomainName = "ap.xxx.agora.local"
mode = LOCAL_RPOXY_LOCAL_ONLY
}
engine.setLocalAccessPoint(configuration)
}
// ... 其他回调
}