更新记录
1.2.0(2026-03-16)
- 新增录音中断自动检测:麦克风被抢占或来电时通过
onError回调通知,不再静默停止 - Android 新增来电检测(
READ_PHONE_STATE权限可选,无权限自动降级为看门狗) - iOS 新增系统中断监听(来电、其他应用抢占时立即回调)
- 录音与播放共存:Android 移除 AudioFocus 管理,iOS 切换为
playAndRecord模式 - 错误码扩展为 7 个,语义更准确,Android/iOS 两端统一
- 修复 iOS
onStart回调时机(引擎启动成功后才触发) - 修复 iOS
tempFilePath补全file://前缀,与 Android 一致
1.1.0(2026-03-10)
1、Android 新增录音输出目录配置:
RecorderOptions.androidOutputScope:external|internal- 默认值改为
external(外部应用专属缓存目录) - 外部目录不可用时自动回退到内部缓存目录
2、Android 新增录音通知增强能力(可选)
RecorderOptions 新增字段:
showNotification(默认 false)notificationNamenotificationText当showNotification=true时,录音期间显示常驻通知,可增强后台/息屏存活率 当showNotification=false时,保持原有录音逻辑不变 Android 清单新增android.permission.POST_NOTIFICATIONS
3、新增事件取消监听 API,Android/iOS 统一支持:
offStartoffStopoffFrameRecordedoffErroroffPauseoffResume
1.0.0(2026-02-12)
首个正式完善发布版本 实现实时PCM音频流回调,支持边录边获取原始音频数据 支持实时分贝监测,帧回调中直接返回当前音量值 提供完整的录音控制:开始、停止、暂停、恢复、取消 Android/iOS双端适配,统一API,纯UTS实现无需原生代码
查看更多平台兼容性
uni-app(4.0)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | Android插件版本 | iOS | iOS插件版本 | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | 5.0 | 1.0.0 | 12 | 1.0.0 | - |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | - | - | - | - | - | - |
uni-app x(5.0)
| Chrome | Safari | Android | Android插件版本 | iOS | iOS插件版本 | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|---|---|
| - | - | 5.0 | 1.0.0 | 12 | 1.0.0 | - | - |
其他
| 多语言 | 暗黑模式 | 宽屏模式 |
|---|---|---|
| √ | √ | √ |
Android iOS录音管理器 - 实时PCM音频流录音插件
支持实时PCM帧回调的uni-app录音插件,提供流式音频数据处理能力,适用于语音识别、实时转写、波形显示等场景。纯UTS实现,Android/iOS双端适配。
✨ 核心特性
- ✅ 实时PCM音频流 - 边录边获取原始PCM数据,延迟低至40ms
- ✅ 实时分贝监测 - RMS算法计算,实时返回音量分贝值
- ✅ 流式数据处理 - 支持边录边传、边录边识别等流式场景
- ✅ WAV文件保存 - 自动保存为标准WAV格式,支持file://直接上传
- ✅ Android/iOS - 双端适配,统一API,透明处理平台差异
- ✅ 暂停/恢复 - 支持录音过程中暂停和恢复
- ✅ 录音与播放共存 - 录音期间可同时播放音频,互不干扰
- ✅ 中断自动检测 - 多层看门狗机制,来电/麦克风被抢占时立即回调通知
- ✅ 突破录音时长限制 - 无系统默认60秒/10分钟限制,支持长时间持续录音
- ✅ 纯UTS实现 - 无需原生代码,开箱即用
📦 安装
将插件目录复制到项目的 uni_modules 目录下即可。
your-project/
└── uni_modules/
└── dh-recorder/
🔐 权限说明
Android
插件自动声明以下权限:
| 权限 | 用途 | 必须 |
|---|---|---|
android.permission.RECORD_AUDIO |
录音 | ✅ 必须 |
android.permission.POST_NOTIFICATIONS |
录音通知(Android 13+,showNotification=true 时使用) |
可选 |
android.permission.READ_PHONE_STATE |
来电检测,来电时自动中断录音 | 可选 |
说明:
READ_PHONE_STATE无需主动申请。已授权时插件自动启用来电检测;未授权时静默降级,录音功能不受影响,仅来电中断检测延迟约3秒。
iOS
需要在 Info.plist 中添加麦克风权限说明(插件已自动配置):
<key>NSMicrophoneUsageDescription</key>
<string>需要使用麦克风进行录音</string>
后台录音(可选):若需要应用进入后台后继续录音,需在 manifest.json 中配置:
"app-plus": {
"distribute": {
"ios": {
"UIBackgroundModes": ["audio"]
}
}
}
未配置时,进入后台约3秒后
onError会收到8010006错误回调(系统中断),录音自动停止。
🚀 快速开始
基本使用
import { getRecorderManager } from '@/uni_modules/dh-recorder';
// 获取录音管理器实例(单例)
const recorderManager = getRecorderManager();
// 监听录音开始
recorderManager.onStart(() => {
console.log('录音开始');
});
// 监听录音帧数据(核心功能)
recorderManager.onFrameRecorded((res) => {
// Android: res.base64 为 base64 字符串,需解码
// iOS: res.frameBuffer 为 ArrayBuffer,可直接使用
const arrayBuffer = res.frameBuffer ?? uni.base64ToArrayBuffer(res.base64 ?? '');
console.log('帧数据大小:', arrayBuffer.byteLength); // 如: 1280字节
console.log('当前分贝:', res.decibel); // 如: -35.2 dB
});
// 监听录音停止
recorderManager.onStop((res) => {
console.log('📁 录音文件:', res.tempFilePath); // file://格式路径
console.log('⏱️ 录音时长:', res.duration, 'ms');
console.log('📦 文件大小:', res.fileSize, 'bytes');
});
// 监听错误(含中断通知)
recorderManager.onError((err) => {
console.error('❌ 录音错误:', err.errCode, err.errMsg);
// 8010005: 麦克风被其他应用抢占
// 8010006: 来电/系统中断
});
// 开始录音
recorderManager.start({
sampleRate: 16000, // 采样率:16kHz(推荐语音场景)
numberOfChannels: 1, // 声道数:单声道
bitsPerSample: 16, // 位深:16位
frameSize: 640, // 帧大小:640样本 ≈ 40ms @ 16kHz
format: 'wav', // 格式:WAV
showNotification: true, // 仅Android:开启录音通知,增强后台/息屏存活率
notificationName: '我的App',
notificationText: '正在录音中,不要关闭应用',
androidOutputScope: 'external',
});
// 停止录音
recorderManager.stop();
上传录音文件
recorderManager.onStop((res) => {
if (res.success && res.tempFilePath) {
uni.uploadFile({
url: 'https://your-server.com/upload',
filePath: res.tempFilePath, // file://格式,可直接上传
name: 'audioFile',
formData: {
duration: res.duration,
fileSize: res.fileSize,
},
success: (uploadRes) => {
console.log('✅ 上传成功:', uploadRes.data);
},
fail: (err) => {
console.error('❌ 上传失败:', err);
},
});
}
});
暂停和恢复
recorderManager.onPause(() => {
console.log('⏸️ 录音已暂停');
});
recorderManager.onResume(() => {
console.log('▶️ 录音已恢复');
});
recorderManager.pause(); // 暂停
recorderManager.resume(); // 恢复
recorderManager.cancel(); // 取消(不保存文件)
中断处理
中断发生时,onError 先回调(原因),onStop 后回调(已录文件),可根据 interrupted 字段区分:
recorderManager.onError((err) => {
switch (err.errCode) {
case 8010005:
showToast('录音被中断,请重新开始');
break;
case 8010006:
showToast('录音被电话中断');
break;
default:
showToast('录音出错:' + err.errMsg);
}
});
recorderManager.onStop((res) => {
if (res.interrupted) {
// 外部中断自动停止:保存已录片段或提示用户
console.log('中断前已录时长:', res.duration, 'ms');
console.log('已保存部分录音:', res.tempFilePath);
// 可选:上传已录片段
uploadPartialRecording(res.tempFilePath);
} else {
// 用户主动停止:正常流程
uploadRecording(res.tempFilePath);
}
updateUIToStopped();
});
📖 API文档
getRecorderManager()
获取录音管理器单例。
返回值: IRecorderManager
RecorderManager 实例方法
start(options)
开始录音。若已在录音中,会先停止当前录音再重新开始。
参数:
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| sampleRate | number | 否 | 16000 | 采样率(Hz),常用值:8000/16000/44100/48000 |
| numberOfChannels | number | 否 | 1 | 声道数:1=单声道,2=立体声 |
| bitsPerSample | number | 否 | 16 | 位深,支持:8/16/24/32 |
| frameSize | number | 否 | 640 | 帧大小(样本数)。16位时:640样本=1280字节≈40ms@16kHz |
| format | string | 否 | 'wav' | 录音格式:wav 或 pcm |
| showNotification | boolean | 否 | false | 仅Android:显示常驻通知,增强后台/息屏存活率 |
| notificationName | string | 否 | 应用名称 | 仅Android:通知标题 |
| notificationText | string | 否 | 正在录音中,不要关闭应用 | 仅Android:通知内容 |
| androidOutputScope | string | 否 | 'external' | 仅Android:文件输出目录,external(外部应用缓存)或 internal(内部私有) |
帧大小(frameSize)说明:
- 单位是样本数,不是字节数
- 16位PCM:1样本 = 2字节,640样本 = 1280字节 ≈ 40ms @ 16kHz
- 值越小回调频率越高,实时性越好,CPU占用也越高
后台录音增强(Android):
showNotification=false(默认):不显示通知,后台/息屏存活率取决于系统showNotification=true:显示常驻通知,可提升后台/息屏场景下的录音存活率
文件目录说明(Android):
external(默认):外部应用专属缓存目录,更利于本地file://直接播放internal:内部私有缓存目录,安全性更高- 外部目录不可用时自动回退到内部
stop()
停止录音,触发 onStop 回调,返回录音文件信息。
pause()
暂停录音,触发 onPause 回调。暂停期间音频帧不回调,但底层持续读取以防缓冲区积压。
resume()
恢复录音,触发 onResume 回调。
cancel()
取消录音,不保存文件,不触发 onStop 回调。
onStart(callback) / offStart(callback)
监听/取消监听录音开始事件。
onStop(callback) / offStop(callback)
监听/取消监听录音停止事件。
回调参数:
{
success: boolean; // 是否成功
tempFilePath: string; // 录音文件路径(file://格式)
duration: number; // 录音总时长(毫秒)
fileSize: number; // 文件大小(字节)
interrupted: boolean; // 是否因外部中断(来电、麦克风被抢占等)自动停止,主动调用 stop() 时为 false
}
中断时
onError先触发(说明原因),onStop后触发(交付已录文件)。若中断时尚无任何录音数据(首帧未到达),则不触发onStop。
路径格式示例:
- Android(external):
file:///storage/emulated/0/Android/data/com.xxx/cache/dh_record_1234567890.wav - iOS:
file:///var/mobile/Containers/Data/Application/.../tmp/dh_record_1234567890.wav
onFrameRecorded(callback) / offFrameRecorded(callback)
监听/取消监听录音帧数据事件,这是本插件的核心功能。
回调参数:
{
frameBuffer?: ArrayBuffer; // 音频帧数据(iOS,Int16小端序PCM)
base64?: string; // base64编码的音频帧数据(Android)
isLastFrame: boolean; // 是否为最后一帧(当前始终为 false)
decibel: number; // 当前帧分贝值(约-96dB静音 到 0dB满刻度)
}
跨平台差异:
- iOS:通过
frameBuffer(ArrayBuffer)直接传输,可直接使用DataView、Int16Array等API - Android:通过
base64传输,需用uni.base64ToArrayBuffer(res.base64)转换
// 兼容两端的写法
recorderManager.onFrameRecorded((res) => {
const buffer = res.frameBuffer ?? uni.base64ToArrayBuffer(res.base64 ?? '');
// 使用 buffer...
});
onError(callback) / offError(callback)
监听/取消监听录音错误及中断事件。
回调参数:
{
errCode: number; // 错误码(见下表)
errSubject: string; // 错误主题(固定为 'dh-recorder')
errMsg: string; // 错误详细描述
}
错误码说明:
| 错误码 | 含义 | 典型场景 |
|---|---|---|
| 8010001 | 录音权限被拒绝 | 用户拒绝麦克风授权 |
| 8010002 | 录音初始化失败 | 硬件或引擎创建失败 |
| 8010003 | 录音启动失败(麦克风被占用) | start() 时麦克风已被其他应用占用 |
| 8010004 | 录音停止失败 | stop() 过程中发生异常 |
| 8010005 | 录音被中断 | 运行中麦克风被抢占、持续无音频数据 |
| 8010006 | 录音被系统/电话中断 | 来电、其他应用抢占音频会话、进入后台(iOS无后台权限时) |
| 8010007 | 录音数据处理异常 | 帧数据读取或转换时发生内部异常 |
onPause(callback) / offPause(callback)
监听/取消监听录音暂停事件。
onResume(callback) / offResume(callback)
监听/取消监听录音恢复事件。
🎯 高级用法
实时语音识别
const recorderManager = getRecorderManager();
const ws = new WebSocket('wss://asr-api.example.com');
recorderManager.onFrameRecorded((res) => {
if (ws.readyState === WebSocket.OPEN) {
// Android 需先解码,iOS 可直接发送
const buffer = res.frameBuffer ?? uni.base64ToArrayBuffer(res.base64 ?? '');
ws.send(buffer);
}
});
recorderManager.start({
sampleRate: 16000,
numberOfChannels: 1,
format: 'pcm', // 实时传输用PCM,无WAV头,减少传输量
});
实时音量显示
const volumePercent = ref(0);
recorderManager.onFrameRecorded((res) => {
// 将 -96dB ~ 0dB 映射到 0 ~ 100
const normalized = Math.max(0, Math.min(100, ((res.decibel + 96) / 96) * 100));
volumePercent.value = normalized;
});
录音波形可视化
const waveformData = ref<number[]>([]);
recorderManager.onFrameRecorded((res) => {
const buffer = res.frameBuffer ?? uni.base64ToArrayBuffer(res.base64 ?? '');
const int16Array = new Int16Array(buffer);
// 每10个样本抽1个,归一化到[-1, 1]
const samples: number[] = [];
for (let i = 0; i < int16Array.length; i += 10) {
samples.push(int16Array[i] / 32768);
}
// 保留最近100个点
waveformData.value = [...waveformData.value, ...samples].slice(-100);
});
⚙️ 技术实现
架构说明
Android端:
- 使用
AudioRecordAPI 进行PCM录音,独立线程持续读取音频数据 ArrayBuffer无法直接跨 JS 边界传输,采用 base64 编码传输- 数据流:
AudioRecord → ByteBuffer → ByteArray → Base64 → JS - 暂停时继续
read()并丢弃数据,防止内部缓冲区积压导致恢复后数据异常
iOS端:
- 使用
AVAudioEngine + installTap实时获取音频,系统推送回调 - Float32 → Int16 转换,兼容标准PCM格式
AVAudioSession.Category为playAndRecord,支持录音与播放同时进行- 采样率自动适配:硬件实际采样率通常48kHz,插件自动降采样到配置值(如16kHz)用于回调,文件保存使用实际采样率保证音质
ArrayBuffer可直接跨 JS 边界传输,无需编码
中断检测机制
插件采用多层机制,确保各种场景下都能及时通知:
| 层次 | Android | iOS | 响应速度 |
|---|---|---|---|
| 直接API检测 | AudioRecord.read() 返回错误码 |
AVAudioEngine.start() 抛出异常 |
立即 |
| 电话监听 | TelephonyManager.PhoneStateListener |
AVAudioSession.interruptionNotification |
立即 |
| 首帧超时 | 1.5秒内无首帧 → 报错 | 1.5秒内无首帧 → 报错 | 1.5秒 |
| 帧超时看门狗 | 3秒无音频帧 → 报错 | 3秒无音频帧 → 报错 | ≤3秒 |
分贝计算算法
基于RMS(均方根)算法:
- 将Int16样本归一化到 [-1, 1]
- 计算RMS:
√(Σsample² / N) - 转换为分贝:
dB = 20 × log₁₀(RMS)
范围约 -96dB(静音)到 0dB(满刻度)。
数据格式
统一输出 16位PCM(小端序,Int16):
- 采样值范围:-32768 到 32767
- 字节序:Little Endian
- 数据类型:
ArrayBuffer(可用new Int16Array(buffer)访问)
📝 注意事项
-
权限:首次使用会自动请求录音权限。Android 13+ 的通知权限需额外申请(通过插件提供的
requestNotificationPermission()) -
iOS 后台录音:默认进入后台会中断录音并收到
8010006错误。如需后台录音,在manifest.json中配置UIBackgroundModes: ["audio"] -
内存管理:
onFrameRecorded默认约40ms回调一次,iOS 录音数据全程在内存中积累,长时间录音(建议不超过20分钟)注意内存占用 -
采样率:
- 语音场景:推荐 16000Hz(小文件、兼容性好)
- 音乐场景:推荐 44100Hz 或 48000Hz
- iOS 实际采样率由硬件决定(通常48kHz),插件自动处理,用户无需关心
-
帧大小:
- 语音识别推荐:640样本(40ms @ 16kHz)
- 实时显示推荐:320样本(20ms @ 16kHz)
-
格式选择:
wav:带44字节文件头,可直接播放,推荐pcm:纯原始数据,适合实时传输,播放需自行添加头
🔧 兼容性
| 平台 | 支持 | 最低版本 | 核心API |
|---|---|---|---|
| Android | ✅ | API 21(Android 5.0) | AudioRecord |
| iOS | ✅ | iOS 12.0 | AVAudioEngine |
| Web | ❌ | - | - |
| 小程序 | ❌ | - | - |
💬 联系方式
如有问题或建议,欢迎通过以下方式反馈:
- 提交 Issue
- DCloud插件市场评论
- DCloud插件交流群

收藏人数:
购买源码授权版(
试用
赞赏(0)
下载 159
赞赏 1
下载 11685657
赞赏 1897
赞赏
京公网安备:11010802035340号