UniApp跨平台自定义消息语音播报实战指南
1. 为什么需要自定义消息语音播报
在移动应用开发中,消息推送是提升用户活跃度和留存率的重要手段。但普通的文字通知往往容易被用户忽略,特别是在商户收款、物流提醒、重要事件通知等场景下,语音播报能够更直接有效地触达用户。
举个例子,我去年给一个生鲜超市开发收款系统时就遇到过这个问题。店员反映在忙碌时经常错过手机推送的订单通知,导致顾客等待时间过长。后来我们给APP加上了语音播报功能,每当有新订单时就会自动播放"新订单:3斤苹果,2箱牛奶",店员在5米外都能听到,大大提高了接单效率。
UniApp作为跨平台开发框架,要实现Android和iOS双端的语音播报功能,需要解决几个核心问题:
- 平台差异:iOS和Android的推送机制、音频播放接口完全不同
- 离线推送:应用被杀死或退到后台时如何保证语音正常播放
- 厂商适配:不同安卓厂商对后台进程的限制策略各异
- 性能优化:避免频繁播报导致的内存泄漏和卡顿
2. 基础环境准备
2.1 UniPush服务开通
首先需要在UniApp项目中开通UniPush服务。这里有个坑要注意:如果你需要用自己的服务器发送推送消息,必须使用UniPush 1.0版本,因为2.0版本目前只支持通过UniApp后台发送消息。
开通步骤:
- 在HBuilderX中打开manifest.json文件
- 找到"App模块配置"选项卡
- 勾选"Push(消息推送)"下的"UniPush"
- 点击右侧的"配置"按钮,按照指引完成服务开通
2.2 原生插件集成
要实现自定义语音播报,我们需要使用DCloud官方提供的两个插件:
- DCloud-PushSound插件:用于设置自定义推送铃声
- DCloud-TTS插件:用于动态文本转语音播报
安装方法:
// 在manifest.json的"App原生插件配置"中添加 { "nativePlugins": [ { "type": "module", "name": "DCloud-PushSound", "version": "1.0.2" }, { "type": "module", "name": "DCloud-TTS", "version": "1.0.0" } ] }2.3 音频资源准备
对于固定语音提示(如"新订单提醒"),需要提前准备好音频文件并放到指定目录:
- iOS:必须是.caf格式,放在nativeplugins/DCloud-PushSound/ios/目录下
- Android:支持.mp3/.wav格式,放在nativeplugins/DCloud-PushSound/android/res/raw/目录下
可以使用以下命令将其他格式转换为iOS需要的caf格式:
afconvert input.mp3 output.caf -d ima4 -f caff -v3. 实现固定语音提示
3.1 Android端配置
Android 8.0以上版本需要使用通知渠道来设置自定义铃声。以下是完整配置示例:
const plugin = uni.requireNativePlugin("DCloud-PushSound"); plugin.setCustomPushChannel({ soundName: 'order_alert', // 对应raw目录下的order_alert.mp3 channelId: 'order_channel', channelDesc: '订单通知渠道', enableLights: true, // 开启呼吸灯 enableVibration: true, // 开启震动 importance: 4, // 高优先级 lockscreenVisibility: 1 // 锁屏可见 });重要提示:Android的通知渠道一旦创建就无法修改配置。如果需要更改铃声,必须使用新的channelId重新创建渠道。
3.2 iOS端配置
iOS的配置相对简单,但需要注意音频文件必须放在正确位置:
if (plus.os.name == "iOS") { setCustomPushChannel({ soundName: 'order_alert', // 对应order_alert.caf文件 channelId: 'order_channel', channelDesc: '订单通知渠道' }); }3.3 服务端推送配置
以PHP为例,发送推送时需要指定sound参数:
$payload = [ 'title' => '新订单', 'content' => '您有新的生鲜订单', 'sound' => 'order_alert.caf', // iOS 'android' => [ 'sound' => 'order_alert' // Android ] ];4. 实现动态语音播报
对于需要根据推送内容动态播报的场景(如"支付宝收款100元"),需要使用TTS文本转语音技术。
4.1 TTS插件初始化
首先初始化语音引擎:
const tts = uni.requireNativePlugin("DCloud-TTS"); // Android需要设置语音包 tts.initEngine({ appid: '你的百度语音APPID', // 或使用系统自带引擎 appkey: '你的百度语音APPKEY', secret: '你的百度语音SECRET' }, (res) => { console.log('初始化结果:', res); });4.2 接收透传消息处理
在App.vue中监听推送消息:
onLaunch: function() { plus.push.addEventListener('receive', (msg) => { if (msg.payload) { const data = JSON.parse(msg.payload); this.playVoice(data.content); // 播放语音 } }); }, methods: { playVoice(text) { tts.speak({ text: text, speed: 5, // 语速1-9 pitch: 5, // 音调1-9 volume: 15 // 音量0-15 }, (res) => { console.log('播放完成', res); }); } }4.3 离线推送处理方案
应用被杀死时无法直接播放语音,可以采用以下方案:
- 厂商通道+特殊铃声:通过各厂商通道发送特定铃声标识
- 后台服务唤醒:Android可以启动前台服务保持运行
- 本地通知+语音文件:下载预生成的语音文件后播放
以华为厂商通道为例:
// 华为推送payload示例 $huaweiPayload = [ 'notification' => [ 'title' => '收款通知', 'body' => '支付宝收款100元', 'sound' => 'receipt_100' // 对应预置的音频文件 ] ];5. 厂商适配与性能优化
5.1 主流厂商适配指南
| 厂商 | 关键配置 | 注意事项 |
|---|---|---|
| 华为 | 申请自分类权益 | 必须安装华为移动服务 |
| 小米 | 控制台配置渠道 | 每天最多5条运营消息 |
| OPPO | ColorOS 3.1+ | 默认关闭通知权限 |
| vivo | 需应用上架 | 文案不能含"test"等词 |
5.2 常见问题解决方案
问题1:华为手机后台收不到推送
- 解决方案:引导用户设置"允许后台活动"
问题2:iOS语音播放被中断
- 解决方案:配置AVAudioSession为播放模式
plus.ios.import('AVFoundation').AVAudioSession.sharedInstance() .setCategoryError('AVAudioSessionCategoryPlayback');问题3:Android 8.0+铃声不生效
- 解决方案:确保每次修改铃声都使用新的channelId
5.3 性能优化建议
- 音频预加载:常用语音提前加载到内存
- 消息去重:相同内容10秒内不重复播报
- 音量自适应:根据环境噪音动态调整音量
- 资源释放:长时间不用时释放TTS引擎
// 音频预加载示例 const preloadAudio = ['welcome', 'goodbye']; preloadAudio.forEach(name => { tts.preload({text: name}); });6. 完整实现示例
下面是一个收款语音播报的完整代码示例:
- 首先在manifest.json中配置插件和权限
- 创建语音播放工具类voice.js:
const tts = uni.requireNativePlugin("DCloud-TTS"); const pushSound = uni.requireNativePlugin("DCloud-PushSound"); let isPlaying = false; const queue = []; export default { init() { // 初始化TTS引擎 tts.initEngine({ mode: 'online' // 使用在线引擎更自然 }); // 设置Android通知渠道 if (plus.os.name !== 'iOS') { pushSound.setCustomPushChannel({ channelId: 'voice_channel', channelDesc: '语音播报通道' }); } }, play(text) { if (isPlaying) { queue.push(text); return; } isPlaying = true; tts.speak({ text: text, speed: 5 }, () => { isPlaying = false; if (queue.length) { this.play(queue.shift()); } }); } }- 在App.vue中使用:
import voice from './utils/voice'; export default { onLaunch() { voice.init(); plus.push.addEventListener('receive', (msg) => { if (msg.payload) { const data = JSON.parse(msg.payload); if (data.type === 'payment') { voice.play(`支付宝收款${data.amount}元`); } } }); } }7. 实际项目经验分享
在最近一个社区团购项目中,我们实现了以下语音播报场景:
- 新订单:"新订单!12栋302需要3斤排骨"
- 到货通知:"您购买的草莓已到自提点"
- 系统提醒:"今日23点将截止下单"
踩过的一些坑值得分享:
- 音频格式问题:最初iOS使用.mp3文件导致音量过小,转成.caf后正常
- 华为保活:部分华为机型需要单独引导用户设置后台权限
- 语速控制:老年人居多的社区需要将语速降到3级
- 多语言支持:双语社区需要根据用户设置切换中英文播报
一个实用的调试技巧是在开发时添加测试按钮:
<button @click="voice.play('测试语音123')">测试播报</button>对于需要更高定制化的项目,可以考虑以下扩展方向:
- 语音模板:支持动态变量插入,如"您好{name},您的订单{code}已发货"
- 情绪控制:通过参数控制高兴、紧急等不同语气
- 离线语音包:提前下载常用语音避免网络依赖
- 3D音效:使用WebAudio API实现空间音效
最后提醒一点,语音播报功能一定要提供关闭开关,尊重用户选择。我们发现在安静场合(如会议室),很多用户会临时关闭语音提示。
