# 实现音视频互动 (/zh-CN/realtime-media/rtc/get-started/quick-start/android)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

本文介绍如何集成声网实时互动 SDK，通过少量代码从 0 开始实现一个简单的实时互动 App，适用于互动直播和视频通话场景。首先，你需要了解以下有关音视频实时互动的基础概念：

- 声网实时互动 SDK：由声网开发的、帮助开发者在 App 中实现实时音视频互动的 SDK。
- 频道：用于传输数据的通道，在同一个频道内的用户可以进行实时互动。
- 主播：可以在频道内**发布**音视频，同时也可以**订阅**其他主播发布的音视频。
- 观众：可以在频道内**订阅**音视频，不具备**发布**音视频权限。

更多概念详见[关键概念](/zh-CN/realtime-media/rtc/reference/key-concept)。

下图展示在 App 中实现音视频互动的基本工作流程：

<DocsImage alt="实现音视频互动" src="https://web-cdn.agora.io/docs-portal/img/rtc/quick-start-tech.png" displayWidth="60%" />

1. 所有用户调用 `joinChannel` 方法加入频道，并根据需要设置用户角色：

   - 互动直播：如果用户需要在频道中发流，则设为主播；如果用户只需要收流，则设为观众。
   - 视频通话：将所有用户的角色都设为主播。
2. 加入频道后，不同角色的用户具备不同的行为：

   - 所有用户默认都可以接收频道中的音视频流。
   - 主播可以在频道内发布音视频流。
   - 观众如果需要发流，可在频道内调用 `setClientRole` 方法修改用户角色，使其具备发流权限。

## 前提条件 [#前提条件]

在实现功能以前，请按照以下要求准备开发环境：

- Android Studio 4.1 以上版本。
- Android API 级别 16 或以上。
- 两台运行 Android 4.1 或以上版本的移动设备。
- 可以访问互联网的计算机。如果你的网络环境部署了防火墙，参考[应对防火墙限制](/zh-CN/realtime-media/rtc/build/setup-and-access/firewall)以正常使用声网服务。
- 一个有效的声网账号以及声网项目。请参考[开通 RTC 服务](/zh-CN/realtime-media/rtc/build/setup-and-access/enable-service)从声网控制台获得 App ID 和临时 Token。

:::warning[注意]
      临时 Token 的有效期是 24 小时。Token 过期会导致加入频道失败。
      :::

## 创建项目 [#创建项目]

本小节介绍如何创建项目并为项目添加体验实时互动所需的权限。

1. (可选) 创建新项目。详见 [Create a project](https://developer.android.com/studio/projects/create-project)。

    1. 打开 **Android Studio**，选择 **New Project**。
    2. 选择 **Phone and Tablet > Empty Views Activity**，点击 **Next**。
    3. 设置项目名称和存储路径，选择语言为 **Java**，点击 **Finish** 创建 Android 项目。

        :::warning[注意]
        创建项目后，**Android Studio** 会自动开始同步 Gradle，稍等片刻至同步成功后再进行下一步操作。
        :::

2. 添加网络及设备权限。

    打开 `/app/src/main/AndroidManifest.xml` 文件，在 `</application>` 后面添加如下权限：

    ```xml
    <!--必要权限-->
    <uses-permission android:name="android.permission.INTERNET"/>

    <!--可选权限-->
    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <!-- 对于 Android 12.0 及以上且集成 v4.1.0 以下 SDK 的设备，还需要添加以下权限 -->
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
    <!-- 对于 Android 12.0 及以上设备，还需要添加以下权限 -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN"/>
    ```

3. 防止代码混淆。

   如果你的 App 未开启 R8/ProGuard 缩减和混淆，无需额外配置混淆规则。如果你的 App 开启了 R8/ProGuard 缩减和混淆，请根据集成方式选择以下任一配置：

   - 快速配置：打开 `/app/proguard-rules.pro` 文件，添加如下规则，保留所有声网 SDK 相关类：

     ```java
     -keep class io.agora.**{*;}
     -dontwarn io.agora.**
     ```

   - 精细化配置：如果你对 App 包体积、代码缩减或混淆效果有更高要求，可以使用 SDK 提供的 `consumer-proguard-rules.pro` 文件。该文件仅保留 Native 层通过 JNI 访问所需的 Java 类、成员和方法，使其他 SDK Java 代码可继续参与 R8/ProGuard 的缩减、优化和混淆。

     通过 AAR 集成时，`consumer-proguard-rules.pro` 已包含在 AAR 中，打包时会自动生效，无需手动拷贝。通过 JAR 集成时，你需要将 `consumer-proguard-rules.pro` 手动拷贝到工程中，并在 `build.gradle` 中添加该文件，例如：

     ```text
     android {
         buildTypes {
             release {
                 minifyEnabled true
                 proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
                         'proguard-rules.pro',
                         'consumer-proguard-rules.pro'
             }
         }
     }
     ```

## 集成 SDK [#集成-sdk]

你可以选用以下任一方式集成声网实时互动 SDK。

<Tabs defaultValue="maven" groupId="integrate-sdk" persist>
<TabsList>
  <TabsTrigger value="maven">通过 Gradle 自动集成</TabsTrigger>
  <TabsTrigger value="manual">手动集成</TabsTrigger>
</TabsList>

<TabsContent value="maven">

1. 进入项目根目录，打开 `settings.gradle` 文件，在 `dependencyResolutionManagement` 的 `repositories` 中添加声网 Maven CDN 仓库。

    请保留项目中已有的 Maven 仓库配置，尤其是 `mavenCentral()`，并额外添加声网 Maven CDN 仓库。

    :::warning[注意]
    如果你在 `settings.gradle` 中找不到 `dependencyResolutionManagement` 字段，可能是因为你的 Android Gradle Plugin 版本低于 v7.1.0。

        相关信息请参考 [Android Gradle Plugin Release Note v7.1.0](https://developer.android.com/build/releases/past-releases/agp-7-1-0-release-notes)。
    :::

    ```groovy
    dependencyResolutionManagement {
        repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
        repositories {
            mavenCentral()
            maven { url = 'https://download.shengwang.cn/maven/' }
        }
    }
    ```

    若你的 Android Gradle Plugin 版本低于 v7.1.0，请打开项目根目录下的 `build.gradle` 文件，在 `allprojects` 的 `repositories` 中添加如下配置：

    ```groovy
    allprojects {
        repositories {
            mavenCentral()
            maven { url = 'https://download.shengwang.cn/maven/' }
        }
    }
    ```

2. 打开 `/app/build.gradle` 文件，在 `dependencies` 中添加声网 RTC SDK 的依赖。你可以从[发版说明](/zh-CN/realtime-media/rtc/reference/release-notes)中查询 SDK 的最新版本，并将 `x.y.z` 替换为具体的版本号。

    ```groovy
    ...
    dependencies {
        ...
        // 对于 4.6.0 及之后的版本
        // x.y.z 替换为具体的 SDK 版本号，如：4.6.1
        // 集成 Full SDK
        implementation 'cn.shengwang.rtc:full-sdk:x.y.z'
        // 集成 Lite SDK
        implementation 'cn.shengwang.rtc:lite-sdk:x.y.z'

        // 对于 4.6.0 之前的版本
        // x.y.z 替换为具体的 SDK 版本号，如：4.0.0 或 4.1.0-1
        // 集成 Full SDK
        implementation 'io.agora.rtc:full-sdk:x.y.z'
        // 或集成 Lite SDK
        implementation 'io.agora.rtc:lite-sdk:x.y.z'
    }
    ```

   :::warning[注意]
   Lite SDK 默认无法与 [RTC 小程序 SDK](/zh-CN/realtime-media/rtc) 互通。如果需要，请[联系技术支持](https://ticket.shengwang.cn/)。
   :::

</TabsContent>

<TabsContent value="manual">

1. 在[下载](/zh-CN/realtime-media/rtc/reference/downloads)页面下载最新版本的 Android 实时互动 SDK，并解压。

2. 打开解压文件，将以下文件或子文件夹复制到你的项目路径中。

    | 文件或子文件夹 | 项目路径 |
    | :--------------------------------- | :----------------------- |
    | `agora-rtc-sdk.jar` 文件 | `/app/libs/` |
    | `arm64-v8a` 文件夹 | `/app/src/main/jniLibs/` |
    | `armeabi-v7a` 文件夹 | `/app/src/main/jniLibs/` |
    | `x86` 文件夹 | `/app/src/main/jniLibs/` |
    | `x86_64` 文件夹 | `/app/src/main/jniLibs/` |
    | `high_level_api` 中的`include` 文件夹 | `/app/src/main/jniLibs/` |

   :::warning[注意]
   自 4.5.0 起，RTC SDK 和 RTM SDK (2.2.0 及以上版本) 都包含 `libaosl.so` 库。如果你通过 CDN 手动集成 RTC SDK 且同时集成了 RTM SDK，为避免冲突，请手动删除版本较低的 `libaosl.so` 库。4.6.0 RTC SDK `libaosl.so` 库版本为 1.3.0。
   :::

3. 在 **Android Studio** 的左侧导航栏上选择 `Project Files/app/libs/agora-rtc-sdk.jar` 文件，右键单击，在下拉菜单中选择 `add as a library`。

</TabsContent>
</Tabs>

## 创建用户界面 [#创建用户界面]

根据实时音视频互动的场景需要，为你的项目创建两个视图框，分别用于展示本地视频和远端视频。如下图所示：

<DocsImage alt="Android 本地与远端视频视图" src="https://web-cdn.agora.io/docs-portal/img/rtc/android-quick-start-ui.png" displayWidth="30%" />

复制以下代码到 `/app/src/main/res/layout/activity_main.xml` 文件中替换原有内容，即可快速创建场景所需的用户界面。

<Accordions>
<Accordion title="创建用户界面示例代码">

```xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Start Video Call!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <FrameLayout
        android:id="@+id/local_video_view_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white" />

    <FrameLayout
        android:id="@+id/remote_video_view_container"
        android:layout_width="160dp"
        android:layout_height="160dp"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:layout_marginTop="16dp"
        android:background="@android:color/darker_gray"
        tools:ignore="MissingConstraints" />

</androidx.constraintlayout.widget.ConstraintLayout>
```

</Accordion>
</Accordions>

<a id="实现流程"></a>

## 实现步骤 [#实现步骤]

本小节介绍如何实现一个实时音视频互动 App。你可以先复制完整的示例代码到你的项目中，快速体验实时音视频互动的基础功能，再按照实现步骤了解核心 API 调用。

下图展示了使用声网 RTC SDK 实现音视频互动的基本流程：

<DocsImage alt="实现流程" src="https://web-cdn.agora.io/docs-portal/img/rtc/quick-start-sequence.svg" displayWidth="70%" />

下面列出了一段实现实时互动基本流程的完整代码以供参考。复制以下代码到 `/app/src/main/java/com/example/<projectname>/MainActivity.java` 文件中替换 `package com.example.<projectname>` 后的全部内容，即可快速体验实时互动基础功能。

:::info[信息]
在 `appId`、`token` 和 `channelName` 字段中传入你在控制台获取到的 App ID、临时 Token，以及生成临时 Token 时填入的频道名。
:::

<Accordions>
<Accordion title="实现实时音视频互动示例代码">

```java
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.SurfaceView;
import android.widget.FrameLayout;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import io.agora.rtc2.ChannelMediaOptions;
import io.agora.rtc2.Constants;
import io.agora.rtc2.IRtcEngineEventHandler;
import io.agora.rtc2.RtcEngine;
import io.agora.rtc2.RtcEngineConfig;
import io.agora.rtc2.video.VideoCanvas;

public class MainActivity extends AppCompatActivity {

    // 填写声网控制台中获取的 App ID
    private String appId = "<#Your App ID#>";
    // 填写频道名
    private String channelName = "<#Your channel name#>";
    // 填写声网控制台中生成的临时 Token
    private String token = "<#Your Token#>";

    private RtcEngine mRtcEngine;

    private final IRtcEngineEventHandler mRtcEventHandler = new IRtcEngineEventHandler() {
        // 成功加入频道回调
        @Override
        public void onJoinChannelSuccess(String channel, int uid, int elapsed) {
            super.onJoinChannelSuccess(channel, uid, elapsed);
            runOnUiThread(() -> {
                Toast.makeText(MainActivity.this, "Join channel success", Toast.LENGTH_SHORT).show();
            });
        }

        // 远端用户或主播加入当前频道回调
        @Override
        public void onUserJoined(int uid, int elapsed) {
            runOnUiThread(() -> {
                // 当远端用户加入频道后，显示指定 uid 的远端视频流
                setupRemoteVideo(uid);
            });
        }

        // 远端用户或主播离开当前频道回调
        @Override
        public void onUserOffline(int uid, int reason) {
            super.onUserOffline(uid, reason);
            runOnUiThread(() -> {
                Toast.makeText(MainActivity.this, "User offline: " + uid, Toast.LENGTH_SHORT).show();
            });
        }
    };

    private void initializeAndJoinChannel() {
        try {
            // 创建 RtcEngineConfig 对象，并进行配置
            RtcEngineConfig config = new RtcEngineConfig();
            config.mContext = getBaseContext();
            config.mAppId = appId;
            config.mEventHandler = mRtcEventHandler;
            // 创建并初始化 RtcEngine
            mRtcEngine = RtcEngine.create(config);
        } catch (Exception e) {
            throw new RuntimeException("Check the error.");
        }
        // 启用视频模块
        mRtcEngine.enableVideo();

        // 创建一个 SurfaceView 对象，并将其作为 FrameLayout 的子对象
        FrameLayout container = findViewById(R.id.local_video_view_container);
        SurfaceView surfaceView = new SurfaceView (getBaseContext());
        container.addView(surfaceView);
        // 将 SurfaceView 对象传入声网实时互动 SDK，设置本地视图
        mRtcEngine.setupLocalVideo(new VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, 0));

        // 开启本地预览
        mRtcEngine.startPreview();

        // 创建 ChannelMediaOptions 对象，并进行配置
        ChannelMediaOptions options = new ChannelMediaOptions();
        // 设置用户角色为 BROADCASTER (主播) 或 AUDIENCE (观众)
        options.clientRoleType = Constants.CLIENT_ROLE_BROADCASTER;
        // 设置频道场景为 BROADCASTING (直播场景)
        options.channelProfile = Constants.CHANNEL_PROFILE_LIVE_BROADCASTING;
        // 发布麦克风采集的音频
        options.publishMicrophoneTrack = true;
        // 发布摄像头采集的视频
        options.publishCameraTrack = true;
        // 自动订阅所有音频流
        options.autoSubscribeAudio = true;
        // 自动订阅所有视频流
        options.autoSubscribeVideo = true;
        // 使用临时 Token 和频道名加入频道，uid 为 0 表示引擎内部随机生成用户名
        // 成功后会触发 onJoinChannelSuccess 回调
        mRtcEngine.joinChannel(token, channelName, 0, options);
    }

    private void setupRemoteVideo(int uid) {
        FrameLayout container = findViewById(R.id.remote_video_view_container);
        SurfaceView surfaceView = new SurfaceView (getBaseContext());
        surfaceView.setZOrderMediaOverlay(true);
        container.addView(surfaceView);
        // 将 SurfaceView 对象传入声网实时互动 SDK，设置远端视图
        mRtcEngine.setupRemoteVideo(new VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, uid));
    }

    private static final int PERMISSION_REQ_ID = 22;

    // 获取体验实时音视频互动所需的录音、摄像头等权限
    private String[] getRequiredPermissions(){
        // 判断 targetSDKVersion 31 及以上时所需的权限
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
            return new String[]{
                    Manifest.permission.RECORD_AUDIO, // 录音权限
                    Manifest.permission.CAMERA, // 摄像头权限
                    Manifest.permission.READ_PHONE_STATE, // 读取电话状态权限
                    Manifest.permission.BLUETOOTH_CONNECT // 蓝牙连接权限
            };
        } else {
            return new String[]{
                    Manifest.permission.RECORD_AUDIO,
                    Manifest.permission.CAMERA
            };
        }
    }

    private boolean checkPermissions() {
        for (String permission : getRequiredPermissions()) {
            int permissionCheck = ContextCompat.checkSelfPermission(this, permission);
            if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 如果已经授权，则初始化 RtcEngine 并加入频道
        if (checkPermissions()) {
            initializeAndJoinChannel();
        } else {
            ActivityCompat.requestPermissions(this, getRequiredPermissions(), PERMISSION_REQ_ID);
        }
    }

    // 系统权限申请回调
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (checkPermissions()) {
            initializeAndJoinChannel();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mRtcEngine != null) {
            // 停止本地视频预览
            mRtcEngine.stopPreview();
            // 离开频道
            mRtcEngine.leaveChannel();
            mRtcEngine = null;
            // 销毁引擎
            RtcEngine.destroy();
        }
    }
}
```

</Accordion>
</Accordions>

### 处理权限请求 [#处理权限请求]

本小节介绍如何导入 Android 相关的类并获取 Android 设备的摄像头、录音等权限。

1. 导入 Android 相关的类

    ```java
    import android.Manifest;
    import android.content.pm.PackageManager;
    import android.os.Bundle;
    import android.view.SurfaceView;
    import android.widget.FrameLayout;
    import android.widget.Toast;

    import androidx.annotation.NonNull;
    import androidx.appcompat.app.AppCompatActivity;
    import androidx.core.app.ActivityCompat;
    import androidx.core.content.ContextCompat;
    ```

2. 获取 Android 权限

    启动应用程序时，检查是否已在 App 中授予了实现实时互动所需的权限。

    ```java
    private static final int PERMISSION_REQ_ID = 22;

    // 获取体验实时音视频互动所需的录音、摄像头等权限
    private String[] getRequiredPermissions(){
        // 判断 targetSDKVersion 31 及以上时所需的权限
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
            return new String[]{
                    Manifest.permission.RECORD_AUDIO, // 录音权限
                    Manifest.permission.CAMERA, // 摄像头权限
                    Manifest.permission.READ_PHONE_STATE, // 读取电话状态权限
                    Manifest.permission.BLUETOOTH_CONNECT // 蓝牙连接权限
            };
        } else {
            return new String[]{
                    Manifest.permission.RECORD_AUDIO,
                    Manifest.permission.CAMERA
            };
        }
    }

    private boolean checkPermissions() {
        for (String permission : getRequiredPermissions()) {
            int permissionCheck = ContextCompat.checkSelfPermission(this, permission);
            if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }
    ```

### 导入声网相关的类 [#导入声网相关的类]

导入声网 RTC SDK 相关的类和接口：

```java
import io.agora.rtc2.ChannelMediaOptions;
import io.agora.rtc2.Constants;
import io.agora.rtc2.IRtcEngineEventHandler;
import io.agora.rtc2.RtcEngine;
import io.agora.rtc2.RtcEngineConfig;
import io.agora.rtc2.video.VideoCanvas;
```

### 定义 App ID 和 Token [#定义-app-id-和-token]

传入从声网控制台获取的 App ID、临时 Token，以及生成临时 Token 时填入的频道名，用于后续初始化引擎和加入频道。

```java
// 填写声网控制台中获取的 App ID
private String appId = "<#Your App ID#>";
// 填写频道名
private String channelName = "<#Your channel name#>";
// 填写声网控制台中生成的临时 Token
private String token = "<#Your Token#>";
```

### 初始化引擎 [#初始化引擎]

调用 `create` [2/2] 方法初始化 `RtcEngine`。

:::warning[注意]
在初始化 SDK 前，需确保终端用户已经充分了解并同意相关的隐私政策。
:::

```java
private RtcEngine mRtcEngine;

private final IRtcEngineEventHandler mRtcEventHandler = new IRtcEngineEventHandler() {
    ...
};

// 创建 RtcEngineConfig 对象，并进行配置
RtcEngineConfig config = new RtcEngineConfig();
config.mContext = getBaseContext();
config.mAppId = appId;
config.mEventHandler = mRtcEventHandler;
// 创建并初始化 RtcEngine
mRtcEngine = RtcEngine.create(config);
```

### 启用视频模块 [#启用视频模块]

按照以下步骤启用视频模块：

1. 调用 `enableVideo` 方法，启用视频模块。
2. 调用 `setupLocalVideo` 方法初始化本地视图，同时设置本地的视频显示属性。
3. 调用 `startPreview` 方法，开启本地视频预览。

```java
// 启用视频模块
mRtcEngine.enableVideo();

// 创建一个 SurfaceView 对象，并将其作为 FrameLayout 的子对象
FrameLayout container = findViewById(R.id.local_video_view_container);
SurfaceView surfaceView = new SurfaceView (getBaseContext());
container.addView(surfaceView);
// 将 SurfaceView 对象传入声网实时互动 SDK，设置本地视图
mRtcEngine.setupLocalVideo(new VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, 0));

// 开启本地预览
mRtcEngine.startPreview();
```

### 加入频道并发布音视频流 [#加入频道并发布音视频流]

调用 `joinChannel` [2/2] 加入频道。在 `ChannelMediaOptions` 中进行如下配置：

- 设置频道场景为 `BROADCASTING` (直播场景) 并设置用户角色设置为 `BROADCASTER` (主播) 或 `AUDIENCE` (观众)。
- 将 `publishMicrophoneTrack` 和 `publishCameraTrack` 设置为 `true`，发布麦克风采集的音频和摄像头采集的视频。
- 将 `autoSubscribeAudio` 和 `autoSubscribeVideo` 设置为 `true`，自动订阅所有音视频流。

```java
// 创建 ChannelMediaOptions 对象，并进行配置
ChannelMediaOptions options = new ChannelMediaOptions();
// 设置用户角色为 BROADCASTER (主播) 或 AUDIENCE (观众)
options.clientRoleType = Constants.CLIENT_ROLE_BROADCASTER;
// 设置频道场景为 BROADCASTING (直播场景)
options.channelProfile = Constants.CHANNEL_PROFILE_LIVE_BROADCASTING;
// 发布麦克风采集的音频
options.publishMicrophoneTrack = true;
// 发布摄像头采集的视频
options.publishCameraTrack = true;
// 自动订阅所有音频流
options.autoSubscribeAudio = true;
// 自动订阅所有视频流
options.autoSubscribeVideo = true;
// 使用临时 Token 和频道名加入频道，uid 为 0 表示引擎内部随机生成用户名
// 成功后会触发 onJoinChannelSuccess 回调
mRtcEngine.joinChannel(token, channelName, 0, options);
```

### 设置远端视图 [#设置远端视图]

调用 `setupRemoteVideo` 方法初始化远端用户视图，同时设置远端用户的视图在本地显示属性。你可以通过 `onUserJoined` 回调获取远端用户的 `uid`。

```java
private void setupRemoteVideo(int uid) {
    FrameLayout container = findViewById(R.id.remote_video_view_container);
    SurfaceView surfaceView = new SurfaceView (getBaseContext());
    surfaceView.setZOrderMediaOverlay(true);
    container.addView(surfaceView);
    // 将 SurfaceView 对象传入声网实时互动 SDK，设置远端视图
    mRtcEngine.setupRemoteVideo(new VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, uid));
}
```

### 实现常用回调 [#实现常用回调]

根据使用场景，定义必要的回调。以下示例代码展示如何实现 `onJoinChannelSuccess`、 `onUserJoined` 和 `onUserOffline` 回调。

```java
// 成功加入频道回调
@Override
public void onJoinChannelSuccess(String channel, int uid, int elapsed) {
    super.onJoinChannelSuccess(channel, uid, elapsed);
    runOnUiThread(() -> {
        Toast.makeText(MainActivity.this, "Join channel success", Toast.LENGTH_SHORT).show();
    });
}

// 远端用户或主播加入当前频道回调
@Override
public void onUserJoined(int uid, int elapsed) {
    runOnUiThread(() -> {
        // 当远端用户加入频道后，显示指定 uid 的远端视频流
        setupRemoteVideo(uid);
    });
}

// 远端用户或主播离开当前频道回调
@Override
public void onUserOffline(int uid, int reason) {
    super.onUserOffline(uid, reason);
    runOnUiThread(() -> {
        Toast.makeText(MainActivity.this, "User offline: " + uid, Toast.LENGTH_SHORT).show();
    });
}
```

### 开始音视频互动 [#开始音视频互动]

在 `onCreate` 中调用一系列方法加载界面布局、检查 App 是否获取实时互动所需权限，并加入频道开始音视频互动。

```java
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // 如果已经授权，则初始化 RtcEngine 并加入频道
    if (checkPermissions()) {
        initializeAndJoinChannel();
    } else {
        ActivityCompat.requestPermissions(this, getRequiredPermissions(), PERMISSION_REQ_ID);
    }
}
```

### 结束音视频互动 [#结束音视频互动]

按照以下步骤结束音视频互动：

1. 调用 `stopPreview` 停止视频预览。
2. 调用 `leaveChannel` 离开当前频道，释放所有会话相关的资源。
3. 调用 `destroy` 销毁引擎，并释放声网 SDK 中使用的所有资源。

   :::warning[注意]
   - 该方法为同步调用。需要等待引擎资源释放后才能执行其他操作，因此建议在子线程中调用该方法，避免主线程阻塞。
      - 调用 `destroy` 后，你将无法再使用 SDK 的所有方法和回调。如需再次使用实时音视频互动功能，你必须重新创建一个新的引擎。详见[初始化引擎](#初始化引擎)。
   :::

    ```java
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mRtcEngine != null) {
            // 停止本地视频预览
            mRtcEngine.stopPreview();
            // 离开频道
            mRtcEngine.leaveChannel();
            mRtcEngine = null;
            // 销毁引擎
            RtcEngine.destroy();
        }
    }
    ```

## 调试 App [#调试-app]

按照以下步骤测试直播 App：

1. 开启 Android 设备的开发者选项，打开 USB 调试，通过 USB 连接线将 Android 设备接入电脑，并在 Android 设备选项中勾选你的 Android 设备。

2. 在 Android Studio 中，点击 <InlineImage alt="同步 Gradle 项目" src="https://web-cdn.agora.io/docs-files/1689672727614" width={25} /> (**Sync Project with Gradle Files**) 进行 Gradle 同步。

3. 待同步成功后，点击 <InlineImage alt="运行" src="https://web-cdn.agora.io/docs-files/1687670569781" width={25} /> (**Run 'app'**) 开始编译。片刻后，App 便会安装到你的 Android 设备上。

4. 启动 App，授予录音和摄像头权限，如果你将用户角色设置为主播，便会在本地视图中看到自己。

5. 使用第二台 Android 设备，重复以上步骤，在该设备上安装 App、打开 App 加入频道，观察测试结果：
    - 如果两台设备均作为主播加入频道，则可以看到对方并且听到对方的声音。
    - 如果两台设备分别作为主播和观众加入，则主播可以在本地视频窗口看到自己；观众可以在远端视频窗口看到主播、并听到主播的声音。

<DocsImage alt="Android 加入频道后的音视频互动界面" src="https://web-cdn.agora.io/docs-portal/img/rtc/android-quick-start-inchannel.png" displayWidth="30%" />

## 后续步骤 [#后续步骤]

在完成音视频互动后，你可以阅读以下文档进一步了解：

- 本文的示例使用了临时 Token 加入频道。在测试或生产环境中，为保证通信安全，声网推荐从服务器中获取 Token，详情请参考[使用 Token 鉴权](/zh-CN/realtime-media/rtc/build/setup-and-access/token-authentication)。
- 如果你想要实现极速直播场景，可以在实时音视频互动的基础上，通过修改观众端的延时级别为低延时 (`AUDIENCE_LATENCY_LEVEL_LOW_LATENCY`) 实现。详见[实现极速直播](/zh-CN/realtime-media/rtc/build/advanced-channel/ls-quick-start)。

## 参考信息 [#参考信息]

### 示例项目 [#示例项目]

声网提供了开源的实时音视频互动示例项目供你参考，你可以前往下载或查看其中的源代码。

<Cards>
  <Card title={<RepositoryCardTitle provider="gitee">Agora-RTC-QuickStart-Android</RepositoryCardTitle>} href="https://gitee.com/agoraio-community/Agora-RTC-QuickStart/tree/main/Android/Agora-RTC-QuickStart-Android" />
  <Card title={<RepositoryCardTitle provider="github">Agora-RTC-QuickStart-Android</RepositoryCardTitle>} href="https://github.com/AgoraIO-Community/Agora-RTC-QuickStart/tree/main/Android/Agora-RTC-QuickStart-Android" />
</Cards>

### 常见问题 [#常见问题]

- [直播场景下，如何监听远端观众角色用户加入/离开频道的事件？](/zh-CN/reference/faq/integration/audience_event)
- [如何处理视频黑屏问题？](/zh-CN/reference/faq/quality/video_blank)
- [为什么我无法打开摄像头？](/zh-CN/reference/faq/quality/video_camera)
- [如何处理频道相关常见问题？](/zh-CN/reference/faq/integration/channel)
- [如何设置日志文件？](/zh-CN/reference/faq/integration/set_log_file)
- [为什么部分 Android 版本应用锁屏或切后台后采集音视频无效？](/zh-CN/reference/faq/quality/android_background)
- [为什么 SDK 中使用的是动态库而不是静态库？](/zh-CN/reference/faq/integration/dynamic_or_static_library)

### 相关文档 [#相关文档]

- [SDK 错误码](/zh-CN/realtime-media/rtc/reference/error-code)
- [频道连接状态管理](/zh-CN/realtime-media/rtc/build/initialize-and-channel/channel-connection)


























