帮你快速理解、总结文档立即下载

Android 智能设备

最近更新时间:2026-09-20 16:47:02
我的收藏
本文将介绍在 Android 平台实现设备响应来自 App 或微信小程序的呼叫(接听/拒绝),以及通话建立后的音视频采集推流、对端画面渲染与挂断。

前提条件

在接入通话功能前,请您先完成 登录与注册 文档中跑通 login 登录功能,如果您已经完成,可以跳过该操作。
说明:
音视频通话依赖摄像头与麦克风采集,请确保已在 AndroidManifest.xml 中声明 android.permission.CAMERAandroid.permission.RECORD_AUDIO 权限,并在运行时向用户申请授权。

接入步骤

步骤 1:注册通话事件回调

登录成功后,通过 TXIoTCallSession.getInstance().addListener 注册 TXIoTCallSession.Listener,通话相关的回调如下:
回调
说明
onCallRequested
收到对端来电时触发,携带 contact(对端信息)与 option(通话媒体类型等)。
onCallAccepted
通话正式建立时触发。被叫方调用 accept 成功、SDK 完成通话通道建立后也会回调,此时才是开启摄像头/麦克风采集推流的时机。
onCallRejected
本次呼叫被拒绝时触发。被叫方调用 reject 成功后也会回调,需结束本次呼叫并释放资源。
onCallTimeout
来电未在指定时间内被接听或拒绝时触发,需释放资源。
onCallHangup
对端挂断通话时触发。
private final TXIoTCallSession.Listener mCallListener = new TXIoTCallSession.Listener() {
@Override
public void onCallRequested(Contact contact, Option option) {
// 收到来电,根据业务决定接听或拒绝(见步骤 2)
}

@Override
public void onCallAccepted(Contact contact) {
// 通话正式建立,此时开启采集推流与渲染对端画面(见步骤 3)
}

@Override
public void onCallRejected(Contact contact) {
// 本次呼叫被拒绝,结束本次呼叫并释放资源
}

@Override
public void onCallTimeout(Contact contact) {
// 来电未及时处理超时,释放资源
}

@Override
public void onCallHangup(Contact contact) {
// 对端挂断,停止采集与渲染并释放资源(见步骤 4)
}
};

// 登录成功后注册监听
TXIoTCallSession.getInstance().addListener(mCallListener);

步骤 2:响应来电

设备作为被叫,收到 onCallRequested 后,根据业务逻辑调用 accept 接听,或调用 reject 拒绝:
注意:
accept 回调 onSuccess 仅代表接听请求已发出,通话尚未建立。请勿在此时立即开启采集推流,务必等到 onCallAccepted 回调触发后(通话正式建立)再开启摄像头/麦克风。
@Override
public void onCallRequested(Contact contact, Option option) {
// 示例:直接接听,实际业务可结合按键、界面弹窗等方式决定接听或拒绝
TXIoTCallSession.getInstance().accept(contact.userId, new TXIoTCallback() {
@Override
public void onSuccess() {
// 接听请求已发出,等待 onCallAccepted 回调触发后再开启采集推流
}

@Override
public void onError(int code, String desc) {
Log.e(TAG, "accept failed: " + code + " " + desc);
}
});

// 拒绝来电:
// TXIoTCallSession.getInstance().reject(contact.userId, callback);
}

步骤 3:开启采集推流与渲染对端画面

通话正式建立(onCallAccepted 回调触发)后,根据 onCallRequested 携带的 option.mediaContent 开启本地采集推流,直接调用如下接口即可:
接口
说明
openCamera(frontCamera, view)
开启摄像头采集并推流,frontCamera 为是否使用前置摄像头,view 为本地画面预览的 TXCloudVideoView(可为 null),视频通话时调用。
openMicrophone()
开启麦克风采集并推流,音频/视频通话均需调用。
startRemoteView(userId, view)
拉取并渲染对端 userId 的视频画面到指定 TXCloudVideoView
// mLocalView / mRemoteView 为布局中的 TXCloudVideoView,分别用于本地预览与对端画面
String userId = "peer_user_id";
if (media == MediaContent.VIDEO || media == MediaContent.AUDIO_VIDEO) {
TXIoTCallSession.getInstance().openCamera(true, mLocalView); // 使用前置摄像头,本地预览到 mLocalView
TXIoTCallSession.getInstance().startRemoteView(userId, mRemoteView);// 渲染对端画面
}

if (media == MediaContent.AUDIO || media == MediaContent.AUDIO_VIDEO) {
TXIoTCallSession.getInstance().openMicrophone();
}

步骤 4:挂断通话

通话中主动挂断,调用 hangup;对端挂断时会触发 onCallHangup,此时应关闭本地采集、停止对端画面渲染:
// 主动挂断
TXIoTCallSession.getInstance().hangup(userId, new TXIoTCallback() {
@Override
public void onSuccess() {
// 停止采集与渲染,释放资源(onCallHangup / onCallTimeout / onCallRejected 均调用)
TXIoTCallSession.getInstance().stopRemoteView(userId);
TXIoTCallSession.getInstance().closeCamera();
TXIoTCallSession.getInstance().closeMicrophone();
}

@Override
public void onError(int code, String desc) {
Log.e(TAG, "hangup failed: " + code + " " + desc);
}
});


说明:
来电未及时处理时会触发 onCallTimeout,处理方式与 onCallHangup 一致:关闭采集、停止渲染并释放资源。

完整示例代码

以下 CallActivity 示例整合了通话事件响应、接听来电、开启采集推流与渲染对端画面、以及挂断的完整流程,实际接入时,请将布局中的 TXCloudVideoView 与业务界面对接。
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import com.tencent.liteav.iot.TXIoTCallback;
import com.tencent.liteav.iot.TXIoTCallSession;
import com.tencent.liteav.iot.TXIoTCallSession.Contact;
import com.tencent.liteav.iot.TXIoTCallSession.MediaContent;
import com.tencent.liteav.iot.TXIoTCallSession.Option;
import com.tencent.liteav.iot.TXIoTError;
import com.tencent.rtmp.ui.TXCloudVideoView;

public class CallActivity extends AppCompatActivity {
private static final String TAG = "CallActivity";

private TXCloudVideoView mLocalView;
private TXCloudVideoView mRemoteView;

// 记住当前通话的媒体类型与对端账号,供接听/挂断复用
private MediaContent mCurrentMedia = MediaContent.AUDIO_VIDEO;
private String mPeerUserId;

private final TXIoTCallSession.Listener mCallListener = new TXIoTCallSession.Listener() {
@Override
public void onCallRequested(Contact contact, Option option) {
Log.i(TAG, "onCallRequested userId=" + contact.userId + " media=" + option.mediaContent);
mPeerUserId = contact.userId;
mCurrentMedia = option.mediaContent;
// 示例:自动接听。实际可弹出接听/拒绝界面
acceptCall(contact.userId);
}

@Override
public void onCallAccepted(Contact contact) {
Log.i(TAG, "onCallAccepted userId=" + contact.userId);
// 通话正式建立,此时才开启采集推流与渲染对端画面
startCallStreams(contact, mCurrentMedia);
}

@Override
public void onCallRejected(Contact contact) {
Log.i(TAG, "onCallRejected userId=" + contact.userId);
stopCallStreams(contact.userId);
}

@Override
public void onCallTimeout(Contact contact) {
Log.i(TAG, "onCallTimeout userId=" + contact.userId);
stopCallStreams(contact.userId);
}

@Override
public void onCallHangup(Contact contact) {
Log.i(TAG, "onCallHangup userId=" + contact.userId);
stopCallStreams(contact.userId);
}
};

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_call);
// mLocalView = findViewById(R.id.local_view);
// mRemoteView = findViewById(R.id.remote_view);

// 请确保已完成 login 登录,并已申请摄像头/麦克风运行时权限
TXIoTCallSession.getInstance().addListener(mCallListener);
}

// 接听来电(采集推流放到 onCallAccepted 回调中执行)
private void acceptCall(String userId) {
TXIoTCallSession.getInstance().accept(userId, new TXIoTCallback() {
@Override
public void onSuccess() {
Log.i(TAG, "accept succeed, waiting for onCallAccepted");
}

@Override
public void onError(int code, String desc) {
Log.e(TAG, "accept failed: " + code + " " + desc);
}
});
}

// 通话建立后:根据媒体类型开启采集推流并渲染对端画面
private void startCallStreams(Contact contact, MediaContent media) {
TXIoTCallSession session = TXIoTCallSession.getInstance();
if (media == MediaContent.VIDEO || media == MediaContent.AUDIO_VIDEO) {
int rc = session.openCamera(true, mLocalView);
if (rc != TXIoTError.SUCCESS) {
Log.e(TAG, "openCamera failed: " + rc);
}
session.startRemoteView(contact.userId, mRemoteView);
}
if (media == MediaContent.AUDIO || media == MediaContent.AUDIO_VIDEO) {
int rc = session.openMicrophone();
if (rc != TXIoTError.SUCCESS) {
Log.e(TAG, "openMicrophone failed: " + rc);
}
}
}

// 主动挂断
private void hangupCall(String userId) {
TXIoTCallSession.getInstance().hangup(userId, new TXIoTCallback() {
@Override
public void onSuccess() {
stopCallStreams(userId);
}

@Override
public void onError(int code, String desc) {
Log.e(TAG, "hangup failed: " + code + " " + desc);
}
});
}

// 停止采集与渲染,释放资源
private void stopCallStreams(String userId) {
TXIoTCallSession session = TXIoTCallSession.getInstance();
if (userId != null) {
session.stopRemoteView(userId);
}
session.closeCamera();
session.closeMicrophone();
}

@Override
protected void onDestroy() {
super.onDestroy();
// 退出通话界面时,务必挂断并释放资源,移除监听
if (mPeerUserId != null) {
hangupCall(mPeerUserId);
} else {
stopCallStreams(null);
}
TXIoTCallSession.getInstance().removeListener(mCallListener);
}
}

常见问题

现象
排查建议
APP 发起呼叫后,设备端未收到 onCallRequested
确认设备已成功登录;确认已调用 addListener 注册监听且未被提前 removeListener
accept 回调 onError 返回失败。
确认设备已成功登录;确认 userId 有效;确认当前没有其他通话正在进行。
本地或对端画面不显示。
确认已声明并授予 CAMERARECORD_AUDIO 权限;确认 openCamera 传入的本地 TXCloudVideoViewstartRemoteView 传入的对端 TXCloudVideoView 均已正确加入布局且可见。
多次通话后设备资源占用持续增长。
检查 onCallHangup / onCallTimeout / onCallRejected 是否都正确调用了 closeCamera / closeMicrophone / stopRemoteView,并在退出界面时 removeListener,避免资源泄漏。

联系我们

如果您在接入或使用过程中有任何疑问或者建议,欢迎 联系我们 提交反馈。