使用 Agora SDK 开发 React Native 视频通话 App

article/2025/10/13 7:53:46

在 React Native 的应用中,从头开始添加视频通话功能是很复杂的。要保证低延迟、负载平衡,还要注意管理用户事件状态,非常繁琐。除此之外,还必须保证跨平台的兼容性。

当然有个简单的方法可以做到这一点。在本次的教程中,我们将使用 Agora Video SDK 来构建一个 React Native 视频通话 App。在深入探讨程序工作之前,我们将介绍应用的结构、设置和执行。你可以在几分钟内,通过几个简单的步骤,让一个跨平台的视频通话应用运行起来。

我们将使用 Agora RTC SDK for React Native 来做例子。在这篇文章中,我使用的版本是 v3.1.6。

创建一个Agora账户

  • 在声网官网注册并登录后台。

声网 Agora 后台中的项目管理

  • 找到 "项目管理 "下的 "项目列表 "选项卡,点击蓝色的 "创建 "按钮,创建一个项目。(当提示使用 App ID+证书时,选择只使用 App ID。)记住你的 App ID,它将在开发App时用于授权你的请求。

注意:本文没有采用 Token 鉴权,建议在生产环境中运行的所有RTE App 都采用Token鉴权。有关Agora平台中基于Token的身份验证的更多信息,请在声网文档中心搜索关键词「Token」,参考相关文档。

示例项目结构

这就是我们正在构建的应用程序的结构:

.├── android├── components│ └── Permission.ts│ └── Style.ts├── ios├── App.tsx.

让我们来运行这个应用

  • 需要安装 LTS 版本的 Node.js 和 NPM。

  • 确保你有一个 Agora 账户,设置一个项目,并生成 App ID。

  • 从主分支下载并解压 ZIP 文件。

  • 运行 npm install 来安装解压目录中的 App 依赖项。

  • 导航到 ./App.tsx,将我们之前生成的 App ID 填入 appId: "<YourAppId>"

  • 如果你是为 iOS 构建,打开终端,执行 cd ios && pod install

  • 连接你的设备,并运行 npx react-native run-android / npx react-native run-ios来启动应用程序。等待几分钟来构建和启动应用程序。

  • 一旦你看到手机(或模拟器)上的主屏幕,点击设备上的开始通话按钮。(iOS模拟器不支持摄像头,所以要用实体设备代替)。

通过以上操作,你应该可以在两个设备之间进行视频聊天通话。该应用默认使用 channel-x 作为频道名称。

应用工作原理

App.tsx

这个文件包含了 React Native 视频通话App中视频通话的所有核心逻辑。

import React, {Component} from 'react'
import {Platform, ScrollView, Text, TouchableOpacity, View} from 'react-native'
import RtcEngine, {RtcLocalView, RtcRemoteView, VideoRenderMode} from 'react-native-agora'import requestCameraAndAudioPermission from './components/Permission'
import styles from './components/Style'/*** @property peerIds Array for storing connected peers* @property appId* @property channelName Channel Name for the current session* @property joinSucceed State variable for storing success*/
interface State {appId: string,token: string,channelName: string,joinSucceed: boolean,peerIds: number[],
}...

我们开始先写import声明。接下来,为应用状态定义一个接口,包含:

  • appId:Agora App ID

  • token:为加入频道而生成的Token。

  • channelName:频道名称(同一频道的用户可以通话)。

  • joinSucceed:存储是否连接成功的布尔值。

  • peerIds:一个数组,用于存储通道中其他用户的UID。

...export default class App extends Component<Props, State> {_engine?: RtcEngineconstructor(props) {super(props)this.state = {appId: YourAppId,token: YourToken,channelName: 'channel-x',joinSucceed: false,peerIds: [],}if (Platform.OS === 'android') {// Request required permissions from AndroidrequestCameraAndAudioPermission().then(() => {console.log('requested!')})}}componentDidMount() {this.init()}/*** @name init* @description Function to initialize the Rtc Engine, attach event listeners and actions*/init = async () => {const {appId} = this.statethis._engine = await RtcEngine.create(appId)await this._engine.enableVideo()this._engine.addListener('Warning', (warn) => {console.log('Warning', warn)})this._engine.addListener('Error', (err) => {console.log('Error', err)})this._engine.addListener('UserJoined', (uid, elapsed) => {console.log('UserJoined', uid, elapsed)// Get current peer IDsconst {peerIds} = this.state// If new userif (peerIds.indexOf(uid) === -1) {this.setState({// Add peer ID to state arraypeerIds: [...peerIds, uid]})}})this._engine.addListener('UserOffline', (uid, reason) => {console.log('UserOffline', uid, reason)const {peerIds} = this.statethis.setState({// Remove peer ID from state arraypeerIds: peerIds.filter(id => id !== uid)})})// If Local user joins RTC channelthis._engine.addListener('JoinChannelSuccess', (channel, uid, elapsed) => {console.log('JoinChannelSuccess', channel, uid, elapsed)// Set state variable to truethis.setState({joinSucceed: true})})}...

我们定义了一个基于类的组件:变量 _engine 将存储从 Agora SDK 导入的 RtcEngine 类实例。这个实例提供了主要的方法,我们的应用程序可以调用这些方法来使用SDK的功能。

在构造函数中,设置状态变量,并为 Android 上的摄像头和麦克风获取权限。(我们使用了下文所述的 permission.ts 的帮助函数)当组件被挂载时,我们调用 init 函数 ,使用 App ID 初始化 RTC 引擎。它还可以通过调用 engine 实例上的 enableVideo 方法来启用视频。(如果省略这一步,SDK 可以在纯音频模式下工作。)

init函数还为视频调用中的各种事件添加了事件监听器。例如,UserJoined 事件为我们提供了用户加入频道时的 UID。我们将这个 UID 存储在我们的状态中,以便在以后渲染他们的视频时使用。

注意:如果在我们加入之前有用户连接到频道,那么在他们加入频道之后,每个用户都会被触发一个 UserJoined 事件。

.../*** @name startCall* @description Function to start the call*/startCall = async () => {// Join Channel using null token and channel nameawait this._engine?.joinChannel(this.state.token, this.state.channelName, null, 0)}/*** @name endCall* @description Function to end the call*/endCall = async () => {await this._engine?.leaveChannel()this.setState({peerIds: [], joinSucceed: false})}render() {return (<View style={styles.max}><View style={styles.max}><View style={styles.buttonHolder}><TouchableOpacityonPress={this.startCall}style={styles.button}><Text style={styles.buttonText}> Start Call </Text></TouchableOpacity><TouchableOpacityonPress={this.endCall}style={styles.button}><Text style={styles.buttonText}> End Call </Text></TouchableOpacity></View>{this._renderVideos()}</View></View>)}_renderVideos = () => {const {joinSucceed} = this.statereturn joinSucceed ? (<View style={styles.fullView}><RtcLocalView.SurfaceViewstyle={styles.max}channelId={this.state.channelName}renderMode={VideoRenderMode.Hidden}/>{this._renderRemoteVideos()}</View>) : null}_renderRemoteVideos = () => {const {peerIds} = this.statereturn (<ScrollViewstyle={styles.remoteContainer}contentContainerStyle={{paddingHorizontal: 2.5}}horizontal={true}>{peerIds.map((value, index, array) => {return (<RtcRemoteView.SurfaceViewstyle={styles.remote}uid={value}channelId={this.state.channelName}renderMode={VideoRenderMode.Hidden}zOrderMediaOverlay={true}/>)})}</ScrollView>)}
}

接下来,还有开始和结束视频聊天通话的方法。 joinChannel 方法接收 Token、频道名、其他可选信息和一个可选的 UID(如果你将 UID 设置为 0,系统会自动为本地用户分配 UID)。

我们还定义了渲染方法,用于显示开始和结束通话的按钮,以及显示本地视频源和远程用户的视频源。我们定义了 _renderVideos 方法 来渲染我们的视频源,使用 peerIds 数组在滚动视图中渲染。

为了显示本地用户的视频源,我们使用 <RtcLocalView.SurfaceView> 组件,需要提供 channelIdrenderMode 。连接到同一 个 channelId 的用户可以相互通信 ,而 renderMode 用于将视频放入视图中或通过缩放来填充视图。

为了显示远程用户的视频源,我们使用 SDK 中的 <RtcLocalView.SurfaceView> 组件,它可以获取远程用户的 UID 以及 channelIdrenderMode

Permission.ts

import {PermissionsAndroid} from 'react-native'/*** @name requestCameraAndAudioPermission* @description Function to request permission for Audio and Camera*/
export default async function requestCameraAndAudioPermission() {try {const granted = await PermissionsAndroid.requestMultiple([PermissionsAndroid.PERMISSIONS.CAMERA,PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,])if (granted['android.permission.RECORD_AUDIO'] === PermissionsAndroid.RESULTS.GRANTED&& granted['android.permission.CAMERA'] === PermissionsAndroid.RESULTS.GRANTED) {console.log('You can use the cameras & mic')} else {console.log('Permission denied')}} catch (err) {console.warn(err)}
}

导出一个函数,向Android上的操作系统申请摄像头和麦克风的权限。

Style.ts

import {Dimensions, StyleSheet} from 'react-native'const dimensions = {width: Dimensions.get('window').width,height: Dimensions.get('window').height,
}export default StyleSheet.create({max: {flex: 1,},buttonHolder: {height: 100,alignItems: 'center',flex: 1,flexDirection: 'row',justifyContent: 'space-evenly',},button: {paddingHorizontal: 20,paddingVertical: 10,backgroundColor: '#0093E9',borderRadius: 25,},buttonText: {color: '#fff',},fullView: {width: dimensions.width,height: dimensions.height - 100,},remoteContainer: {width: '100%',height: 150,position: 'absolute',top: 5},remote: {width: 150,height: 150,marginHorizontal: 2.5},noUserText: {paddingHorizontal: 10,paddingVertical: 5,color: '#0093E9',},
})

Style.ts 文件包含了组件的 样式。

这就是快速开发一个 React Native 视频聊天通话 App 的方法。你可以参考 Agora React Native API Reference 去查看可以帮助你快速添加更多功能的方法,比如将摄像头和麦克风静音,设置视频配置文件和音频混合等等。

获取更多文档、Demo、技术帮助

  • 获取 SDK 开发文档,可访问声网文档中心。
  • 如需参考各类场景 Demo,可访问下载中心获取。
  • 了解更多教程、RTE 技术干货与技术活动,可访问声网开发者社区。

注册声网Agora开发者账号,快速开始。


http://chatgpt.dhexx.cn/article/feuGJ6PX.shtml

相关文章

MySQL联合索引底层数据结构是怎样的

目录 1. 联合索引数据结构图 2.联合索引是如何进行排序的 3. 联合索引查询特点 1. 联合索引数据结构图 如下图所示联合索引的数据结构, 通过name&#xff0c;age&#xff0c;position三个字典进行一个联合索引&#xff0c;构建B树索引结构。 2.联合索引是如何进行排序的 B树…

MySQL联合索引在B+树如何存储以及寻址

文章目录 引入Innodb B树联合索引存储以及寻址总结 引入 最近找工作&#xff0c; 去一家三方支付公司面试&#xff0c;前面得过程还挺好&#xff0c;所有的提都回答对了&#xff08;心里暗自窃喜应该能拿到高工资offer&#xff0c;迎娶白富美&#xff0c;然后走向人生巅峰&…

MySQL联合索引生效规则

最近项目中一张表数量测试达到千万级别&#xff0c;考虑加索引&#xff0c;对于单列索引及联合索引&#xff08;复合索引&#xff09;简单学习了下&#xff0c;做一下笔记。 联合索引生效前提&#xff1a;因为联合索引有顺序讲究&#xff0c;联合索引的第一个字段是引导列&…

mysql联合索引的数据结构

一、本文主要讲解的内容有&#xff1a; 联合索引在B树上的存储结构联合索引的查找方式为什么会有最左前缀匹配原则 在分享这篇文章之前&#xff0c;我在网上查了关于MySQL联合索引在B树上的存储结构这个问题&#xff0c;翻阅了很多博客和技术文章&#xff0c;其中有几篇讲述的…

mysql联合索引有效和失效的情况分析

关于mysql的索引&#xff0c;是mysql优化一个非常重要的方面。那么关于索引是否有效就是非常关键了。很多人设计了索引&#xff0c;但是发现依旧很慢。那么这个时候就判断sql的索引执行情况非常重要了。网上有大量的博主也写过不少类似的文章&#xff0c;但是关于联合索引的具体…

MySQL联合索引(abc)命中规则

1.建表 mysql创建一张表&#xff0c;表名&#xff1a;‘test_models’ id列为 主键&#xff0c;int类型 &#xff0c;自增a,b,c,d,e 全部是int&#xff08;11&#xff09;为&#xff08;a,b,c&#xff09;添加一个联合索引 index_abc 执行语句&#xff1a; CREATE TABLE te…

mysql联合索引

mysql联合索引的使用 命名规则&#xff1a;表名_字段名 1、需要加索引的字段&#xff0c;要在where条件中 2、数据量少的字段不需要加索引 3、如果where条件中是OR关系&#xff0c;加索引不起作用 4、符合最左原则 以下是我的建表语句 CREATE TABLE test ( id int(11) uns…

mysql 联合索引结构与索引匹配原则

联合索引结构与索引匹配原则 最左前缀匹配原则&#xff1a;在MySQL建立联合索引时会遵守最左前缀匹配原则&#xff0c;即最左优先&#xff0c;在检索数据时从联合索引的最左边开始匹配。 要想理解联合索引的最左匹配原则&#xff0c;先来理解下索引的底层原理。索引的底层是一…

详解MySQL联合索引

引言 本文预计分为两个部分:(1)联合索引部分的基础知识 在这个部分&#xff0c;我们温习一下联合索引的基础(2)联合索引部分的实战题 在这个部分&#xff0c;列举几个我认为算是实战中的代表题&#xff0c;挑出来说说。 正文 基础 讲联合索引&#xff0c;一定要扯最左匹配!…

mysql联合索引的使用

这篇笔记主要记录联合索引的使用 设置了shopId、userId、relationId三个字段&#xff0c;作为联合索引&#xff0c;这三个字段&#xff0c;都是long类型的&#xff0c;也就是bigint 分别验证以下几个场景&#xff1a; 场景一&#xff1a;explain select * from testIndex wher…

MySQL联合索引的原理

面试中被问到了联合索引&#xff0c;突然就涉及到了知识盲区&#xff0c;对不起&#xff0c;我只知道B树&#xff0c;B树&#xff0c;哈希索引&#xff0c;聚簇索引&#xff0c;非聚簇索引&#xff0c;联合索引的原理&#xff1f;。。 对不起涉及到了我的知识盲区了。 这里对联…

Mysql 联合索引

联合索引底层数据结构 MySQL可以使用多个字段同时建立一个索引,叫做联合索引。上文中讲到索引的底层结构就是一个二叉树&#xff0c;联合索引也是一样&#xff0c;它的非叶子节点中存的就不只是一个列&#xff0c;是索引的所有列&#xff0c;并且它的排序就是根据索引列的先后顺…

mysql联合索引详解

比较简单的是单列索引&#xff08;btree&#xff09;。遇到多条件查询时&#xff0c;不可避免会使用到多列索引。联合索引又叫复合索引。 btree结构如下&#xff1a; 每一个磁盘块在mysql中是一个页&#xff0c;页大小是固定的&#xff0c;mysql innodb的默认的页大小是16k&a…

MySQL索引详解

本文主要介绍MySQL索引底层原理及优化&#xff0c;理解SQL是如何执行&#xff0c;MySQL如何选择合适的索引以及时间都消耗在哪些地方&#xff0c;再加上一些优化的知识&#xff0c;可以帮助大家更好的理解MySQL&#xff0c;理解常见优化技巧背后的原理。希望本文中的原理、示例…

MySQL索引之联合索引

目录 1. 联合索引1.1. 联合索引的存储结构1.2. 联合索引的查询流程1.3. 最左前缀匹配原则1.3.1. 最左前缀匹配原则说明 2. 索引下推2.1. 无索引下推的执行流程2.2. 有索引下推的执行流程 1. 联合索引 在平时开发中&#xff0c;我们最常见的是聚集索引&#xff0c;但在我们需要…

jedis和redisTemplate

使用原生jedis和spring的redisTemplate调用连接池&#xff0c;发现差别巨大&#xff1a; redis配置&#xff1a; redis:database: 0host: 127.0.0.1port: 6379password: 123456timeout: 5000lettuce:shutdown-timeout: 200pool:max-active: 500max-idle: 100min-idle: 50max-w…

16.Jedis

目录 一、Jedis知识点总览。 二、连接池配置&#xff1a; 三、测试类&#xff1a; 一、Jedis知识点总览。 public class ProvinceServiceImpl implements ProvinceService {private ProvinceDao dao new ProvinceDaoImpl();Overridepublic List<Province> findAll(…

Jedis的配置和使用

什么是jedis 是官方推荐的java连接开发工具&#xff0c;使用java操作Redis的中间件&#xff0c;如果要使用java操作redis&#xff0c;那么要对jedis十分熟悉 测试 导入对应的依赖(Jedis和fastjson)&#xff1a; <dependencies><dependency><groupId>redi…

Jredis操作redis的入门级例子

redis入门级例子&#xff1a; Java代码 package com.liuxinglanyue.test; import java.util.ArrayList; import java.util.List; import org.jredis.JRedis; import org.jredis.RedisException; import org.jredis.ri.alphazero.JRedisClient; import org.…

jedis入门

目录 一、Jedis 1.Jedis简介 2.导包 3.官方文档 4.常用API 5.基本操作 6.jedis连接池的使用 7.工具类的编写 8.简单的使用 9.JedisPoolConfig的配置参数 一、Jedis 1.Jedis简介 Redis不仅是使用命令来操作&#xff0c;现在基本上主流的语言都有客户端支持&#xf…