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

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

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

创建一个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?: RtcEngine constructor(props) {
super(props)
this.state = {
appId: YourAppId,
token: YourToken,
channelName: 'channel-x',
joinSucceed: false,
peerIds: [],
}
if (Platform.OS === 'android') {
// Request required permissions from Android
requestCameraAndAudioPermission().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.state
this._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 IDs
const {peerIds} = this.state
// If new user
if (peerIds.indexOf(uid) === -1) {
this.setState({
// Add peer ID to state array
peerIds: [...peerIds, uid]
})
}
}) this._engine.addListener('UserOffline', (uid, reason) => {
console.log('UserOffline', uid, reason)
const {peerIds} = this.state
this.setState({
// Remove peer ID from state array
peerIds: peerIds.filter(id => id !== uid)
})
}) // If Local user joins RTC channel
this._engine.addListener('JoinChannelSuccess', (channel, uid, elapsed) => {
console.log('JoinChannelSuccess', channel, uid, elapsed)
// Set state variable to true
this.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 name
await 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}>
<TouchableOpacity
onPress={this.startCall}
style={styles.button}>
<Text style={styles.buttonText}> Start Call </Text>
</TouchableOpacity>
<TouchableOpacity
onPress={this.endCall}
style={styles.button}>
<Text style={styles.buttonText}> End Call </Text>
</TouchableOpacity>
</View>
{this._renderVideos()}
</View>
</View>
)
} _renderVideos = () => {
const {joinSucceed} = this.state
return joinSucceed ? (
<View style={styles.fullView}>
<RtcLocalView.SurfaceView
style={styles.max}
channelId={this.state.channelName}
renderMode={VideoRenderMode.Hidden}/>
{this._renderRemoteVideos()}
</View>
) : null
} _renderRemoteVideos = () => {
const {peerIds} = this.state
return (
<ScrollView
style={styles.remoteContainer}
contentContainerStyle={{paddingHorizontal: 2.5}}
horizontal={true}>
{peerIds.map((value, index, array) => {
return (
<RtcRemoteView.SurfaceView
style={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、技术帮助

使用 Agora SDK 开发 React Native 视频通话App的更多相关文章

  1. React Native发布APP之打包iOS应用

    用React Native开发好APP之后,如何将APP发布以供用户使用呢?一款APP的发布流程无外乎:签名打包—>发布到各store这两大步骤.本文将向大家分享如何签名打包一款React Na ...

  2. React Native & CodePush & App Center

    React Native & CodePush & App Center https://docs.microsoft.com/en-us/appcenter/distribution ...

  3. 关于开发React Native的注意事项

    今天在写一个简单的RN的Demo时,一连出现了好几个错误,最后幸亏得以解决,在这里把我踩过的坑以及解决办法分享出来: 1.运行出现错误:Could not connect to development ...

  4. React Native的APP打包教程

    1.改软件的名称 2.改软件的图标 3.给做好的项目打成APP包 改软件的名称 找到项目的改名的位置 然后用记事本打开strings.xml,然后改自己想要的名字 改软件的图标 找到如下5个文件,然后 ...

  5. React Native & Web APP

    React Native Build native mobile apps using JavaScript and React https://facebook.github.io/react-na ...

  6. 带你从零学ReactNative开发跨平台App开发[react native SqlLite 终极运用](十二)

    ReactNative跨平台开发系列教程: 带你从零学ReactNative开发跨平台App开发(一) 带你从零学ReactNative开发跨平台App开发(二) 带你从零学ReactNative开发 ...

  7. 带你从零学ReactNative开发跨平台App开发-[react native 仿boss直聘](十三)

    ReactNative跨平台开发系列教程: 带你从零学ReactNative开发跨平台App开发(一) 带你从零学ReactNative开发跨平台App开发(二) 带你从零学ReactNative开发 ...

  8. 前段开发 react native tab功能

    import React, { Component } from 'react'; import { StyleSheet, Text, View, Image, } from 'react-nati ...

  9. 打造开发React Native的Sublime

     之前一年多一直用Sublime Text做前端开发,最近做React开发,发现不太好用,就尝试其他编辑器.WebStorm和VS Code都用过,WebStorm基本不用装插件,可以直接用,但用习惯 ...

  10. react native改变app的图标和名称

    beauty\android\app\src\main\res

随机推荐

  1. 关于elementui 面包屑设置

    后台管理面包屑 新建组件 <template> <el-breadcrumb separator-class="el-icon-arrow-right"> ...

  2. spider_使用urllib库 提交post请求,有道翻译案例

    """使用urllib库 提交post请求, 有道翻译"""from urllib import requestfrom urllib im ...

  3. Lua监听事件观察者模式(多个监听者监听)

    fireEvent 产生事件,创建协程分发(在registerEventListener注册过的事件中通过事件名字找到对应的信息,然后执行对应模块的OnEvent函数),多个地方都注册了同一个事件的话 ...

  4. java发送短信验证码带倒计时

    分享一个完整的java发送短信验证码的完整实例,这是一个官方的使用demo,带有60秒倒计时功能. 效果: 我使用的是榛子云短信平台, 官网地址:http://smsow.zhenzikj.com 我 ...

  5. Linux 三剑客常用命令

    shell三剑客===================================================grep===================================== ...

  6. linux执行某个文件夹下的所有sql文件

    一个目录下有多个sql文件,hive需要执行该目录下的每个文件 #!/bin/bash dir=$(ls -l /app/test_warehouse/git/azkaban/test_shrimp/ ...

  7. SQL 之 SQL server 中 遇到字段的值为null 遇到的坑

    SQL server 中,如果查询或筛选的条件中,某字段的值为NULL,如何处理?又有哪些坑需要避免呢? 直接上坑 : NULL 与其他任何类型进行逻辑判断: [解释]: NULL表示值未知,和空字符 ...

  8. vue创建项目的命令

    一.首先下载node环境 二.全局安装vue-cli cnpm i -g @vue/cli 这里一定要注意是vue/cli,而不是vue-cli 三.新建文件夹,打开cmd命令 1.vue creat ...

  9. 关于IllegalMonitorStateException异常的解释之一

    注意 在同步控制方法或同步控制块里调用wait(),notify()和notifyAll().如果在非同步控制方法里调用这些方法,程序能通过编译,但运行的时候,将得到IllegalMonitorSta ...

  10. Linux中/etc目录下passwd和shadow文件

    /etc/passwd介绍 首先,通过cat /etc/passwd 来查看文件/etc/passwd中内容: root:x:0:0:root:/root:/bin/bash daemon:x:1:1 ...