本文介绍如何使用 Agora 语音通话 SDK 快速实现语音通话。

示例项目

我们在 GitHub 上提供一个开源的一对一语音通话示例项目 Agora-Android-Voice-Tutorial-1to1 供你参考。

前提条件

  • Android Studio 3.0 或以上版本
  • Android SDK API 等级 16 或以上
  • 支持 Android 4.1 或以上版本的移动设备
  • 有效的 Agora 账户App ID
如果你的网络环境部署了防火墙,请根据应用企业防火墙限制打开相关端口。

准备开发环境

本节介绍如何创建项目,将 Agora 语音 SDK 集成进你的项目中,并添加相应的设备权限。

创建 Android 项目

参考以下步骤创建一个 Android 项目。若已有 Android 项目,可以直接查看集成 SDK

创建 Android 项目
  1. 打开 Android Studio,点击 Start a new Android Studio project
  2. Select a Project Template 界面,选择 Phone and Tablet > Empty Activity,然后点击 Next
  3. Configure Your Project 界面,依次填入以下内容:
    • Name:你的 Android 项目名称,如 HelloAgora
    • Package name:你的项目包的名称,如 io.agora.helloagora
    • Save location:项目的存储路径
    • Language:项目的编程语言,如 Java
    • Minimum API level:项目的最低 API 等级

然后点击 Finish。根据屏幕提示,安装可能需要的插件。

上述步骤使用 Android Studio 3.6.2 示例。你也可以直接参考 Android Studio 官网文档创建首个应用

集成 SDK

选择如下任意一种方式将 Agora 语音 SDK 集成到你的项目中。

方法一:使用 JCenter 自动集成

在项目的 /app/build.gradle 文件中,添加如下行:

...
dependencies {
    ...
    // x.y.z 请填写具体版本号,如:3.0.0
    // 可通过 SDK 发版说明取得最新版本号
    implementation 'io.agora.rtc:voice-sdk:x.y.z'
}
请点击查看发版说明获取最新版本号。

方法二:手动复制 SDK 文件

  1. 前往 SDK 下载页面,获取最新版的 Agora 语音 SDK,然后解压。
  2. 将 SDK 包内 libs 路径下的如下文件,拷贝到你的项目路径下:
文件或文件夹 项目路径
agora-rtc-sdk.jar 文件 /app/libs/
arm-v8a 文件夹 /app/src/main/jniLibs/
armeabi-v7a 文件夹 /app/src/main/jniLibs/
x86 文件夹 /app/src/main/jniLibs/
x86_64 文件夹 /app/src/main/jniLibs/
  • 如果你的项目无需使用加密功能,建议删除 SDK 包内的 libagora-crypto.so 文件。
  • 如果你使用的是 armeabi 库,可以把 armeabi-v7a 内的文件放入 armeabli 文件夹内。如果遇到不兼容的情况,请联系 sales@agora.io 咨询适配相关问题。

添加项目权限

根据场景需要,在 /app/src/main/AndroidManifest.xml 文件中添加如下行,获取相应的设备权限:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="io.agora.tutorials1v1acall">

   <uses-permission android:name="android.permission.READ_PHONE_STATE" />   
   <uses-permission android:name="android.permission.INTERNET" />
   <uses-permission android:name="android.permission.RECORD_AUDIO" />
   <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
   <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
   <uses-permission android:name="android.permission.BLUETOOTH" />
   <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
   // 如果你的场景中涉及读取外部存储,需添加如下权限:
   <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
   // 如果你使用的是 Android 10.0 及以上设备,还需要添加如下权限:
   <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />

...
</manifest>

如果你的 targetSdkVersion ≥ 29,还需要在 AndroidManifest.xml 文件的 <application> 区域添加如下行:

   <application
      android:requestLegacyExternalStorage="true">
      ...
   </application>

防止代码混淆

app/proguard-rules.pro 文件中添加如下行,防止混淆 Agora SDK 的代码:

-keep class io.agora.**{*;}

实现语音通话

本节介绍如何实现语音通话。语音通话的 API 调用时序见下图:

1. 创建用户界面

根据场景需要,为你的项目创建语音通话的用户界面。若已有界面,可以直接查看导入类

你可以参考 Agora-Android-Voice-Tutorial-1to1 示例项目的 activity_voice_chat_view.xml 文件中的代码。

创建 UI 示例
<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_voice_chat_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="io.agora.tutorials1v1acall.VoiceChatViewActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="@dimen/activity_vertical_margin"
        android:orientation="vertical">

        <TextView
            android:id="@+id/quick_tips_when_use_agora_sdk"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="40dp"
            android:layout_marginLeft="@dimen/activity_horizontal_margin"
            android:layout_marginStart="@dimen/activity_horizontal_margin"
            android:gravity="center_vertical|start"
            android:text="1. Default channel name is voiceDemoChannel1\n2. Waiting for remote users\n3. This demo only supports 1v1 voice calling" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:orientation="horizontal">

            <ImageView
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="20"
                android:onClick="onLocalAudioMuteClicked"
                android:scaleType="centerInside"
                android:src="@drawable/btn_mute" />

            <ImageView
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="20"
                android:onClick="onSwitchSpeakerphoneClicked"
                android:scaleType="centerInside"
                android:src="@drawable/btn_speaker" />

            <ImageView
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="20"
                android:onClick="onEncCallClicked"
                android:scaleType="centerInside"
                android:src="@drawable/btn_end_call" />

        </LinearLayout>

    </LinearLayout>

</RelativeLayout>

2. 导入类

在项目的 Activity 文件中添加如下行:

import io.agora.rtc.IRtcEngineEventHandler;
import io.agora.rtc.RtcEngine;

3. 获取设备权限

调用 checkSelfPermission 方法,在开启 Activity 时检查并获取 Android 移动设备的麦克风使用权限。

// Java
private static final int PERMISSION_REQ_ID_RECORD_AUDIO = 22;

// App 运行时确认麦克风的使用权限。
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_voice_chat_view);

    // 获取权限后,初始化 RtcEngine,并加入频道。
    if (checkSelfPermission(Manifest.permission.RECORD_AUDIO, PERMISSION_REQ_ID_RECORD_AUDIO)) {
        initAgoraEngineAndJoinChannel();
    }
}

private void initAgoraEngineAndJoinChannel() {
    initializeAgoraEngine();
    joinChannel(); 
}

public boolean checkSelfPermission(String permission, int requestCode) {
    Log.i(LOG_TAG, "checkSelfPermission " + permission + " " + requestCode);
    if (ContextCompat.checkSelfPermission(this,
            permission)
            != PackageManager.PERMISSION_GRANTED) {

        ActivityCompat.requestPermissions(this,
                new String[]{permission},
                requestCode);
        return false;
    }
    return true;
}
// Kotlin
// app 运行时确认麦克风的使用权限。
override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  setContentView(R.layout.activity_voice_chat_view)

  // 获取权限后,初始化 RtcEngine,并加入频道。
  if (checkSelfPermission(Manifest.permission.RECORD_AUDIO, PERMISSION_REQ_ID_RECORD_AUDIO)) {
    initAgoraEngineAndJoinChannel()
  }
}

private fun initAgoraEngineAndJoinChannel() {
  initializeAgoraEngine()
  joinChannel()
}

private fun checkSelfPermission(permission. String, requestCode: Int): Boolean {
  Log.i(LOG_TAG, "checkSelfPermission $permission $reuqestCode")
  if (ContextCompat.checkSelfPermission(this, 
          permission) != PackageManager.PERMISSION_GRANTED) {

    ActivityCompat.requestPermission(this
            arrayOf(permission),
            requestCode)
    return false
  }
  return true
}

4. 初始化 RtcEngine

在调用其他 Agora API 前,需要创建并初始化 RtcEngine 对象。

将获取到的 App ID 添加到 string.xml 文件中的 agora_app_id 一栏。调用 create 方法,传入获取到的 App ID,即可初始化 RtcEngine。

你还根据场景需要,在初始化时注册想要监听的回调事件,如远端用户下线或静音回调。注意不要在这些回调中进行 UI 操作。

// Java
private RtcEngine mRtcEngine;
private final IRtcEngineEventHandler mRtcEventHandler = new IRtcEngineEventHandler() {  

    // 注册 onUserOffline 回调。远端用户离开频道后,会触发该回调。 
    @Override
    public void onUserOffline(final int uid, final int reason) { 
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                onRemoteUserLeft(uid, reason);
            }
        });
    }

    // 注册 onUserMuteAudio 回调。远端用户静音后,会触发该回调。
    @Override
    public void onUserMuteAudio(final int uid, final boolean muted) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                onRemoteUserVoiceMuted(uid, muted);
            }
        });
    }
};

...
// 调用 Agora SDK 的方法初始化 RtcEngine。
private void initializeAgoraEngine() {
    try {
        mRtcEngine = RtcEngine.create(getBaseContext(), getString(R.string.agora_app_id), mRtcEventHandler);
        mRtcEngine.setChannelProfile(Constants.CHANNEL_PROFILE_COMMUNICATION);
    } catch (Exception e) {
        Log.e(LOG_TAG, Log.getStackTraceString(e));

        throw new RuntimeException("NEED TO check rtc sdk init fatal error\n" + Log.getStackTraceString(e));
    }
}
// Kotlin
private var mRtcEngine: RtcEngine? = null
private val mRtcEventHandler = object : IRtcEngineEventHandler() {

  // 注册 onUserOffline 回调。远端用户离开频道后,会触发该回调。
  override fun onUserOffline(uid: Int, reason: Int) {
    runOnUiThread { onRemoteUserLeft() }
  }

  // 注册 onUserMuteAudio 回调。远端用户静音后,会触发该回调。
  override fun onUserMuteAudio(uid: Int, muted: Boolean) {
    runOnUiThread { onRemoteUserVoiceMuted(uid, muted)}
  }
}

...

// 调用 Agora SDK 的方法初始化 RtcEngine。
private fun initializeAgoraEngine() {
  try {
    mRtcEngine = RtcEngine.create(baseContext, getString(R.string.agora_app_id), mRtcEventHandler)
  } catch (e: Exception) {
    Log.e(LOG_TAG, Log.getStackTraceString(e))

    throw RuntimeException("NEED TO check rtc sdk init fatal error\n" + Log.getStackTraceString(e))
  }
}

5. 加入频道

完成初始化后,你就可以调用 joinChannel 方法加入频道。你需要在该方法中传入如下参数:

  • token:传入能标识用户角色和权限的 Token。可设为如下一个值:

    • NULL
    • 临时 Token。临时 Token 服务有效期为 24 小时。你可以在控制台里生成一个临时 Token,详见获取临时 Token
    • 在你的服务器端生成的 Token。在安全要求高的场景下,我们推荐你使用此种方式生成的 Token,详见生成 Token
    若项目已启用 App 证书,请使用 Token。
  • channelName:传入能标识频道的频道 ID。App ID 相同、频道 ID 相同的用户会进入同一个频道。

  • uid: 本地用户的 ID。数据类型为整型,且频道内每个用户的 uid 必须是唯一的。若将 uid 设为 0,则 SDK 会自动分配一个 uid,并在 onJoinChannelSuccess 回调中报告。

更多的参数设置注意事项请参考 joinChannel 接口中的参数描述。

// Java
private void joinChannel() {
    String accessToken = getString(R.string.agora_access_token);
    if (TextUtils.equals(accessToken, "") || TextUtils.equals(accessToken, "#YOUR ACCESS TOKEN#")) {
        accessToken = null;
    }

    // 调用 Agora SDK 的 joinChannel 方法加入频道。未指定 uid,SDK 会自动分配一个。
    mRtcEngine.joinChannel(accessToken, "voiceDemoChannel1", "Extra Optional Data", 0);
}
// Kotlin
private fun joinChannel() {

  // 调用 Agora SDK 的 joinChannel 方法加入频道。未指定 uid,SDK 会自动分配一个。
  mRtcEngine!!.joinChannel(token, "demoChannel1", "Extra Optional Data", 0)
}

6. 离开频道

根据场景需要,如结束通话、关闭 App 或 App 切换至后台时,调用 leaveChannel 离开当前通话频道。

// Java
private void leaveChannel() {
    mRtcEngine.leaveChannel();
}
// Kotlin
private fun leaveChannel() {
  mRtcEngine!!.leaveChannel()
}

示例代码

你可以在 Agora-Android-Voice-Tutorial-1to1 示例项目的 VoiceChatViewActivity.java 文件中查看完整的源码和代码逻辑。

运行项目

在 Android 设备中运行该项目。当成功开始语音通话时,你可以听到对方的说话声音。

相关链接

我们在 GitHub 上提供一个开源的一对多语音通话示例项目 Group-Voice-Call。如果你需要实现一对多群聊场景,可以前往下载或查看源代码。

使用 Agora 语音通话 SDK 开发过程中,你还可以参考如下文档: