介绍
仓库人员使用PDA扫码时是比较关注提示音播放的,但是陆陆续续出现了一些问题,对用户影响较大
PDA使用的是uniapp框架打包成APP实现
声音播放使用的是uni.createInnerAudioContext()来创建使用
排查难点
- 需要联接PDA调试,手机和PC上复现不了
- uniapp的调试模式比较简陋,只能console
- 在极端使用情况下才能触发
问题一 扫码多次后APP严重卡顿
结论:同一个innerAudioContext实例不能多次执行播放
重点对扫码操作进行了排查,连续使用激光扫码超20+后,出现APP整体卡顿,无法操作
排查扫码逻辑后发现是有一些代码可优化,但扫码优化后卡顿依旧
每次扫码后都会播放成功/失败的提示音,关闭提示音播放,使用明显流畅了
官方也未做出注明为什么播放声音会导致卡顿
最终捣鼓发现,原因是不能使用同一个innerAudioContext实例来多次播放
本意是使用单例模式减少多次创建的开销
问题二 偶尔出现不播放
结论:自动播放使用autoplay = true,不要使用play()
只有出入库扫码后出现,且无法每次必现,增加了调试难度
原代码写法是先创建实例
const audioContext = uni.createInnerAudioContext();
然后赋值文件源,type是文件名
audioContext.src = `/static/voice/${type}.mp3`;
最后播放
audioContext.play();
正常情况都能播放,10次有1-2次没声音
加上onError事件后没声音也不触发error,难以定位
audioContext.onError((e) => {
console.log('onError', e);
});
最终捣鼓发现,需要在赋值文件src前,使用autoplay = true,而不能赋值后直接调用play()
注:如果非要使用play(),可以在onCanplay事件回调里执行播放
audioContext.autoplay = true;
audioContext.src = `/static/voice/${type}.mp3`;
这里的原因比较明显,音源赋值后可能并未加载完成,此时不能直接播放,但是不报错增加了排查的难度
问题三 连续多次播放,后续整个APP都没声音了
结论:实例数量超过了上限,需要销毁实例后再创建
在快速录入零件时,播放多了后概率出现APP声音没了,重启应用后才又有声音
这个问题有一部分PDA复现不了,不巧本人拿的就是这种PDA,耽误了一些时间在测试上
在加上onError捕获后可以检测到异常,原因是创建了过多的实例不能再创建了
于是在播放结束时销毁实例
audioContext.onEnded(() => {
audioContext.destroy();
});
试了试发现不行,于是查到不少同样问题的帖子,需要在onPause里调用销毁实例
试了下貌似可以一直播放了,但发现一个问题:在快速播放时,第2次播放声音会与第一次声音重叠,按理来说,同一时间应该只有一个声音在播放
这里尝试在创建实例前销毁上一次实例
audioContext?.destroy();
audioContext = uni.createInnerAudioContext();
这样两次快速播放过程就是onPlay→onStop→onPlay→onEnded
最终代码
let audioContext;
/**
* 播放音频
* @param {string} type warn | right
*/
const playVoice = (type) => {
return new Promise((resolve) => {
audioContext?.destroy();
audioContext = uni.createInnerAudioContext();
audioContext.onEnded(() => {
// 播放结束
resolve();
});
audioContext.onError((e) => {
console.log('play voice error', e);
});
audioContext.autoplay = true;
audioContext.src = `/static/voice/${type}.mp3`;
});
};
export default playVoice;