z***@163.com
z***@163.com
  • 发布:2026-05-15 13:53
  • 更新:2026-05-15 14:00
  • 阅读:25

【报Bug】uni-app官方提供的录音功能,执行stop后,没有回调onStop\onError

分类:鸿蒙Next

产品分类: uniapp/App

PC开发环境操作系统: Mac

PC开发环境操作系统版本号: 5.0.3.404

HBuilderX类型: 正式

HBuilderX版本号: 4.76

手机系统: HarmonyOS NEXT

手机系统版本号: HarmonyOS 5.1.0

手机厂商: 华为

手机机型: Mate 60 Pro

页面类型: vue

vue版本: vue3

打包方式: 离线

项目创建方式: HBuilderX

示例代码:

<template>
<view class="container">
<!-- 录音状态 -->
<view class="status">{{ status }}</view>

<!-- 按钮 -->  
<button type="primary" @click="startRecord">开始录音</button>  
<button style="margin: 0 10px" @click="endRecord">结束录音</button>  
<button @click="playVoice">播放录音</button>  

</view>
</template>

<script>
const recorderManager = uni.getRecorderManager();
const innerAudioContext = uni.createInnerAudioContext();
innerAudioContext.autoplay = true;

export default {
data() {
return {
status: "就绪",
voicePath: "",
};
},

onLoad() {
recorderManager.onStop((res) => {
console.log("录音完成:", JSON.stringify(res));
this.voicePath = res.tempFilePath;
this.status = "录音已完成,可以播放";
});

recorderManager.onError((err) => {  
  // 打印最详细的错误对象  
  console.error("录音底层错误详情:", JSON.stringify(err));  
  // this.status = "异常:" + JSON.stringify(err);  
});  

},

methods: {
// 开始录音(去掉报错的 uni.authorize,直接开始录音触发权限)
startRecord() {
try {
console.log("开始录音");
this.status = "正在录音...";

    recorderManager.start({  
      duration: 60000,  
      sampleRate: 44100,  
      numberOfChannels: 1,  
      encodeBitRate: 192000,  
      format: "aac",  
      frameSize: 50,  
    });  
  } catch (e) {  
    console.error("startRecord 失败:", e);  
    this.status = "开始录音失败:" + e.message;  
  }  
},  

// 结束录音  
endRecord() {  
  try {  
    console.log("结束录音");  
    recorderManager.stop();  
    this.status = "已停止录音";  
  } catch (e) {  
    console.error("endRecord 失败:", e);  
  }  
},  

// 播放录音  
playVoice() {  
  console.log("播放录音");  
  if (this.voicePath) {  
    innerAudioContext.src = this.voicePath;  
    innerAudioContext.play();  
    this.status = "播放中...";  
  } else {  
    this.status = "请先录音";  
  }  
},  

},
};
</script>

<style scoped>
.container {
padding: 30px;
text-align: center;
}
.status {
margin-bottom: 20px;
font-size: 16px;
color: #333;
}
</style>

操作步骤:

<template>
<view class="container">
<!-- 录音状态 -->
<view class="status">{{ status }}</view>

<!-- 按钮 -->  
<button type="primary" @click="startRecord">开始录音</button>  
<button style="margin: 0 10px" @click="endRecord">结束录音</button>  
<button @click="playVoice">播放录音</button>  

</view>
</template>

<script>
const recorderManager = uni.getRecorderManager();
const innerAudioContext = uni.createInnerAudioContext();
innerAudioContext.autoplay = true;

export default {
data() {
return {
status: "就绪",
voicePath: "",
};
},

onLoad() {
recorderManager.onStop((res) => {
console.log("录音完成:", JSON.stringify(res));
this.voicePath = res.tempFilePath;
this.status = "录音已完成,可以播放";
});

recorderManager.onError((err) => {  
  // 打印最详细的错误对象  
  console.error("录音底层错误详情:", JSON.stringify(err));  
  // this.status = "异常:" + JSON.stringify(err);  
});  

},

methods: {
// 开始录音(去掉报错的 uni.authorize,直接开始录音触发权限)
startRecord() {
try {
console.log("开始录音");
this.status = "正在录音...";

    recorderManager.start({  
      duration: 60000,  
      sampleRate: 44100,  
      numberOfChannels: 1,  
      encodeBitRate: 192000,  
      format: "aac",  
      frameSize: 50,  
    });  
  } catch (e) {  
    console.error("startRecord 失败:", e);  
    this.status = "开始录音失败:" + e.message;  
  }  
},  

// 结束录音  
endRecord() {  
  try {  
    console.log("结束录音");  
    recorderManager.stop();  
    this.status = "已停止录音";  
  } catch (e) {  
    console.error("endRecord 失败:", e);  
  }  
},  

// 播放录音  
playVoice() {  
  console.log("播放录音");  
  if (this.voicePath) {  
    innerAudioContext.src = this.voicePath;  
    innerAudioContext.play();  
    this.status = "播放中...";  
  } else {  
    this.status = "请先录音";  
  }  
},  

},
};
</script>

<style scoped>
.container {
padding: 30px;
text-align: center;
}
.status {
margin-bottom: 20px;
font-size: 16px;
color: #333;
}
</style>

预期结果:

<template>
<view class="container">
<!-- 录音状态 -->
<view class="status">{{ status }}</view>

<!-- 按钮 -->  
<button type="primary" @click="startRecord">开始录音</button>  
<button style="margin: 0 10px" @click="endRecord">结束录音</button>  
<button @click="playVoice">播放录音</button>  

</view>
</template>

<script>
const recorderManager = uni.getRecorderManager();
const innerAudioContext = uni.createInnerAudioContext();
innerAudioContext.autoplay = true;

export default {
data() {
return {
status: "就绪",
voicePath: "",
};
},

onLoad() {
recorderManager.onStop((res) => {
console.log("录音完成:", JSON.stringify(res));
this.voicePath = res.tempFilePath;
this.status = "录音已完成,可以播放";
});

recorderManager.onError((err) => {  
  // 打印最详细的错误对象  
  console.error("录音底层错误详情:", JSON.stringify(err));  
  // this.status = "异常:" + JSON.stringify(err);  
});  

},

methods: {
// 开始录音(去掉报错的 uni.authorize,直接开始录音触发权限)
startRecord() {
try {
console.log("开始录音");
this.status = "正在录音...";

    recorderManager.start({  
      duration: 60000,  
      sampleRate: 44100,  
      numberOfChannels: 1,  
      encodeBitRate: 192000,  
      format: "aac",  
      frameSize: 50,  
    });  
  } catch (e) {  
    console.error("startRecord 失败:", e);  
    this.status = "开始录音失败:" + e.message;  
  }  
},  

// 结束录音  
endRecord() {  
  try {  
    console.log("结束录音");  
    recorderManager.stop();  
    this.status = "已停止录音";  
  } catch (e) {  
    console.error("endRecord 失败:", e);  
  }  
},  

// 播放录音  
playVoice() {  
  console.log("播放录音");  
  if (this.voicePath) {  
    innerAudioContext.src = this.voicePath;  
    innerAudioContext.play();  
    this.status = "播放中...";  
  } else {  
    this.status = "请先录音";  
  }  
},  

},
};
</script>

<style scoped>
.container {
padding: 30px;
text-align: center;
}
.status {
margin-bottom: 20px;
font-size: 16px;
color: #333;
}
</style>

实际结果:

<template>
<view class="container">
<!-- 录音状态 -->
<view class="status">{{ status }}</view>

<!-- 按钮 -->  
<button type="primary" @click="startRecord">开始录音</button>  
<button style="margin: 0 10px" @click="endRecord">结束录音</button>  
<button @click="playVoice">播放录音</button>  

</view>
</template>

<script>
const recorderManager = uni.getRecorderManager();
const innerAudioContext = uni.createInnerAudioContext();
innerAudioContext.autoplay = true;

export default {
data() {
return {
status: "就绪",
voicePath: "",
};
},

onLoad() {
recorderManager.onStop((res) => {
console.log("录音完成:", JSON.stringify(res));
this.voicePath = res.tempFilePath;
this.status = "录音已完成,可以播放";
});

recorderManager.onError((err) => {  
  // 打印最详细的错误对象  
  console.error("录音底层错误详情:", JSON.stringify(err));  
  // this.status = "异常:" + JSON.stringify(err);  
});  

},

methods: {
// 开始录音(去掉报错的 uni.authorize,直接开始录音触发权限)
startRecord() {
try {
console.log("开始录音");
this.status = "正在录音...";

    recorderManager.start({  
      duration: 60000,  
      sampleRate: 44100,  
      numberOfChannels: 1,  
      encodeBitRate: 192000,  
      format: "aac",  
      frameSize: 50,  
    });  
  } catch (e) {  
    console.error("startRecord 失败:", e);  
    this.status = "开始录音失败:" + e.message;  
  }  
},  

// 结束录音  
endRecord() {  
  try {  
    console.log("结束录音");  
    recorderManager.stop();  
    this.status = "已停止录音";  
  } catch (e) {  
    console.error("endRecord 失败:", e);  
  }  
},  

// 播放录音  
playVoice() {  
  console.log("播放录音");  
  if (this.voicePath) {  
    innerAudioContext.src = this.voicePath;  
    innerAudioContext.play();  
    this.status = "播放中...";  
  } else {  
    this.status = "请先录音";  
  }  
},  

},
};
</script>

<style scoped>
.container {
padding: 30px;
text-align: center;
}
.status {
margin-bottom: 20px;
font-size: 16px;
color: #333;
}
</style>

bug描述:

不执行 recorderManager.onStop((res) => {
console.log("录音完成:", JSON.stringify(res));
this.voicePath = res.tempFilePath;
this.status = "录音已完成,可以播放";
});

recorderManager.onError((err) => {  
  // 打印最详细的错误对象  
  console.error("录音底层错误详情:", JSON.stringify(err));  
  // this.status = "异常:" + JSON.stringify(err);  
});
2026-05-15 13:53 负责人:无 分享
已邀请:
Ask小助手

Ask小助手

欢迎到专业群( uni-app鸿蒙化技术交流群 ) 咨询,群中有相关专业的管理员和群友。

感谢你的反馈,我们来针对这个 bug 进行评审。

1. 反馈内容完整性检查

  • BUG 标题:“uni-app官方提供的录音功能,执行stop后,没有回调onStop\onError” 表述清晰,能直接定位问题。
  • BUG 描述:说明了回调未触发的情况,并给出了相关代码,意图明确。
  • 代码示例:提供了完整的页面代码,可直接运行,无需调整。
  • 复现步骤:此处填写的内容与代码示例重复,没有额外描述操作步骤,但结合示例中的按钮逻辑(开始录音→结束录音),可以理解复现方式。建议后续补充明确的步骤说明,例如:在鸿蒙真机上点击“开始录音”后,再点击“结束录音”,观察控制台无 onStop 日志输出。
  • 预期结果:预期 onStop 回调正常触发并获取录音文件路径,合理。
  • 实际结果:填写的内容再次重复了代码,没有描述实际现象。根据标题推断实际结果是回调未执行,建议修改为具体的现象描述,如“控制台没有打印录音完成的日志,status 也未更新”。
  • 分类信息:已提供开发环境、HBuilderX 版本、手机系统及设备等信息,完整。

总体来看,反馈内容基本完整,但复现步骤和实际结果部分填写有误,需要优化。

2. Bug 是否成立
根据知识库中的文档,uni.getRecorderManager() 在鸿蒙平台上是支持的(HBuilderX 4.31 起)。
在 ISSUE 系统中,存在一个已知相关问题 没有权限时,双数触发开始录音,不会触发onError事件 ,该问题指出在鸿蒙等平台上,当麦克风权限未正确授予时,录音相关回调(包括 onStop/onError)可能不触发。你的场景很可能与此相关。
官方已针对该问题提供了解决方案:推荐使用开源的 uni-recorder 插件来替代原生 API,该插件已对权限和回调逻辑做了适配。
因此,这个 bug 成立,属于已知问题,并非误报。

3. 基本概念与平台注意事项

  • 你使用的平台是 HarmonyOS NEXT,在 uni-app 中属于独立平台,不能使用 app-plus 相关 API(如 plus.audio 等)。你的代码中未出现此类调用,符合规范。
  • 鸿蒙平台需要主动申请麦克风权限,如果未授权,录音 API 可能不会按预期回调。可以先检查应用是否已获得麦克风权限。

建议的解决方案

  1. 替换为官方推荐的 uni-recorder 插件,该插件已处理鸿蒙端的权限和回调异常。
    插件地址:https://gitcode.com/dcloud/uni-api/tree/alpha/uni_modules/uni-recorder
    使用方式参考 ISUE 中的说明。
  2. 若仍想使用原生 API,请确保在调用 recorderManager.start() 前已获取麦克风权限(可通过 uni.authorize 或手动引导用户授权),并监听 onError 来捕获权限错误。
  3. 如果问题依然存在,可以尝试升级 HBuilderX 至最新正式版(目前为 5.07),关注后续版本是否修复了该问题。

如果你在替换插件后仍有疑问,欢迎补充更详细的操作步骤和实际现象,我们再进一步跟进。

内容为 AI 生成,仅供参考
z***@163.com

z***@163.com (作者) - 正式进入uni-app开发

已开启麦克风权限,执行了stop后,还是无法回调onStop\onError

要回复问题请先登录注册