expo-audio
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseExpo Audio (expo-audio)
Expo Audio (expo-audio)
Guide for using to implement audio playback and recording in React
Native apps.
expo-audio在React Native应用中使用实现音频播放与录制的指南。
expo-audioOverview
概述
- Package: (replaces deprecated
expo-audio)expo-av - Platform: Android, iOS, tvOS, Web
- Bundled version: ~1.1.1
- Documentation: https://docs.expo.dev/versions/latest/sdk/audio/
- 包: (替代已废弃的
expo-audio)expo-av - 支持平台: Android、iOS、tvOS、Web
- 捆绑版本: ~1.1.1
- 文档地址: https://docs.expo.dev/versions/latest/sdk/audio/
Installation
安装
bash
npx expo install expo-audiobash
npx expo install expo-audioConfiguration
配置
app.json Plugin Configuration
app.json 插件配置
Add the plugin to your :
expo-audioapp.jsonjson
{
"expo": {
"plugins": [
[
"expo-audio",
{
"microphonePermission": "Allow $(PRODUCT_NAME) to access your microphone.",
"recordAudioAndroid": true
}
]
]
}
}Configurable properties:
- (iOS only): String for NSMicrophoneUsageDescription. Set to
microphonePermissionto disable.false - (Android only): Boolean to enable RECORD_AUDIO permission (default:
recordAudioAndroid)true
在中添加插件:
app.jsonexpo-audiojson
{
"expo": {
"plugins": [
[
"expo-audio",
{
"microphonePermission": "允许$(PRODUCT_NAME)访问您的麦克风。",
"recordAudioAndroid": true
}
]
]
}
}可配置属性:
- (仅iOS):NSMicrophoneUsageDescription对应的描述字符串。设置为
microphonePermission可禁用该权限请求。false - (仅Android):是否启用RECORD_AUDIO权限的布尔值(默认值:
recordAudioAndroid)true
Background Audio (iOS)
iOS后台音频播放
For background audio playback on iOS, add to :
UIBackgroundModesapp.jsonjson
{
"expo": {
"ios": {
"infoPlist": {
"UIBackgroundModes": ["audio"]
}
}
}
}若要在iOS上实现后台音频播放,需在中添加:
app.jsonUIBackgroundModesjson
{
"expo": {
"ios": {
"infoPlist": {
"UIBackgroundModes": ["audio"]
}
}
}
}Core Concepts
核心概念
AudioPlayer
AudioPlayer
The class handles audio playback. You can create players using:
AudioPlayer- hook (recommended for React components)
useAudioPlayer() - function (for imperative usage outside components)
createAudioPlayer()
AudioPlayer- Hook(推荐在React组件中使用)
useAudioPlayer() - 函数(适用于组件外的命令式调用场景)
createAudioPlayer()
AudioRecorder
AudioRecorder
The class handles audio recording. Use:
AudioRecorder- hook (recommended for React components)
useAudioRecorder()
AudioRecorder- Hook(推荐在React组件中使用)
useAudioRecorder()
Usage Patterns
使用模式
Playing Sounds (React Hook - Recommended)
播放音频(React Hook - 推荐)
Use hook in React components:
useAudioPlayertsx
import { Button, View } from "react-native";
import { useAudioPlayer } from "expo-audio";
const audioSource = require("./assets/sound.mp3");
export default function App() {
const player = useAudioPlayer(audioSource);
return (
<View>
<Button title="Play" onPress={() => player.play()} />
<Button title="Pause" onPress={() => player.pause()} />
<Button
title="Replay"
onPress={() => {
player.seekTo(0);
player.play();
}}
/>
</View>
);
}Important: Unlike , doesn't automatically reset
playback position when audio finishes. After , the player stays paused
at the end. To replay, call to reset position.
expo-avexpo-audioplay()seekTo(seconds)在React组件中使用 Hook:
useAudioPlayertsx
import { Button, View } from "react-native";
import { useAudioPlayer } from "expo-audio";
const audioSource = require("./assets/sound.mp3");
export default function App() {
const player = useAudioPlayer(audioSource);
return (
<View>
<Button title="播放" onPress={() => player.play()} />
<Button title="暂停" onPress={() => player.pause()} />
<Button
title="重播"
onPress={() => {
player.seekTo(0);
player.play();
}}
/>
</View>
);
}重要提示:与不同,在音频播放结束后不会自动重置播放位置。调用后,播放器会停在结尾处。若要重播,需调用重置位置。
expo-avexpo-audioplay()seekTo(seconds)Playing Sounds (Imperative - Outside Components)
播放音频(命令式 - 组件外)
For imperative usage (e.g., utility functions), use :
createAudioPlayertsx
import { AudioPlayer, createAudioPlayer, setAudioModeAsync } from "expo-audio";
let player: AudioPlayer | null = null;
export async function loadSound(uri: string): Promise<void> {
// Configure audio mode
await setAudioModeAsync({
playsInSilentMode: true,
allowsRecording: false,
});
// Create player
player = createAudioPlayer({ uri });
}
export async function playSound(volume: number = 1.0): Promise<void> {
if (!player) {
await loadSound("https://example.com/sound.mp3");
}
if (player) {
player.volume = volume;
player.seekTo(0);
player.play();
}
}
export async function releaseSound(): Promise<void> {
if (player) {
player.release();
player = null;
}
}⚠️ Memory Management: When using , you must manually call
when done to prevent memory leaks.
createAudioPlayerrelease()对于命令式调用场景(如工具函数),使用:
createAudioPlayertsx
import { AudioPlayer, createAudioPlayer, setAudioModeAsync } from "expo-audio";
let player: AudioPlayer | null = null;
export async function loadSound(uri: string): Promise<void> {
// 配置音频模式
await setAudioModeAsync({
playsInSilentMode: true,
allowsRecording: false,
});
// 创建播放器
player = createAudioPlayer({ uri });
}
export async function playSound(volume: number = 1.0): Promise<void> {
if (!player) {
await loadSound("https://example.com/sound.mp3");
}
if (player) {
player.volume = volume;
player.seekTo(0);
player.play();
}
}
export async function releaseSound(): Promise<void> {
if (player) {
player.release();
player = null;
}
}⚠️ 内存管理:使用时,必须在使用完毕后手动调用以防止内存泄漏。
createAudioPlayerrelease()Recording Sounds
录制音频
tsx
import { useEffect, useState } from "react";
import { Button, View } from "react-native";
import {
AudioModule,
RecordingPresets,
setAudioModeAsync,
useAudioRecorder,
useAudioRecorderState,
} from "expo-audio";
export default function App() {
const audioRecorder = useAudioRecorder(RecordingPresets.HIGH_QUALITY);
const recorderState = useAudioRecorderState(audioRecorder);
const record = async () => {
await audioRecorder.prepareToRecordAsync();
audioRecorder.record();
};
const stopRecording = async () => {
// Recording available on `audioRecorder.uri`
await audioRecorder.stop();
};
useEffect(() => {
(async () => {
const status = await AudioModule.requestRecordingPermissionsAsync();
if (!status.granted) {
Alert.alert("Permission to access microphone was denied");
}
await setAudioModeAsync({
playsInSilentMode: true,
allowsRecording: true,
});
})();
}, []);
return (
<View>
<Button
title={recorderState.isRecording ? "Stop Recording" : "Start Recording"}
onPress={recorderState.isRecording ? stopRecording : record}
/>
</View>
);
}tsx
import { useEffect, useState } from "react";
import { Button, View } from "react-native";
import {
AudioModule,
RecordingPresets,
setAudioModeAsync,
useAudioRecorder,
useAudioRecorderState,
} from "expo-audio";
export default function App() {
const audioRecorder = useAudioRecorder(RecordingPresets.HIGH_QUALITY);
const recorderState = useAudioRecorderState(audioRecorder);
const record = async () => {
await audioRecorder.prepareToRecordAsync();
audioRecorder.record();
};
const stopRecording = async () => {
// 录制文件可通过`audioRecorder.uri`获取
await audioRecorder.stop();
};
useEffect(() => {
(async () => {
const status = await AudioModule.requestRecordingPermissionsAsync();
if (!status.granted) {
Alert.alert("麦克风访问权限已被拒绝");
}
await setAudioModeAsync({
playsInSilentMode: true,
allowsRecording: true,
});
})();
}, []);
return (
<View>
<Button
title={recorderState.isRecording ? "停止录制" : "开始录制"}
onPress={recorderState.isRecording ? stopRecording : record}
/>
</View>
);
}AudioPlayer API
AudioPlayer API
Properties
属性
- : Number (0.0 to 1.0) - Current playback volume
volume - : Boolean - Whether audio is currently playing
isPlaying - : Boolean - Whether audio source is loaded
isLoaded - : Number - Total duration in seconds (null if not loaded)
duration - : Number - Current playback position in seconds
currentTime
- : 数字(0.0至1.0)- 当前播放音量
volume - : 布尔值 - 音频是否正在播放
isPlaying - : 布尔值 - 音频源是否已加载
isLoaded - : 数字 - 音频总时长(秒,未加载时为null)
duration - : 数字 - 当前播放位置(秒)
currentTime
Methods
方法
- : Start or resume playback
play() - : Pause playback
pause() - : Seek to specific position
seekTo(seconds: number) - : Release player resources (required for
release())createAudioPlayer
- : 开始或恢复播放
play() - : 暂停播放
pause() - : 跳转到指定位置
seekTo(seconds: number) - : 释放播放器资源(使用
release()时必须调用)createAudioPlayer
Event Listeners
事件监听器
Use hook to react to player state changes:
useAudioPlayerStatus()tsx
import { useAudioPlayer, useAudioPlayerStatus } from "expo-audio";
const player = useAudioPlayer(source);
const status = useAudioPlayerStatus(player);
// status.isPlaying, status.currentTime, status.duration, etc.使用 Hook监听播放器状态变化:
useAudioPlayerStatus()tsx
import { useAudioPlayer, useAudioPlayerStatus } from "expo-audio";
const player = useAudioPlayer(source);
const status = useAudioPlayerStatus(player);
// status.isPlaying, status.currentTime, status.duration, etc.AudioRecorder API
AudioRecorder API
Methods
方法
- : Prepare recorder with options
prepareToRecordAsync(options?) - : Start recording
record() - : Stop recording (returns URI)
stop() - : Pause recording
pause() - : Release recorder resources
release()
- : 准备录制(可传入配置项)
prepareToRecordAsync(options?) - : 开始录制
record() - : 停止录制(返回文件URI)
stop() - : 暂停录制
pause() - : 释放录制器资源
release()
Recording Presets
录制预设
tsx
import { RecordingPresets } from "expo-audio";
// Available presets:
RecordingPresets.HIGH_QUALITY;
RecordingPresets.LOW_QUALITY;tsx
import { RecordingPresets } from "expo-audio";
// 可用预设:
RecordingPresets.HIGH_QUALITY;
RecordingPresets.LOW_QUALITY;Audio Mode Configuration
音频模式配置
Use to configure audio behavior:
setAudioModeAsync()tsx
import { setAudioModeAsync } from "expo-audio";
await setAudioModeAsync({
playsInSilentMode: true, // Play even when device is in silent mode
allowsRecording: false, // Allow recording (required for recording)
interruptionMode: "duck", // 'duck' | 'mix' | 'doNotMix'
});使用配置音频行为:
setAudioModeAsync()tsx
import { setAudioModeAsync } from "expo-audio";
await setAudioModeAsync({
playsInSilentMode: true, // 设备静音时仍可播放
allowsRecording: false, // 允许录制(录制功能必需)
interruptionMode: "duck", // 'duck' | 'mix' | 'doNotMix'
});Common Patterns
常见模式
Preload Audio on App Start
应用启动时预加载音频
tsx
// app/_layout.tsx
import { useEffect } from "react";
import { loadGongSound, unloadGongSound } from "../lib/audio";
export default function RootLayout() {
useEffect(() => {
loadGongSound();
return () => {
unloadGongSound();
};
}, []);
return <Stack />;
}tsx
// app/_layout.tsx
import { useEffect } from "react";
import { loadGongSound, unloadGongSound } from "../lib/audio";
export default function RootLayout() {
useEffect(() => {
loadGongSound();
return () => {
unloadGongSound();
};
}, []);
return <Stack />;
}Play Sound with Volume Control
带音量控制的音频播放
tsx
import { createAudioPlayer, setAudioModeAsync } from "expo-audio";
let player: AudioPlayer | null = null;
export async function playSound(volume: number = 1.0): Promise<void> {
if (!player) {
await setAudioModeAsync({ playsInSilentMode: true });
player = createAudioPlayer({ uri: "https://example.com/sound.mp3" });
}
if (player) {
player.volume = volume;
player.seekTo(0);
player.play();
}
}tsx
import { createAudioPlayer, setAudioModeAsync } from "expo-audio";
let player: AudioPlayer | null = null;
export async function playSound(volume: number = 1.0): Promise<void> {
if (!player) {
await setAudioModeAsync({ playsInSilentMode: true });
player = createAudioPlayer({ uri: "https://example.com/sound.mp3" });
}
if (player) {
player.volume = volume;
player.seekTo(0);
player.play();
}
}Replay Sound (Reset Position)
重播音频(重置位置)
tsx
// Important: expo-audio doesn't auto-reset position
player.seekTo(0); // Reset to beginning
player.play(); // Play from starttsx
// 重要提示:expo-audio不会自动重置位置
player.seekTo(0); // 重置到开头
player.play(); // 从头开始播放Migration from expo-av
从expo-av迁移
Key Differences
主要差异
- No auto-reset: doesn't reset position when playback finishes. Call
expo-audioto replay.seekTo(0) - Hook-based API: Prefer over
useAudioPlayer()Audio.Sound.createAsync() - Direct property access: Use instead of
player.volume = 0.5player.setVolumeAsync(0.5) - Simplified API: Fewer methods, more direct property access
- 无自动重置:在播放结束后不会重置位置,需调用
expo-audio实现重播。seekTo(0) - 基于Hook的API:优先使用而非
useAudioPlayer()Audio.Sound.createAsync() - 直接属性访问:使用替代
player.volume = 0.5player.setVolumeAsync(0.5) - 简化API:方法更少,属性访问更直接
Migration Example
迁移示例
Before (expo-av):
tsx
const { sound } = await Audio.Sound.createAsync({ uri });
await sound.setVolumeAsync(0.5);
await sound.setPositionAsync(0);
await sound.playAsync();After (expo-audio):
tsx
const player = createAudioPlayer({ uri });
player.volume = 0.5;
player.seekTo(0);
player.play();迁移前(expo-av):
tsx
const { sound } = await Audio.Sound.createAsync({ uri });
await sound.setVolumeAsync(0.5);
await sound.setPositionAsync(0);
await sound.playAsync();迁移后(expo-audio):
tsx
const player = createAudioPlayer({ uri });
player.volume = 0.5;
player.seekTo(0);
player.play();Web Considerations
Web端注意事项
- MediaRecorder on Chrome may produce WebM files missing duration metadata (known Chromium issue)
- Consider using polyfills like for better browser compatibility
kbumsik/opus-media-recorder - Web browsers require HTTPS for microphone access (MediaDevices getUserMedia security)
- Chrome浏览器的MediaRecorder可能会生成缺少时长元数据的WebM文件(已知Chromium问题)
- 可考虑使用等polyfill提升浏览器兼容性
kbumsik/opus-media-recorder - Web浏览器需要HTTPS环境才能访问麦克风(MediaDevices getUserMedia安全限制)
Permissions
权限
Request Recording Permissions
请求录制权限
tsx
import { AudioModule } from "expo-audio";
const status = await AudioModule.requestRecordingPermissionsAsync();
if (!status.granted) {
// Handle permission denied
}tsx
import { AudioModule } from "expo-audio";
const status = await AudioModule.requestRecordingPermissionsAsync();
if (!status.granted) {
// 处理权限被拒绝的情况
}Check Permission Status
检查权限状态
tsx
const status = await AudioModule.getRecordingPermissionsAsync();
// status.granted, status.canAskAgain, etc.tsx
const status = await AudioModule.getRecordingPermissionsAsync();
// status.granted, status.canAskAgain, etc.Best Practices
最佳实践
- Use hooks in components: Prefer in React components for automatic lifecycle management
useAudioPlayer() - Release resources: Always call when using
release()manuallycreateAudioPlayer() - Reset position for replay: Call before replaying sounds
seekTo(0) - Configure audio mode: Set for meditation/notification sounds
playsInSilentMode: true - Handle errors: Wrap audio operations in try-catch blocks
- Preload sounds: Load sounds on app start for better UX
- 在组件中使用Hook:优先在React组件中使用,实现自动生命周期管理
useAudioPlayer() - 释放资源:使用时,务必在使用完毕后调用
createAudioPlayer()release() - 重置位置实现重播:重播音频前调用
seekTo(0) - 配置音频模式:对于冥想/通知类音频,设置
playsInSilentMode: true - 错误处理:将音频操作包裹在try-catch块中
- 预加载音频:在应用启动时加载音频,提升用户体验