更新记录

1.1.0(2026-11-12) 下载此版本

  • 新增 onRecordState 注册式监听:录音异常中断时触发,code = 1005,data 为已自动保存的文件路径
  • Android:采集线程遇 read 错误 / 写文件异常(麦克风被抢占、存储写满等)时自动收尾(回写 wav 头、释放麦克风、关闭前台服务)并回调
  • iOS:录音期间轮询 isRecording(0.5s),来电打断 / 编码错误 / 系统停止录音时自动收尾并回调
  • 收尾逻辑抽为统一函数,finishing 标志防止 stopRecord 与异常收尾并发重复处理
  • 注意:App 进程被系统杀死时无法回调(进程内代码已终止),前端可用 storage 记录路径 + 下次启动检查做补救

平台兼容性

uni-app(3.98)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
√ - × × √ - 9.0 16 ×
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
× × × × × × × × × × - -

uni-app x(3.98)

Chrome Safari Android iOS 鸿蒙 微信小程序
- - - - - -

其他

多语言 暗黑模式 宽屏模式 蒸汽模式
× × × √

LordXuan-recorder

UTS 原生录音插件,支持 Android / iOS 后台录音,输出标准 wav 文件,Android 9+ 自动挂载麦克风类型前台服务 + 常驻通知,避免后台录音被系统静音。

  • Android:AudioRecord 采集 16bit PCM,边采边写 wav(44 字节文件头 + PCM 数据)
  • iOS:AVAudioRecorder + AVAudioSession(playAndRecord),配合 UIBackgroundModes: audio 支持后台录音
  • HarmonyOS:占位实现,统一回调 code = -1
  • 全部使用系统原生 API,不引入任何三方 aar / framework

目录结构

LordXuan-recorder/
├── utssdk/
│   ├── interface.uts               # 统一接口与类型(三端共用)
│   ├── unierror.uts                # 统一错误码
│   ├── app-android/
│   │   ├── index.uts               # Android 实现(AudioRecord + 前台服务 + 通知)
│   │   ├── config.json             # minSdkVersion
│   │   └── AndroidManifest.xml     # 权限 + Service 声明(打包自动合并)
│   ├── app-ios/
│   │   ├── index.uts               # iOS 实现(AVFoundation)
│   │   └── config.json             # deploymentTarget
│   └── app-harmony/
│       ├── index.uts               # 鸿蒙占位
│       └── config.json
├── package.json
├── changelog.md
└── readme.md

API

所有方法都是 callback 风格,回调参数统一为:

{
  code: number, // 0 成功,非 0 失败
  msg: string,
  data: any | null
}
方法 参数 data
checkRecordPermission(callback) - boolean 是否已授权
requestRecordPermission(callback) - boolean 是否已授权
startRecord(options, callback) RecordOption string 录音文件绝对路径(此时文件已创建但只有 44 字节 wav 头,尚无音频数据)
stopRecord(callback) - string 录音文件绝对路径(文件已完整,可播放 / 上传)
getSystemSdkVersion(callback) - Android 返回 SDK_INT,iOS / 鸿蒙返回 -1
onRecordState(callback) - 注册录音状态监听,异常中断时触发,见下方说明

RecordOption

字段 类型 默认值 说明
filePath string 自动生成 保存路径,见下方路径规则
sampleRate number 44100 采样率
channel number 1 1 单声道,2 立体声

路径规则:

  • 不传:保存到应用私有目录 lordxuan_record_<时间戳>.wav
  • _doc/rec.wav:保存到应用私有 files 目录
  • /storage/.../rec.wav:按绝对路径保存(需自行保证目录可写)
  • rec.wav:保存到应用私有目录根目录

错误码(unierror.uts)

code 说明
0 成功
1001 权限拒绝
1002 录音已经在进行中
1003 录音启动失败(含创建文件失败、麦克风占用、前台服务启动失败、App 不在前台)
1004 未开始录音就调用停止
1005 录音异常中断,插件已自动收尾并保存文件,data 为文件路径(通过 onRecordState 回调)
-1 鸿蒙暂未实现录音

异常中断监听 onRecordState

录音过程中可能被外部因素中断:来电、其他 App 抢占麦克风、存储写满、系统停止录音等。插件检测到异常中断后会自动完成收尾(回写 wav 头、释放麦克风、关闭前台服务与通知),并通过 onRecordState 通知前端:

import { onRecordState } from '@/uni_modules/LordXuan-recorder'

// 建议在 App 启动时就注册,重复调用以最后一次为准
onRecordState((res) => {
    if (res.code === 1005) {
        // res.data 为已自动保存的文件路径,可正常播放/上传
        console.log('录音异常中断,文件已保存:', res.data)
    }
})

注意:App 进程被系统杀死时无法回调(进程内代码已全部终止)。针对这种情况,前端可用 storage 做补救:

// startRecord 成功时记录
uni.setStorageSync('lastRecording', res.data)
// stopRecord 成功后清除
uni.removeStorageSync('lastRecording')
// App 下次启动时检查:key 仍存在说明上次录音被进程杀死中断,可清理坏文件
const last = uni.getStorageSync('lastRecording')
if (last) {
    uni.removeStorageSync('lastRecording')
    // 此文件只有 wav 头没有数据,可删除或提示用户
}

Vue 页面调用示例

UTS 插件仅 App(Android / iOS)端可用,import 与调用必须放在 #ifdef APP-PLUS 条件编译里,否则编译到小程序 / web 端会报"文件查找失败":

<template>
    <view class="page">
        <button @click="checkPermission">检查权限</button>
        <button @click="requestPermission">申请权限</button>
        <button type="primary" @click="start">开始录音</button>
        <button type="warn" @click="stop">停止录音</button>
        <text class="tip">{{ tip }}</text>
        <text class="path" v-if="filePath">{{ filePath }}</text>
        <button v-if="filePath" @click="play">播放录音</button>
    </view>
</template>

<script>
    // #ifdef APP-PLUS
    import {
        checkRecordPermission,
        requestRecordPermission,
        startRecord,
        stopRecord,
        getSystemSdkVersion
    } from '@/uni_modules/LordXuan-recorder'
    // #endif

    export default {
        data() {
            return {
                tip: '等待操作',
                filePath: ''
            }
        },
        mounted() {
            // #ifdef APP-PLUS
            getSystemSdkVersion((res) => {
                console.log('系统版本:', res)
            })
            // #endif
        },
        methods: {
            checkPermission() {
                // #ifdef APP-PLUS
                checkRecordPermission((res) => {
                    this.tip = `权限状态:code=${res.code}, granted=${res.data}`
                })
                // #endif
            },
            requestPermission() {
                // #ifdef APP-PLUS
                requestRecordPermission((res) => {
                    this.tip = `申请结果:${res.msg},granted=${res.data}`
                })
                // #endif
            },
            start() {
                // #ifdef APP-PLUS
                startRecord({
                    // 不传 filePath 时插件自动生成
                    filePath: '_doc/lordxuan_' + Date.now() + '.wav',
                    sampleRate: 44100,
                    channel: 1
                }, (res) => {
                    console.log('startRecord:', res)
                    if (res.code === 0) {
                        this.filePath = res.data
                        this.tip = '录音中……可切到后台测试'
                        // Android 9+ 通知栏出现「正在录音中」常驻通知
                    } else {
                        this.tip = `启动失败:${res.msg}`
                    }
                })
                // #endif
            },
            stop() {
                // #ifdef APP-PLUS
                stopRecord((res) => {
                    console.log('stopRecord:', res)
                    if (res.code === 0) {
                        this.filePath = res.data
                        this.tip = '录音已保存'
                    } else {
                        this.tip = `停止失败:${res.msg}`
                    }
                })
                // #endif
            },
            play() {
                const innerAudioContext = uni.createInnerAudioContext()
                innerAudioContext.src = this.filePath
                innerAudioContext.play()
            }
        }
    }
</script>

<style>
    .page {
        padding: 40rpx;
    }

    button {
        margin-bottom: 24rpx;
    }

    .tip {
        display: block;
        margin-top: 24rpx;
        color: #666;
    }

    .path {
        display: block;
        margin-top: 16rpx;
        font-size: 24rpx;
        color: #999;
        word-break: break-all;
    }
</style>

项目根目录 manifest.json 需要补充的配置

Android 的权限与 Service 已由插件自带的 utssdk/app-android/AndroidManifest.xml 自动合并,正常无需再配置。若你的打包环境未合并,可在 app-plus.distribute.android.permissions 手动补:

"android": {
    "permissions": [
        "<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
        "<uses-permission android:name=\"android.permission.FOREGROUND_SERVICE\"/>",
        "<uses-permission android:name=\"android.permission.FOREGROUND_SERVICE_MICROPHONE\"/>",
        "<uses-permission android:name=\"android.permission.POST_NOTIFICATIONS\"/>"
    ]
}

iOS 必须补麦克风用途描述与后台音频模式:

"ios": {
    "privacyDescription": {
        "NSMicrophoneUsageDescription": "用于录制语音并保存为本地音频文件"
    },
    "UIBackgroundModes": ["audio"]
}

说明:

  • NSMicrophoneUsageDescription 缺失时,iOS 上申请麦克风权限会直接失败
  • 未开启 audio 后台模式时,App 退到后台会挂起,录音中断
  • HBuilderX 也可以在 manifest.json 可视化界面 → App 模块配置 / iOS 后台运行能力 中勾选,效果一致

调试说明(必看)

  1. 必须使用自定义调试基座:运行 → 运行到手机或模拟器 → 制作自定义调试基座,标准基座不包含 UTS 原生插件代码。
  2. 原生配置(AndroidManifest.xml / 权限 / config.json)必须重新打基座或云打包才生效,改完不要只热刷新。
  3. Android 建议真机测试,模拟器部分机型没有麦克风或前台服务行为不一致。
  4. 前台服务限制:App 已经进入后台时不允许再启动前台录音服务,插件在启动前会校验 App 是否在前台,不在前台直接回 1003;请务必在页面上点击「开始录音」后再切后台。
  5. Android 13+ 需要通知权限才能看到常驻通知,插件会在 requestRecordPermission 时一并申请。
  6. 通知是 ongoing(不可滑动关闭),stopRecord 后会自动移除并停止服务。

常见问题

Q:控制台报 Unable to instantiate service uts.sdk.modules.LordXuanRecorder.RecordService?

说明插件包名与清单里的 android:name 不匹配。UTS 插件默认包名规则为 uts.sdk.modules. + 插件 id 转驼峰(LordXuan-recorder → LordXuanRecorder)。若你的工程生成的包名不同(例如首字母被小写),请同时修改:

  • utssdk/app-android/AndroidManifest.xml 中 package 与 <service android:name>
  • utssdk/app-android/index.uts 中的 SERVICE_CLASS_NAME 常量

Q:Android 后台录音没有声音?

确认是否走的是前台服务(SDK>=28 必须),以及是否授予了通知权限;部分 ROM 还需要在系统设置里允许「后台运行 / 自启动」。

Q:录音文件在电脑上放不出声?

插件输出的是标准 PCM wav(44 字节头),若文件头未回写(例如 App 被强杀),时长信息会为 0,可用工具修复;正常 stopRecord 流程会自动回写。

开发文档

隐私、权限声明

1. 本插件需要申请的系统权限列表:

android.permission.RECORD_AUDIO;android.permission.FOREGROUND_SERVICE;android.permission.FOREGROUND_SERVICE_MICROPHONE;android.permission.POST_NOTIFICATIONS;iOS:NSMicrophoneUsageDescription

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不收集、不上传任何用户数据,录音文件仅保存在应用私有目录

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无

许可协议

MIT协议

暂无用户评论。