CallSDK Android 接入文档
1. 概述
CallSDK 是一款 Android 通话 SDK,提供通话初始化、拨号、通话控制(挂断 / 静音 / 扬声器 / DTMF)、外显号码配置等核心功能。
核心特性
- 支持音频通话(SIP 协议)
- 支持两种通话场景:主动外呼 和 服务端发起通话
- 外显号码组 / 号码配置
- 通话状态监听(振铃 / 接听 / 挂断 / 失败)
- 通话控制(静音、扬声器、DTMF 发送)
通话场景说明
| 场景 | 说明 | 触发方式 |
|---|---|---|
| 主动外呼 | 开发者调用 makeCall() 发起通话 |
开发者主动调用 |
| 服务端发起通话 | 服务端调度通话任务,SDK 自动接听 SIP | SDK 自动处理,通过 OnServerCallListener 通知开发者 |
架构概览
┌──────────────────────────────────────────────────────────┐
│ CallSDK │
│ │
│ ① InitListener → 初始化结果 │
│ ② OnServerCallListener → 服务端通话通知(开发者打开页面) │
│ ③ CallStateListener → 通话状态变化(开发者更新 UI) │
│ ④ MakeCallCallback → 外呼发起结果 │
│ ⑤ setOnKickedListener → 被踢下线通知 │
└──────────────────────────────────────────────────────────┘
2. 环境准备
2.1 权限声明
在 AndroidManifest.xml 中添加必要权限:
<!-- 网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- 音频/通话权限 -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<!-- 蓝牙耳机相关 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!-- Android 12+ 需要 BLUETOOTH_CONNECT 权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
注意:
RECORD_AUDIO需要动态申请,没有权限拨打会失败。
2.2 依赖引入
- 将 版本更新日志 中最新的
callsdk-xxx.aar拷贝至app/libs目录 - 在模块
build.gradle中配置:
android {
repositories {
flatDir {
dirs 'libs'
}
}
}
dependencies {
implementation(name: 'callsdk-xxx', ext: 'aar')
// 如果项目中没有引入 okhttp,需要加入
implementation 'com.squareup.okhttp3:okhttp:4.9.3'
}
3. 快速集成
所有回调均在主线程触发,可直接更新 UI。
3.1 初始化 SDK
// 1. 配置 SDK 参数
val config = SDKConfig(
username = account,
password = password, // 与 passwordPk 二选一
passwordPk = passwordPk, // 与 password 二选一
baseUrl = Environment.DEBUG,
logConfig = LogConfig(
enableLog = true,
logLevel = Log.INFO,
logFilePath = null, // null 使用默认路径:filesDir/logs
fileLoggingEnabled = true,
consoleLogEnabled = true
)
)
// 2. 初始化 SDK(含登录)
CallSDK.init(
context = applicationContext,
config = config,
object : InitListener {
override fun onInitSuccess() {
// 初始化成功(含登录成功),SDK 就绪
}
override fun onInitFailed(errorCode: Int, errorMsg: String?) {
// 初始化失败,错误码详见「5.1 初始化错误」
}
}
)
// 3. 设置被踢下线监听(独立于 init,全局生效)
CallSDK.setOnKickedListener {
// 账号被踢下线
}
3.2 监听服务端发起的通话
当服务端发起通话时,SDK 会自动接听 SIP,然后通过此监听器通知开发者。开发者应在回调中打开通话界面。
// 建议在初始化成功后设置(如在主页面 onCreate 中)
CallSDK.setOnServerCallListener { callInfo ->
// 收到服务端发起的通话,打开通话界面
val intent = Intent(this, CallActivity::class.java)
intent.putExtra("callId", callInfo.callId)
intent.putExtra("direction", callInfo.direction.name)
startActivity(intent)
}
3.3 监听通话状态
通话状态监听器用于在通话界面中更新 UI,适用于主动外呼和服务端发起的通话。
class CallActivity : AppCompatActivity() {
private val callStateListener = object : CallStateListener {
override fun onCallAlerting(callInfo: CallInfo) {
// 对方振铃中
}
override fun onCallAnswered(callInfo: CallInfo) {
// 对方已接听,通话建立
}
override fun onCallReleased(callInfo: CallInfo) {
// 通话已结束
}
override fun onCallFailed(callInfo: CallInfo, errorCode: Int, errorMsg: String) {
// 通话失败,错误码详见「5.2 呼叫错误」
}
override fun onDtmfReceived(callInfo: CallInfo, dtmf: String) {
// 收到 DTMF 信号
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 注册通话状态监听器
CallSDK.addCallStateListener(callStateListener)
}
override fun onDestroy() {
super.onDestroy()
// 移除通话状态监听器
CallSDK.removeCallStateListener(callStateListener)
}
}
3.4 拨打电话
CallSDK.makeCall(
phoneNumber = "13800138000",
callback = object : MakeCallCallback {
override fun onSuccess(callInfo: CallInfo) {
// 呼叫已成功发出
// 后续通话状态通过 CallStateListener 接收
}
override fun onFailed(errorCode: Int, errorMsg: String) {
// 呼叫发起失败(黑名单、风控、网络等原因)
// 错误码详见「5.2 呼叫错误」
}
}
)
3.5 拨打电话携带自定义参数
自定义参数为 JSONObject 类型,大小限制 < 256 字节,超出会触发 onFailed。
val userData = JSONObject().apply {
put("orderId", "12345")
put("source", "crm")
}
CallSDK.makeCall(
phoneNumber = "13800138000",
userData = userData,
callback = object : MakeCallCallback {
override fun onSuccess(callInfo: CallInfo) {
// 呼叫已成功发出
}
override fun onFailed(errorCode: Int, errorMsg: String) {
// 呼叫发起失败
}
}
)
3.6 挂断电话
CallSDK.hangupCall()
3.7 发送 DTMF
// 发送单个 DTMF 信号(如:*、#、0-9)
CallSDK.sendDTMF("1")
3.8 扬声器控制
// 打开/关闭扬声器
CallSDK.openLoudSpeaker(true) // true: 打开,false: 关闭
// 获取扬声器状态
val isSpeakerOn = CallSDK.isLoudSpeakerOn()
3.9 静音控制
// 开启/关闭静音
CallSDK.openMute(true) // true: 静音,false: 取消静音
// 获取静音状态
val isMute = CallSDK.isMuteOn()
3.10 查询外显号码组列表
CallSDK.queryNumberGroupList(
page = 1,
pageSize = 20,
listener = object : NumberGroupListListener {
override fun onSuccess(response: NumberGroupListResponse?) {
// 查询成功
}
override fun onFailed(errorMsg: String?) {
// 查询失败
}
}
)
3.11 查询外显号码列表
CallSDK.queryDisplayNumberList(
listener = object : DisplayNumberListListener {
override fun onSuccess(numbers: List<DisplayNumber>?) {
// 查询成功
}
override fun onFailed(errorMsg: String?) {
// 查询失败
}
}
)
3.12 设置座席外显号码组(通过号码组 ID)
CallSDK.updateAgentNumberGroupById(
numberGroupId = "001",
listener = object : AgentConfigListener {
override fun onSuccess(config: AgentConfig?) {
// 设置成功
}
override fun onFailed(errorMsg: String?) {
// 设置失败
}
}
)
3.13 设置座席外显号码组(通过号码组名称)
CallSDK.updateAgentNumberGroupByName(
numberGroupName = "group_name",
listener = object : AgentConfigListener {
override fun onSuccess(config: AgentConfig?) {
// 设置成功
}
override fun onFailed(errorMsg: String?) {
// 设置失败
}
}
)
3.14 设置座席自定义外显号码
CallSDK.updateAgentSelectNumber(
selectNumber = "010-12345678",
listener = object : AgentConfigListener {
override fun onSuccess(config: AgentConfig?) {
// 设置成功
}
override fun onFailed(errorMsg: String?) {
// 设置失败
}
}
)
3.15 查询座席配置
CallSDK.getAgentConfig(
listener = object : AgentConfigListener {
override fun onSuccess(config: AgentConfig?) {
// 查询成功
}
override fun onFailed(errorMsg: String?) {
// 查询失败
}
}
)
3.16 获取登录信息
val loginInfo = CallSDK.getLoginInfo()
3.17 获取当前通话信息
val callInfo = CallSDK.getCurrentCallInfo()
// callInfo?.callId — 通话 ID
// callInfo?.phoneNumber — 电话号码
// callInfo?.direction — 通话方向(APP / SERVER)
// callInfo?.state — 当前状态(CALLING / ALERTING / ANSWERED / RELEASED / FAILED)
// callInfo?.startTime — 通话开始时间戳
3.18 注销登录
CallSDK.logout()
3.19 销毁 SDK
销毁 SDK 实例,释放所有资源(通话、连接、监听器等)。
CallSDK.release()
4. 数据模型
4.1 CallInfo — 通话信息
| 字段 | 类型 | 说明 |
|---|---|---|
callId |
String? |
通话 ID |
phoneNumber |
String? |
电话号码 |
direction |
CallDirection |
通话方向 |
state |
CallState |
当前通话状态 |
startTime |
Long |
通话开始时间(毫秒时间戳) |
4.2 CallDirection — 通话方向
| 值 | 说明 |
|---|---|
APP |
APP 端发起(开发者主动调用 makeCall()) |
SERVER |
服务端发起,SDK 自动接听 |
4.3 CallState — 通话状态
| 值 | 说明 |
|---|---|
CALLING |
呼叫发起中 |
ALERTING |
对方振铃中 |
ANSWERED |
通话中(对方已接听) |
RELEASED |
通话已结束 |
FAILED |
通话失败 |
4.4 通话状态流转
主动外呼:
CALLING → ALERTING → ANSWERED → RELEASED
└→ FAILED
服务端发起:
CALLING → ALERTING → ANSWERED → RELEASED
└→ FAILED
5. 错误码
5.1 错误码分类
| 错误码范围 | 分类 | 说明 |
|---|---|---|
| 0 | 成功 | 操作成功 |
| -1 ~ -99 | 初始化错误 | SDK 初始化相关错误 |
| -100 ~ -199 | 参数验证错误 | 输入参数验证失败 |
| -200 ~ -299 | 登录流程错误 | 登录、认证、SIP 注册相关错误 |
| -300 ~ -399 | 通话相关错误 | 呼叫、通话控制相关错误 |
| -400 ~ -499 | 网络错误 | 网络连接、超时相关错误 |
| -2000 ~ -2099 | HTTP 请求错误 | HTTP 接口调用错误 |
| -2100 ~ -2199 | WebSocket 错误 | WebSocket 连接相关错误 |
| -4000 ~ -4099 | 外显号码错误 | 外显号码配置相关错误 |
| -9999 | 未知错误 | 未定义的错误 |
5.2 通用错误
| 错误码 | 常量名 | 原因 |
|---|---|---|
| -9999 | UNKNOWN_ERROR |
未知错误(网络异常、解析失败等兜底错误) |
5.3 初始化错误(-1 ~ -99)
| 错误码 | 常量名 | 原因 |
|---|---|---|
| -1 | SDK_INIT_FAILED |
SDK 初始化失败 |
| -2 | SDK_NOT_INITIALIZED |
SDK 未初始化 |
| -3 | PJSIP_CREATE_FAILED |
PJSIP 创建失败 |
| -4 | PJSIP_INIT_FAILED |
PJSIP 初始化失败 |
| -5 | TRANSPORT_CREATE_FAILED |
传输层创建失败(UDP/TCP) |
| -6 | PJSIP_START_FAILED |
PJSIP 启动失败 |
5.4 参数验证错误(-100 ~ -199)
| 错误码 | 常量名 | 原因 |
|---|---|---|
| -100 | INVALID_ACCOUNT |
账号无效 |
| -101 | INVALID_PASSWORD |
密码无效 |
| -104 | ACCOUNT_FORMAT_ERROR |
账号格式错误 |
| -105 | ACCOUNT_EMPTY |
账号为空 |
| -106 | PASSWORD_EMPTY |
密码为空 |
| -107 | SERVER_URL_EMPTY |
服务器 URL 为空 |
| -108 | TOKEN_EMPTY |
Token 为空 |
5.5 登录流程错误(-200 ~ -299)
通过 InitListener.onInitFailed(errorCode, errorMsg) 回调返回。
| 错误码 | 常量名 | 原因 |
|---|---|---|
| -200 | LOGIN_REQUEST_FAILED |
登录请求失败 |
| -201 | LOGIN_AUTH_FAILED |
登录认证失败 |
| -202 | LOGIN_FAILED_WRONG_CREDENTIALS |
登录失败,账号或密码错误 |
| -203 | SOCKET_CONNECT_FAILED |
Socket 连接失败 |
| -205 | SIP_CONFIG_FAILED |
获取 SIP 配置失败 |
| -206 | SIP_CONFIG_INCOMPLETE |
SIP 配置信息不完整 |
| -207 | SIP_REGISTER_FAILED |
SIP 注册失败(通用) |
| -209 | SIP_REGISTER_REJECTED |
SIP 注册被拒绝 (403) |
| -210 | LOGIN_FAILED_ACCOUNT_FROZEN |
账号已被冻结,请联系管理员解冻 |
| -211 | AGENT_DISABLED |
坐席已停用 |
| -212 | ACCOUNT_NOT_FOUND |
账户未找到 |
| -213 | AGENT_NOT_FOUND |
坐席未找到 |
| -215 | SIP_REGISTER_NOT_FOUND |
SIP 注册未找到 (404) |
| -216 | SIP_REGISTER_SERVER_ERROR |
SIP 注册服务器错误 (5xx) |
| -217 | GET_PUBLIC_KEY_FAILED |
获取公钥失败 |
5.6 通话相关错误(-300 ~ -399)
通过 MakeCallCallback.onFailed(errorCode, errorMsg) 或 CallStateListener.onCallFailed(callInfo, errorCode, errorMsg) 回调返回。
| 错误码 | 常量名 | 原因 | 触发阶段 |
|---|---|---|---|
| -300 | CALL_FAILED |
呼叫失败(通用) | 通话中(SIP 层) |
| -301 | CALL_PERMISSION_DENIED |
通话权限被拒绝 | 拨号前 |
| -302 | CALL_NOT_LOGGED_IN |
未登录无法呼叫 | 拨号前 |
| -303 | CALL_NUMBER_EMPTY |
呼叫号码为空 | 拨号前 |
| -304 | CALL_REJECTED |
呼叫被拒绝 (403) | 通话中(SIP 层) |
| -305 | CALL_NUMBER_NOT_FOUND |
号码不存在 (404) | 通话中(SIP 层) |
| -306 | CALL_USER_BUSY |
用户忙线 (486) | 通话中(SIP 层) |
| -307 | CALL_REQUEST_TERMINATED |
对方拒接或手机软件拦截 (487) | 通话中(SIP 层) |
| -308 | CALL_TIMEOUT |
呼叫超时 (408) | 通话中(SIP 层) |
| -309 | CALL_TEMPORARILY_UNAVAILABLE |
暂时不可用 (480) | 通话中(SIP 层) |
| -310 | CALL_FAILED_BLACKLIST |
号码在黑名单中,无法拨打 | 拨号前(黑名单校验) |
| -311 | CALL_FAILED_RISK_LIMIT |
呼叫次数已达上限(风控拦截) | 拨号前(风控查询) |
| -312 | CALL_FAILED_USERDATA_TOO_LARGE |
userData 超过 255 字节限制 | 拨号前(参数校验) |
| -313 | CALL_NOT_ACCEPTABLE |
媒体协商失败 (488) | 通话中(SIP 层) |
| -314 | CALL_REGISTRATION_DROPPED |
SIP 注册掉线 (477) | 通话中(SIP 层) |
| -315 | CALL_NOT_EXIST |
通话不存在 (481) | 通话中(SIP 层) |
| -316 | CALL_ADDRESS_INCOMPLETE |
号码不完整 (484) | 通话中(SIP 层) |
| -317 | CALL_DECLINED |
对方拒绝接听 (603) | 通话中(SIP 层) |
| -318 | CALL_BUSY_EVERYWHERE |
全局忙线 (600) | 通话中(SIP 层) |
| -319 | CALL_SERVER_ERROR |
服务器内部错误 (500) | 通话中(SIP 层) |
| -320 | CALL_BAD_GATEWAY |
网关错误 (502) | 通话中(SIP 层) |
| -321 | CALL_SERVICE_UNAVAILABLE |
服务不可用 (503) | 通话中(SIP 层) |
| -322 | CALL_SERVER_TIMEOUT |
服务器超时 (504) | 通话中(SIP 层) |
| -323 | CALL_NETWORK_LOST |
通话中网络中断 | 通话中(网络监听) |
触发阶段说明:
- 拨号前:在
makeCall()调用后、SIP 拨号前触发,通过MakeCallCallback.onFailed()返回- 通话中:SIP 拨号后触发,通过
CallStateListener.onCallFailed()返回
5.7 网络错误(-400 ~ -499)
| 错误码 | 常量名 | 原因 |
|---|---|---|
| -401 | NETWORK_TIMEOUT |
网络超时 |
5.8 HTTP 请求错误(-2000 ~ -2099)
| 错误码 | 常量名 | 原因 |
|---|---|---|
| -2001 | HTTP_INVALID_URL |
无效 URL |
| -2007 | HTTP_ERROR |
HTTP 错误 (4xx, 5xx,通用) |
| -2008 | HTTP_EMPTY_RESPONSE |
空响应数据 |
| -2009 | HTTP_JSON_PARSE_FAILED |
JSON 解析失败 |
5.9 WebSocket 错误(-2100 ~ -2199)
| 错误码 | 常量名 | 原因 |
|---|---|---|
| -2100 | WS_INVALID_URL |
无效的 WebSocket URL |
| -2101 | WS_CONNECT_FAILED |
WebSocket 连接失败 |
5.10 外显号码错误(-4000 ~ -4099)
| 错误码 | 常量名 | 原因 |
|---|---|---|
| -4001 | DISPLAY_NUMBER_AGENT_ID_EMPTY |
坐席 ID 为空 |
| -4002 | DISPLAY_NUMBER_CONFIG_FAILED |
获取外显号码配置失败 |
5.13 错误处理建议
- 初始化错误:检查 SDK 是否正确集成,确保在调用其他方法前先初始化
- 参数验证错误:检查输入参数是否为空、格式是否正确
- 登录流程错误:检查账号密码、网络连接、服务器配置
- 通话相关错误:确保已登录且 SIP 连接成功,检查被叫号码格式
- 网络错误:检查网络连接状态,可能需要重试
- 未知错误:查看详细错误消息,联系技术支持
6. API 参考
6.1 初始化 & 生命周期
| 方法 | 说明 |
|---|---|
CallSDK.init(context, config, initListener) |
初始化 SDK(含登录) |
CallSDK.logout() |
注销登录,释放通话和连接资源 |
CallSDK.release() |
销毁 SDK 实例,释放所有资源(含监听器),调用后需重新 init() |
CallSDK.getLoginInfo(): LoginInfo? |
获取登录信息 |
CallSDK.getConfig(): SDKConfig |
获取当前 SDK 配置 |
6.2 监听器
| 方法 | 说明 |
|---|---|
CallSDK.setOnServerCallListener(listener) |
设置服务端通话监听器(全局唯一,传 null 移除) |
CallSDK.setOnKickedListener(listener) |
设置被踢下线监听器(全局唯一,传 null 移除) |
CallSDK.addCallStateListener(listener) |
添加通话状态监听器 |
CallSDK.removeCallStateListener(listener) |
移除通话状态监听器 |
6.3 通话
| 方法 | 说明 |
|---|---|
CallSDK.makeCall(phoneNumber, callback) |
发起通话 |
CallSDK.makeCall(phoneNumber, userData, callback) |
发起通话(携带自定义参数) |
CallSDK.hangupCall() |
挂断当前通话 |
CallSDK.sendDTMF(dtmfNumber) |
发送 DTMF 信号 |
CallSDK.getCurrentCallInfo(): CallInfo? |
获取当前通话信息 |
CallSDK.getCurrentCallId(): String? |
获取当前通话 ID |
6.4 通话控制
| 方法 | 说明 |
|---|---|
CallSDK.openLoudSpeaker(open) |
打开/关闭扬声器 |
CallSDK.isLoudSpeakerOn(): Boolean |
获取扬声器状态 |
CallSDK.openMute(open) |
开启/关闭静音 |
CallSDK.isMuteOn(): Boolean |
获取静音状态 |
6.5 外显号码配置
| 方法 | 说明 |
|---|---|
CallSDK.queryNumberGroupList(page, pageSize, listener) |
查询外显号码组列表 |
CallSDK.queryDisplayNumberList(listener) |
查询外显号码列表 |
CallSDK.updateAgentNumberGroupById(id, listener) |
设置座席外显号码组(通过 ID) |
CallSDK.updateAgentNumberGroupByName(name, listener) |
设置座席外显号码组(通过名称) |
CallSDK.updateAgentSelectNumber(number, listener) |
设置座席自定义外显号码 |
CallSDK.getAgentConfig(listener) |
查询座席配置 |
6.6 监听器接口
InitListener — 初始化监听器
interface InitListener {
fun onInitSuccess() {}
fun onInitFailed(errorCode: Int, errorMsg: String?) {}
}
OnServerCallListener — 服务端通话监听器
fun interface OnServerCallListener {
fun onServerCall(callInfo: CallInfo)
}
MakeCallCallback — 发起通话回调
interface MakeCallCallback {
fun onSuccess(callInfo: CallInfo)
fun onFailed(errorCode: Int, errorMsg: String)
}
CallStateListener — 通话状态监听器
interface CallStateListener {
fun onCallAlerting(callInfo: CallInfo) {}
fun onCallAnswered(callInfo: CallInfo) {}
fun onCallReleased(callInfo: CallInfo) {}
fun onCallFailed(callInfo: CallInfo, errorCode: Int, errorMsg: String) {}
fun onDtmfReceived(callInfo: CallInfo, dtmf: String) {}
}
所有方法提供默认空实现,开发者只需 override 关心的回调。
7. 完整集成示例
7.1 最小集成(仅主动外呼)
// Application 或 LoginActivity 中
CallSDK.init(applicationContext, config, object : InitListener {
override fun onInitSuccess() {
}
})
// 通话页面中
class CallActivity : AppCompatActivity() {
private val listener = object : CallStateListener {
override fun onCallAlerting(callInfo: CallInfo) { /* 振铃中 */ }
override fun onCallAnswered(callInfo: CallInfo) { /* 开始计时 */ }
override fun onCallReleased(callInfo: CallInfo) { finish() }
override fun onCallFailed(callInfo: CallInfo, errorCode: Int, errorMsg: String) {
Toast.makeText(this@CallActivity, errorMsg, Toast.LENGTH_SHORT).show()
finish()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
CallSDK.addCallStateListener(listener)
CallSDK.makeCall("xxxxxxxxxx", object : MakeCallCallback {
override fun onSuccess(callInfo: CallInfo) { /* 呼叫已发出 */ }
override fun onFailed(errorCode: Int, errorMsg: String) { finish() }
})
}
override fun onDestroy() {
super.onDestroy()
CallSDK.removeCallStateListener(listener)
}
}
7.2 完整集成(主动外呼 + 服务端通话)
// ── 初始化后,设置全局监听 ──
// 服务端通话:收到通知后打开通话页面
CallSDK.setOnServerCallListener { callInfo ->
val intent = Intent(context, CallActivity::class.java)
intent.putExtra("callId", callInfo.callId)
intent.putExtra("direction", CallDirection.SERVER.name)
startActivity(intent)
}
// 被踢下线
CallSDK.setOnKickedListener {
}
// ── 通话页面(统一处理两种场景)──
class CallActivity : AppCompatActivity() {
private lateinit var direction: CallDirection
private val listener = object : CallStateListener {
override fun onCallAlerting(callInfo: CallInfo) { /* 振铃中 */ }
override fun onCallAnswered(callInfo: CallInfo) { /* 通话中 */ }
override fun onCallReleased(callInfo: CallInfo) { finish() }
override fun onCallFailed(callInfo: CallInfo, errorCode: Int, errorMsg: String) {
Toast.makeText(this@CallActivity, errorMsg, Toast.LENGTH_SHORT).show()
finish()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
direction = CallDirection.valueOf(
intent.getStringExtra("direction") ?: CallDirection.APP.name
)
// 注册监听
CallSDK.addCallStateListener(listener)
when (direction) {
CallDirection.APP -> {
// 主动外呼
val phone = intent.getStringExtra("phoneNumber") ?: return
CallSDK.makeCall(phone, object : MakeCallCallback {
override fun onSuccess(callInfo: CallInfo) { /* 呼叫已发出 */ }
override fun onFailed(errorCode: Int, errorMsg: String) { finish() }
})
}
CallDirection.SERVER -> {
}
}
}
override fun onDestroy() {
super.onDestroy()
CallSDK.removeCallStateListener(listener)
}
}
8. Demo
Demo 集成了通话 SDK,包含自定义拨号盘页面和通话页面,UI 开发可参考 Demo。
8.1 前台服务
通话开始时会创建一个前台通知用来保活。
8.2 耳机
耳机的具体实现可参考 Demo:
- 需要增加蓝牙权限:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!-- Android 12+ -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
HeadsetPlugReceiver— 具体实现可参考 Demo 中的HeadsetPlugReceiver类,实现了监听耳机插拔的逻辑。
9. 混淆配置
-keep class com.useasy.callsdk.** { *; }
-keep interface com.useasy.callsdk.** { *; }
10. 常见问题
10.1 初始化失败
- 检查用户名 / 密码是否正确
- 检查网络连接(需联网状态)
- 检查环境地址是否可访问
10.2 通话无声音
- 检查录音 / 扬声器权限是否授予
- 检查设备音量是否正常
11. 注意事项
- 权限处理:Android 6.0+ 需动态申请录音权限,建议在拨号前检查并申请。
- 监听器生命周期:
addCallStateListener和removeCallStateListener必须成对调用,建议分别在onCreate和onDestroy中调用。 - 服务端通话:
OnServerCallListener应在初始化成功后尽早设置,避免错过通话通知。
12. 版本更新日志
| 版本号 | 更新时间 | 核心更新 |
|---|---|---|
| 1.3.2 | 2026-3-13 | 1. 通话中监听网络状态,3 秒未恢复网络则自动挂断 2. 网络未连接时不重连 Socket 3. 通话事件通知去重,修复连续两次 onCallAlerting 的 bug4. 新增 release() 方法,支持完整销毁 SDK 实例 |
| 1.3.1 | 2026-3-11 | 1. 修复退出登录后重新登录再呼叫导致 crash 的问题 2. 精简 native .so 体积(去除strip 调试信息) |
| 1.3.0 | 2026-2-28 | 1. 重构通话监听 API:统一 CallStateListener,支持 add/remove 2. 新增 OnServerCallListener 用于服务端通话通知3. 新增 MakeCallCallback 分离外呼发起结果4. 新增 getCurrentCallInfo() 支持页面状态同步5. onKicked 独立为 setOnKickedListener6. 新增 CallInfo 统一通话信息模型7. 错误码对齐 8. 替换新的底层通话库 9. 优化日志 10. 修复通话bug |
| 1.2.11 | 2026-1-16 | 1. 拨号前增加黑名单校验 2. 拨号前增加风控查询 |
| 1.2.10 | 2026-1-8 | 1. 优化通话回铃音杂音问题 |
| 1.2.9 | 2026-1-7 | 1. 修复通话震铃音问题 2. 设置外显号码组支持通过 id 和 name 设置 |
| 1.2.8 | 2026-1-6 | 1. onCallReleased 回调增加 hangupType 参数 2. 修复 onCallFailed 异常回调的问题 3. updateAgentNumberGroup 等接口去除 agent_id 参数 4. 增加日志 |
| 1.2.7 | 2025-12-31 | 1. 修复 crash |
| 1.2.6 | 2025-12-28 | 1. SDKConfig 增加 passwordPk 参数 2. Demo 优化,动态申请蓝牙权限 |
| 1.2.5 | 2025-12-24 | 1. 兼容 okhttp 3.x 版本 2. LogConfig 支持 consoleLogEnabled 配置 |
| 1.2.4 | 2025-12-21 | 1. 外呼支持传递自定义参数 2. 初始化失败和外呼失败回调增加错误码和错误信息 3. 修复拨号信息残留 Bug 4. 修复通话声音小、有回音的问题 5. 优化日志 |
| 1.2.3 | 2025-12-19 | 1. 增加响铃中回调 onCallAlerting 2. 增加日志配置 LogConfig 3. 保留 init 参数中的 Context,去除其他方法的 Context 参数 4. 去除 setMute 方法 5. 优化 SDK,增加状态类型 |
| 1.2.2 | 2025-12-18 | 1. 支持蓝牙耳机/耳机通话 2. 优化 SDK 接口 3. SDK 回调统一回调在主线程 |
| 1.2.1 | 2025-12-16 | 1. 资源精简 2. 修复 Demo 计时基准错误问题 |
| 1.2.0 | 2025-12-15 | 1. 修复对端挂断电话无法收到回调的问题 2. 通话增加前台通知,启动前台 Service 3. 资源精简 |
| 1.0.0 | 2025-11-30 | 初始版本,支持音频通话、外显号码配置、通话控制 |
13. 从 1.2.x 迁移到 1.3.0
主要变更
| 旧 API | 新 API | 说明 |
|---|---|---|
CallSDK.makeCall(phone, CallStateListener) |
CallSDK.makeCall(phone, MakeCallCallback) + addCallStateListener() |
外呼发起结果和通话状态分离 |
CallSDK.setServerCallListener(ServerCallStateListener) |
CallSDK.setOnServerCallListener(OnServerCallListener) |
简化为单一回调 |
InitListener.onKicked() |
CallSDK.setOnKickedListener { } |
独立为全局监听 |
CallStateListener 回调参数为 callId: String? |
回调参数为 callInfo: CallInfo |
统一通话信息模型 |
onCallReleased(hangupType, callId) |
onCallReleased(callInfo) |
hangupType 信息后续通过 CallInfo 扩展 |
onCallProceeding(callId) |
已移除 | 内部状态,不再暴露 |
onConnected() / onDisconnected() |
已移除 | SIP 内部状态,不再暴露 |
onIncomingCall(callId) |
已移除 | SDK 自动接听,不再暴露 |
迁移步骤
- 将
InitListener中的onKicked()改为CallSDK.setOnKickedListener { } - 将
makeCall的CallStateListener参数改为MakeCallCallback - 在通话页面
onCreate中调用CallSDK.addCallStateListener(listener) - 在通话页面
onDestroy中调用CallSDK.removeCallStateListener(listener) - 更新所有回调方法签名,参数从
callId: String?改为callInfo: CallInfo
压缩包中包含demo示例,demo 集成了通话 SDK,同时还有自定义的拨号盘页面以及通话页面,UI 开发可参考 Demo