• 购买插件,选择该插件绑定的项目。
  • 在HBuilderX里找到项目,在manifest的app原生插件配置中勾选模块,如需要填写参数则参考插件作者的文档添加。
  • 根据插件作者的提供的文档开发代码,在代码中引用插件,调用插件功能。
  • 打包自定义基座,选择插件,得到自定义基座,然后运行时选择自定义基座,进行log输出测试。
  • 开发完毕后正式云打包
  • 付费原生插件目前不支持离线打包。
    Android 离线打包原生插件另见文档 https://nativesupport.dcloud.net.cn/NativePlugin/offline_package/android
    iOS 离线打包原生插件另见文档 https://nativesupport.dcloud.net.cn/NativePlugin/offline_package/ios

    注意事项:使用HBuilderX2.7.14以下版本,如果同一插件且同一appid下购买并绑定了多个包名,提交云打包界面提示包名绑定不一致时,需要在HBuilderX项目中manifest.json->“App原生插件配置”->”云端插件“列表中删除该插件重新选择

    Recorder原生录音插件:实时帧回调、文件流式读写

    本原生插件提供Android、iOS双端支持(可单独购买),是前端组件 Recorder-UniCore 的配套插件,搭配使用时可以提供丰富的功能支持;或者直接调用本原生插件接口,不过能做到的功能有限。

    推荐搭配 Recorder-UniCore 前端组件一起使用,可做到:

  • 支持已有的大部分录音格式:mp3、wav、pcm、amr、ogg、g711a、g711u等
  • 支持实时处理,包括变速变调、实时上传、ASR语音转文字
  • 支持可视化波形显示;可配置回声消除、降噪; 注意:不支持通话时录音
  • 支持使用部分线控耳机、蓝牙耳机、USB外设的麦克风录音
  • 支持文件流式读写,录音数据可以实时写入到文件
  • 支持离线使用,前端组件和本原生插件均不依赖网络
  • 详细文档(含Demo项目): https://github.com/xiangyuecn/Recorder/tree/master/app-support-sample/demo_UniApp

    Recorder开源库地址: https://github.com/xiangyuecn/Recorder

    如果github打不开,可以 点此访问Gitee仓库地址

    选择适用你的版本

    本原生插件支持Android、iOS分开单独到DCloud插件市场购买,均包含了Recorder-UniCore前端组件的商用授权;你可以只购买授权,费用为 ¥199元 ,相当于同时赠送了Android版原生插件(如果用不着这个原生插件可以不使用即可)。

    购买后可联系客服,同时提供订单信息,客服拉你进入VIP支持QQ群,在群文件中可下载Recorder-UniCore前端组件的 app-uni-support.js 文件最新源码;客服联系方式: QQ( 125 165 4593 ) ,或者直接联系作者 QQ( 753 610 399 ) (回复可能没有客服及时)。

    注:VIP支持群的主要作用是代表你已获得授权许可,可以随时获得 app-uni-support.js 文件最新版源码;不作为问答或售后群使用,当然如果你有问题也可以直接群里问,花费时间不多的,作者免费顺带就解答了,如果复杂花费比较久时间的,可能要适当收点人工费用,或者选择进行付费指导。

    Recorder-UniCore 组件中自带的 app-uni-support.js 文件是压缩版,功能和源码版一致,在VIP支持群中下载得到此文件源码后,可以直接替换组件中的这个文件,也可以不替换。

    插件市场付费购买插件支持开票(由DCloud开具),具体请参考 DCloud 云服务如何开具发票 ;在插件市场 我的插件 - 我使用的插件 - 购买的插件 可查看到购买的插件 和 开票相关指引。

    测试时无需购买插件,试用是免费的,并且原生插件试用是无限制的。

  • 先到 Recorder-UniCore 下载示例项目(需根目录执行 npm install 完成 recorder-core 依赖的安装),在HBuilder中打开后,在项目manifest.json配置中分配一个uni-app应用标识
  • 然后点击本插件页面中的试用按钮,在这个应用标识对应的项目中试用
  • 然后在项目manifest.json的App原生插件配置中勾选本原生插件(注意:勾选了试用版时,购买后需要重新勾选成正式版)
  • HBuilder中提交云打包打自定义基座
  • 连接手机进行调试,使用自定义基座运行即可测试
  • 录音权限配置

    在uni-app项目的 manifest.json 中配置好Android和iOS的录音权限声明。

    //Android需要勾选的权限,第二个也必须勾选
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
    【注意】Android如果需要在后台录音,需要启用后台录音保活服务,请参考下面的`androidNotifyService`,否则锁屏或进入后台一段时间后App可能会被禁止访问麦克风导致录音静音、无法录音(renderjs中H5录音也受影响)
    //iOS需要声明的权限
    NSMicrophoneUsageDescription
    【注意】iOS需要在 `App常用其它设置`->`后台运行能力`中提供`audio`配置,不然App切到后台后立马会停止录音

    使用方法一:搭配Recorder-UniCore前端组件使用

  • 到插件市场下载安装 Recorder-UniCore 前端组件,并且项目根目录安装 recorder-core npm install recorder-core --registry=https://registry.npmmirror.com/
  • 试用或购买本原生插件,在项目manifest.json的App原生插件配置中勾选本原生插件(注意:勾选了试用版时,购买后需要重新勾选成正式版)
  • 参考上面的录音权限配置,在项目manifest.json中配置好权限
  • 云打包自定义基座进行测试、或正式打包,即完成集成
  • /** 集成好Recorder-UniCore组件后,直接调用RecordApp相关方法进行录音即可,详细请参考下面的demo_UniApp文档链接
        RecordApp.RequestPermission(...) 请求录音权限
        RecordApp.Start({ 开始录音
            type:"mp3",sampleRate:16000,bitRate:16
            //android_audioSource、ios_categoryOptions ... 原生插件支持的更多配置参数,请参考下面recordStart原生接口
        }, ...)
        RecordApp.Stop(...) 停止录音
        RecordApp.Pause() 暂停
        RecordApp.Resume() 继续
    //在调用RecordApp.RequestPermission之前进行配置,建议放到import后面直接配置(全局生效)
    //也可以判断一下只在iOS上或Android上启用,不判断就都启用,比如判断iOS:RecordApp.UniIsApp()==2
    RecordApp.UniNativeUtsPlugin={ nativePlugin:true }; 
    //只需进行这个配置就行,在RecordApp录音时就会自动使用原生插件来录音;未启用原生插件时App内默认是在renderjs使用H5录音
    //提升后台录音的稳定性:配置了原生插件后,可配置 `RecordApp.UniWithoutAppRenderjs=true` 禁用renderjs层音频编码(WebWorker加速),变成逻辑层中直接编码(但会降低逻辑层性能),后台运行时可避免部分手机WebView运行受限的影响
    //提升后台录音的稳定性:需要启用后台录音保活服务(iOS不需要),详细请参考下面的`androidNotifyService`

    手动调用原生插件接口

    /**App中集成了原生插件后按下面代码进行调用,注意需要先配置RecordApp.UniNativeUtsPlugin
        参数:action 字符串,要调用的功能;args 对象,调用参数
        返回:any 根据功能定义返回对应的结果,出错会抛异常
    比如将任意数据保存到文件:**/
        var result=await RecordApp.UniNativeUtsPluginCallAsync("writeFile",{path:"test.txt", dataBase64:"dGVzdDEyMw=="});
    }catch(e){
        console.error(e)
    //更多可用接口请参考下面的《直接调用原生插件接口 - 可用的接口》

    详细文档、RecordApp方法、属性文档

    请先阅读 demo_UniApp文档 ,含Demo项目;更高级使用还需深入阅读 Recorder文档 RecordApp文档 (均为完整的一个README.md文档),Recorder文档中包含了更丰富的示例代码:基础录音、实时处理、格式转码、音频分析、音频混音、音频生成 等等,大部分能在uniapp中直接使用。

    使用方法二:直接调用原生插件接口

    注意:仅使用原生插件接口来进行录音时,只支持返回pcm格式音频数据,不支持其他格式;pcm数据可以使用Recorder库进行转码成其他格式,或者直接使用Recorder-UniCore前端组件支持更多格式录音。

  • 试用或购买本原生插件,在项目manifest.json的App原生插件配置中勾选本原生插件(注意:勾选了试用版时,购买后需要重新勾选成正式版)
  • 参考上面的录音权限配置,在项目manifest.json中配置好权限
  • 云打包自定义基座进行测试、或正式打包,即完成集成
  • 调用插件接口

    //加载插件,注意:如果你使用的单独的Android或iOS版,请使用下面带后缀的
    var RecNP=uni.requireNativePlugin("Recorder-NativePlugin");
    //var RecNP=uni.requireNativePlugin("Recorder-NativePlugin-Android");
    //var RecNP=uni.requireNativePlugin("Recorder-NativePlugin-iOS");
    //调用插件接口,固定使用request方法进行调用
    RecNP.request({
        action:"writeFile" //接口名称,这个例子是将任意数据保存到文件
        ,args:{path:"test.txt", dataBase64:"dGVzdDEyMw=="} //接口参数
    },function(data){ //接口调用结果回调
        if(data.status!="success"){
            console.error(data.message); //调用错误处理
            return;
        console.log(data.value); //接口调用结果
    //绑定原生层回调
    RecNP.request({action:"jsCall",args:{}},function(data){
        if(data.action=="onLog"){ //原生层日志输出
            var msg="[RecNP]["+data.tag+"]"+data.message;
            data.isError? console.error(msg) : console.log(msg);
        }else if(data.action=="onRecord"){ //录音pcm数据回调
            var sampleRate=data.sampleRate; //采样率
            var pcm=new Int16Array(uni.base64ToArrayBuffer(data.pcmDataBase64)); //16位pcm
            console.log("onRecord "+sampleRate+" "+pcm.length);
    //建议自行封装一个函数返回Promise来异步调用,方便好使
    var RecNP_CallAsync=function(action,args){ return new Promise(function(resolve, reject){
        RecNP.request({ action:action,args:args||{} },function(data){
            if(data.status!="success"){
                reject(new Error(data.message));
                return;
            resolve(data.value);
    }) };
    //调用,这个例子是将任意数据保存到文件
        var result=await RecNP_CallAsync("writeFile",{path:"test.txt", dataBase64:"dGVzdDEyMw=="});
    }catch(e){
        console.error(e)
    

    可用的接口

    【文档说明】
    下面的action中,开头的名字为RecNP.request的action,“参数”为args,“返回”为接口的返回值data.value
    【录音相关action】 注意:在使用Recorder-UniCore组件时会自动调用这些接口,请勿自行调用
    jsCall 【这是一个特殊方法】绑定原生层js回调,当原生层需要给js发送消息时,会进行回调(RecNP.request的回调方法会被反复调用)
        参数:{}
        返回:{ action:"", ... } //原生层会反复调用回调方法,通过其中的action来判断返回的是什么内容
            {action:"noop"} //无需处理,多次调用jsCall时原生层释放老的回调
            {action:"onLog",isError:false,tag:"xx",message:"xxx"} //原生层日志输出
            {action:"onRecord",sampleRate:44100,pcmDataBase64:"base64"} //录音数据回调,pcm为16位单声道,sampleRate是pcm的采样率
    recordPermission 请求录音权限
        参数:{}
        返回:1 //权限状态code数值:1有权限,3用户拒绝。(2未用到;此接口早期移植的时候忘记改成对象,保留数值格式)
    recordStart 开始录音,录音pcm数据会通过jsCall回调,开始后必须每5秒调用一次recordAlive;本方法会获取录音权限,但建议先调用recordPermission提前获取录音权限
            appNativePlugin_sampleRate:44100 //可选录制的采样率(24/09/05新增),默认返回44100采样率的pcm数据,可取值48000(在js中使用Recorder.SampleData函数来转换成需要的任意采样率);其他采样率值不一定可用,可能导致无法打开录音,请测试好后再提供
            ,appNativePlugin_AEC_Enable:false //可选是否启用回声消除,默认不启用
            ,android_audioSource:0 //可选Android指定麦克风源 MediaRecorder.AudioSource,默认值为0,0 DEFAULT 默认音频源,1 MIC 主麦克风,5 CAMCORDER 相机方向的麦,6 VOICE_RECOGNITION 语音识别,7 VOICE_COMMUNICATION 语音通信(带回声消除)。配置值除7外,会禁用回声消除
            ,ios_categoryOptions:0x1|0x4 //可选iOS的AVAudioSession setCategory的withOptions参数值,取值请下面的iosSetDefault_categoryOptions;默认值为5(0x1|0x4)
            ,ios_categoryResetPlayback_AEC:false //可选iOS打开录音时重置录音环境,用于提高部分音频设备的兼容性(24/09/05新增);iOS录音开启回声消除时,本原生插件可能无法感知到新插入的USB音频输入设备(如Lightning口/USB领夹麦克风),首次录音前插入的无此影响;可在录音时提供本true启用音频环境重置(默认false不重置),将能感知到所有音频输入设备,但会导致Start变慢;注意:录音中途新插入的USB设备可能任何配置下均无法感知到,需下次Start时才可以
        返回:{} //空对象
    recordStop 停止录音
        参数:{}
        返回:{} //空对象
    recordAlive 定时心跳(开始录音后5秒发一次),如果超过10秒未发心跳,将会停止录音,防止未stop导致泄露
        参数:{}
        返回:{} //空对象
    【其他可用action】
    getInfo 获取插件信息
        参数:{}
            info:"" //插件信息字符串
            ,bundleId:"xx.xx.xx" //当前App包名,Android为Package值、iOS为Bundle ID值
            ,bundleIds:["allow all"] //插件打包包名列表,默认匹配任何包名
            ,distInfo:"standard" //插件打包信息,默认standard
    debugInfo 获取调试信息 (24/09/05新增)
        参数:{}
        返回:{ appMemoryUsage:123 } //app内存占用大小(不一定准),单位字节;数据来源:Android Debug.getMemoryInfo.TotalPss,iOS task_info.TASK_VM_INFO.phys
    setSpeakerOff 切换扬声器外放和听筒播放,随时都可以调用;但需注意打开录音时可能会自动切换播放方式(24/09/05起打开录音默认off:false+headset:true),可在打开录音后调用一次切换成你需要的播放方式。iOS Bug:部分iOS系统版本首次切换或首次打开录音时,可能会导致已有的音频播放暂停但有播放进度变成假的无声,建议切换后或开始录音后再打开播放
            off:true //必填,true听筒播放,false扬声器播放,连接耳机时此配置无效
            headset:true //选填,默认true耳机播放,false扬声器播放(同时使用手机上的麦克风),连接耳机时此配置生效
            //配置场景:当由代码进行主动调用,比如开启回声消除录音时想播放的声音大点,就只提供off:false,这时没连接耳机会从扬声器播放,有耳机就从耳机播放
            //配置场景:当类似由用户点击外放按钮时调用,同时提供off:false+headset:false,这时不管有没有耳机,都会从扬声器播放
        返回:{  } //空对象
    iosSetDefault_categoryOptions iOS设置默认值,Android不可调用,为iOS的AVAudioSession setCategory的withOptions参数值;recordStart开始录音时如果未提供ios_categoryOptions参数,将会使用此默认值,提供了时将赋值给此默认值;setSpeakerOff调用时也会使用到此默认值
            value:0x1|0x4 //必填,取值(多选,默认值5=0x1|0x4):0 什么也不设置,0x1 MixWithOthers,0x2 DuckOthers,0x4 AllowBluetooth,0x8 DefaultToSpeaker(不可用,通过setSpeakerOff来切换),0x11 InterruptSpokenAudioAndMixWithOthers,0x20 AllowBluetoothA2DP,0x40 AllowAirPlay,0x80 OverrideMutedMicrophoneInterruption
        返回:{  } //空对象
    writeFile 数据写入文件,可新建文件、追加写入(文件流写入)
            path:"文件路径" //必填,支持的路径请参考下面
            ,dataBase64:"base64" //必填,写入的任意内容base64编码,可以为空字符串(如仅新建文件)
            ,append:false //可选,是否追加写入到文件结尾,默认false会新建文件并写入数据
            ,seekOffset:-1 //可选,从现有文件指定位置写入并覆盖对应内容(24/09/05新增),取值:-1不指定(默认),0 到 文件长度(0为文件开头位置,append配置无效)
            fullPath:"/文件绝对路径"
    readFile 读取文件,可流式读取
            path:"文件路径" //必填,支持的路径请参考下面
            ,type:"base64" //可选,返回结果类型,默认base64,设为text时将读取成utf-8文本,提供了chunkSize时只支持base64
            ,chunkSize:0 //可选,本次读取的最大长度,单位字节,默认0读取全部
            ,chunkOffset:0 //可选,提供了chunkSize时,指定读取的开始位置
            data:"文本或base64" //文件内容,类型取决于提供的type
            ,isExists:true //文件是否存在;文件不存在时不会返回错误,此时的data为空字符串
            ,totalSize:0 //文件大小
            ,fullPath:"/文件绝对路径"
    deleteFile 删除文件或文件夹
            path:"文件路径" //必填,支持的路径请参考下面(文件不存在时不会报错)
            ,isDir:false //可选,true时此路径是文件夹,删除此文件夹,默认false
        返回:{ fullPath:"/文件绝对路径" }
    moveFile 移动或重命名文件
            fromPath:"源文件路径" //必填,支持的路径请参考下面
            ,path:"新文件路径" //必填,如果存在会覆盖
        返回:{ fullPath:"/移动后的文件绝对路径" }
    copyFile 复制文件
            fromPath:"源文件路径" //必填,支持的路径请参考下面
            ,path:"新文件路径" //必填,如果存在会覆盖
        返回:{ fullPath:"/复制后的文件绝对路径" }
    resolvePath 解析路径成绝对路径
            path:"文件或文件夹路径" //必填(空字符串时为store目录),支持的路径请参考下面
            ,pathInfo:false //可选,是否返回路径信息,默认false不返回
            fullPath:"/文件绝对路径"
            ,pathInfo:{ //可选返回路径信息
                isExists:true //文件或文件夹是否存在
                ,isFile:true //true时path为文件,false时为文件夹
                ,size:123 //isFile时文件大小,文件夹为0
                ,date:123456 //isFile时文件更新时间,毫秒,文件夹为0
    listPath 读取文件夹内的文件
        参数:{ path:"文件夹路径" } //必填(空字符串时为store目录),支持的路径请参考下面
            files:[ { //此文件夹下的文件
                    name:"文件名"
                    ,size:123 //文件大小
                    ,date:123456 //文件更新时间,毫秒
            ,dirs:[ "文件夹名" ] //此文件夹下的文件夹
            ,fullPath:"/文件夹绝对路径" //结尾不带/
    androidNotifyService 搭配常驻通知的Android后台录音保活服务(24/09/05新增),iOS不可调用。注意:需要在项目根目录提供AndroidManifest.xml配置才可调用本接口,调用时App必须在前台(适配Android 12+),需要先调用请求录音权限后才能开启服务(适配Android 14+),详细请参考下面的“Android后台录音保活”
            title:"录音通知标题" //打开服务时必填,close为true时无需提供
            ,content:"录音通知内容" //可选,close为true时无需提供
            ,close:false //设为true时关闭服务和通知,不管打开服务是否成功,都需要调用close关闭
        返回:{ } //close时返回空对象
        返回:{ //非close时只要成功返回结果,就代表服务已在运行,但通知可能不显示或会延迟显示,并不影响服务运行
            notifyPermissionCode:1 //仅供参考,通知的显示权限状态,取值:1有通知权限,3不确定,当App的targetSdkVersion>=33(Android 13+)时代表无通知权限,小于33时可能系统设置中未打开App的通知或正在弹框询问是否允许通知
            ,notifyPermissionMsg:"" //仅供参考,权限code对应的消息,当App的targetSdkVersion>=33时为:`(已|未)获得App的通知权限`,小于33时为:`系统设置中App的通知(已|未)打开`
    androidStoragePermission__limited 简易获取Android的外部存储权限,iOS不可调用,当你需要读写当前应用数据以外的文件时(如手机的Download目录文件),需要先获取外部存储权限;注意这个只会请求WRITE_EXTERNAL_STORAGE权限,因此TargetSDK需小于33(Android 13),否则此权限永远是拒绝的(请自行用别的途径获取权限)
        参数:{}
        返回:{ code:1 } //权限状态:1有权限,3用户拒绝。(2未用到)
    【支持的路径】
    "store://文件夹/文件.png" 或 "文件夹/文件.png" (开头不带/)
        app内部保存文件,文件夹是可选的
            Android为app的file目录 + 文件夹/文件.png
            iOS为app的Library/Files目录 + 文件夹/文件.png
    "__doc://文件夹/文件.png"
        app内部保存文件,文件夹是可选的;兼容iOS Documents目录专用的,正常用"store://"就够了
            Android为app的file/__doc目录 + 文件夹/文件.png
            iOS为app的Documents目录 + 文件夹/文件.png
    "cache://文件夹/文件.png"
        app内部缓存文件,文件夹是可选的,存储的文件可能会被用户或系统删除
            Android为app的cache目录 + 文件夹/文件.png
            iOS为app的Library/Caches目录 + 文件夹/文件.png
    "file:///绝对路径/文件.png" 或 "/绝对路径/文件.png" (开头有/)
        绝对路径,一般只允许读写app自己目录内的文件;Android获取到了外部存储权限时(调用androidStoragePermission__limited),可能可以可读写外部存储中的文件;iOS不支持读写非app自己目录文件

    Android后台录音保活

    Android 9开始,为了保护用户隐私,锁屏或进入后台一段时间后App可能会被禁止访问麦克风、摄像头等功能,导致无法录音、或录音数据全部是静音,因此需要使用保活机制才能在后台录音;H5录音、原生接口录音均受影响。iOS不存在此类问题,iOS只要在manifest.json的后台运行能力中提供audio配置,即可在后台录音。

    本插件已提供androidNotifyService接口,来控制搭配常驻通知的Android后台录音保活服务,打开服务后可提升App在后台或锁屏后录音的稳定性,注意:不同机型的录音保活效果不一定100%有效;此服务为前台服务(Foreground services),在启动的时候会在状态栏显示一个常驻的通知(通知可能不显示或会延迟显示,并不影响服务运行)。

    【注意】targetSdkVersion>=31(Android 12+)时,App必须在前台才能调用此服务接口,在后台时不允许调用;targetSdkVersion>=34(Android 14+)时,microphone类型的服务依赖录音权限,因此建议先请求录音权限(调用RecordApp.RequestPermission、或recordPermission接口),成功后再来调用androidNotifyService接口。

    【注意】使用前台服务后,在上架时可能需要声明需要使用的任何前台服务类型、提供使用说明和具体用例,否则可能导致部分应用市场无法上架;为了不影响不使用此服务的App,要使用本服务前要在AndroidManifest.xml中进行配置后才能使用

    //请在项目根目录新建AndroidManifest.xml文件,填入以下内容(修改后需要重新打包才会生效)
    //文档篇幅有限,下面内容的原文在demo项目的同名文件中,可在demo中找到此文件,或到github查看:https://github.com/xiangyuecn/Recorder/blob/master/app-support-sample/demo_UniApp/AndroidManifest.xml
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" 
        package="io.dcloud.nativeresouce">
        <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
        <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
        <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE"/>
        <application>
            <service
                android:name="uni.plugin.recorder.RecorderNativePlugin$RecNotifyService"
                android:foregroundServiceType="microphone"
                android:exported="false">
            </service>
        </application>
    </manifest>
    //上面这段代码仅用于Android后台录音保活服务,如果项目不需要后台录音保活,可以不复制
    //iOS只要在manifest.json的`后台运行能力`中提供`audio`配置,即可在后台录音