uni-app在小程序端安卓设备音频播放正常ios无法播放也无报错
uni-app在小程序端安卓设备音频播放正常ios无法播放也无报错
| 项目信息 | 值 |
|---|---|
| 产品分类 | uniapp/小程序/微信 |
| PC开发环境 | Windows |
| PC开发环境版本 | win11 22631 |
| HBuilderX类型 | 正式 |
| HBuilderX版本 | 4.76 |
| 第三方开发者工具 | 1.06.2504010 |
| 基础库版本 | 1.0.0 |
| 项目创建方式 | HBuilderX |
示例代码:
/**
* 获取百度 Access Token(跨平台)
*/
async getAccessToken() {
const AK = "YOUR_BAIDU_API_KEY";
const SK = "YOUR_BAIDU_SECRET_KEY";
const BASE_API = 'https://your-domain.com/api';
const cached = uni.getStorageSync('baidu_access_token');
if (cached) {
const { token, expires_at } = JSON.parse(cached);
if (Date.now() < expires_at) {
return token;
}
}
const url = `${BASE_API}/baidu/token?grant_type=client_credentials&client_id=${AK}&client_secret=${SK}`;
try {
const [error, res] = await uni.request({
url,
method: 'POST',
header: { 'Content-Type': 'application/x-www-form-urlencoded' }
});
if (error) throw error;
if (res.statusCode !== 200) throw new Error(`HTTP ${res.statusCode}`);
const data = res.data;
if (data.access_token) {
uni.setStorageSync('baidu_access_token', JSON.stringify({
token: data.access_token,
expires_at: Date.now() + (data.expires_in - 600) * 1000
}));
return data.access_token;
} else {
throw new Error(data.error_description || '获取token失败');
}
} catch (error) {
console.error('获取 Access Token 失败:', error);
throw new Error(`获取百度语音 token 失败:${error.message}`);
}
},
/**
* 预览音频(跨平台兼容,使用 MP3)
*/
async previewAudio() {
if (!this.newAudio.text?.trim()) {
uni.showToast({ title: '请输入需要合成的文本', icon: 'none' });
return;
}
if (this.newAudio.text.length > 30) {
uni.showToast({ title: '文本不能超过30个字', icon: 'none' });
return;
}
this.previewLoading = true;
try {
const BASE_API = 'https://your-domain.com/api'; // ← 已脱敏
const deviceInfo = uni.getSystemInfoSync();
const cuid = `uniapp_${(deviceInfo.deviceId || deviceInfo.model || 'unknown').replace(/\s+/g, '_')}_${Date.now()}`;
const encodedText = encodeURIComponent(this.newAudio.text);
const params = {
tex: encodedText,
cuid,
ctp: '1',
lan: 'zh',
spd: this.newAudio.spd.toString(),
pit: this.newAudio.pit.toString(),
vol: this.newAudio.vol.toString(),
per: this.newAudio.per.toString(),
aue: '3' // MP3 格式
};
const accessToken = await this.getAccessToken();
params.tok = accessToken;
const queryString = Object.keys(params)
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
.join('&');
console.log('【调试】请求参数:', queryString);
const [error, res] = await uni.request({
url: `${BASE_API}/baidu/tts`,
method: 'POST',
header: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: queryString,
responseType: 'arraybuffer'
});
console.log('【调试】响应状态:', res?.statusCode);
console.log('【调试】响应头:', res?.header);
if (error) throw error;
if (res.statusCode !== 200) {
const text = String.fromCharCode.apply(null, new Uint8Array(res.data));
throw new Error(`HTTP ${res.statusCode}: ${text.substring(0, 100)}`);
}
const contentType = (res.header['Content-Type'] || res.header['content-type'] || '').toLowerCase();
console.log('【调试】Content-Type:', contentType);
if (contentType.includes('application/json')) {
const errData = typeof res.data === 'string' ? JSON.parse(res.data) : res.data;
throw new Error(`合成失败: ${errData.err_msg || errData.error}`);
}
if (!contentType.includes('audio')) {
console.warn('【警告】非音频响应,尝试转文本预览:');
try {
const text = String.fromCharCode.apply(null, new Uint8Array(res.data));
console.log('【调试】响应文本预览:', text.substring(0, 200));
} catch (e) {
console.log('【调试】转文本失败:', e);
}
throw new Error('服务器未返回音频数据');
}
// 保存为临时 MP3 文件
const filePath = `${uni.env.USER_DATA_PATH}/preview_${Date.now()}.mp3`;
await new Promise((resolve, reject) => {
uni.getFileSystemManager().writeFile({
filePath,
data: res.data,
encoding: '',
success: resolve,
fail: reject
});
});
// 播放音频
if (this.previewAudioElement) {
this.previewAudioElement.destroy();
}
this.previewAudioElement = uni.createInnerAudioContext();
this.previewAudioElement.src = filePath;
this.previewAudioElement.onError((e) => {
console.error('【播放错误】', e);
uni.showToast({ title: '播放失败: ' + (e.errMsg || e.errCode), icon: 'none' });
});
this.previewAudioElement.onEnded(() => {
uni.getFileSystemManager().unlink({ filePath, fail: () => {} });
});
this.previewAudioElement.play();
uni.showToast({ title: '音频预览播放中', icon: 'success' });
} catch (error) {
console.error('【最终错误】语音合成失败:', error);
uni.showToast({ title: `语音合成失败: ${error.message}`, icon: 'none' });
} finally {
this.previewLoading = false;
}
}
操作步骤:
- hbuilder运行在微信开发者工具点击按钮,正常播放。同时也发布到微信小程序端,ios无任何报错没有音频播放,安卓可以。
预期结果:
- ios端也可以播放
实际结果:
- ios端无法播放,安卓端正常播放
bug描述:
- 问题描述
- 在微信开发者工具中,调用语音合成接口预览音频功能完全正常;在真机调试时,安卓端可以正确使用,但是ios端没有声音播放。
更多关于uni-app在小程序端安卓设备音频播放正常ios无法播放也无报错的实战教程也可以访问 https://www.itying.com/category-93-b0.html
2 回复
同样的代码,试试原生微信小程序是否也有这个问题
更多关于uni-app在小程序端安卓设备音频播放正常ios无法播放也无报错的实战教程也可以访问 https://www.itying.com/category-93-b0.html
这是一个典型的iOS音频播放兼容性问题。主要原因是iOS对音频播放有更严格的用户交互限制。
问题分析:
- iOS要求音频播放必须由用户主动触发,不能通过异步回调自动播放
- 在
previewAudio()方法中,音频播放是在网络请求完成后通过createInnerAudioContext().play()触发的,这在iOS上会被系统阻止
解决方案: 在用户点击事件中预先创建并初始化音频上下文,确保播放动作与用户操作保持同步。
修改建议:
// 在组件初始化时创建音频上下文
data() {
return {
previewAudioElement: null
}
},
onLoad() {
// 提前创建音频上下文
this.previewAudioElement = uni.createInnerAudioContext();
this.previewAudioElement.onError((e) => {
console.error('【播放错误】', e);
uni.showToast({ title: '播放失败: ' + (e.errMsg || e.errCode), icon: 'none' });
});
},
// 修改预览音频方法
async previewAudio() {
// ... 前面的代码保持不变
try {
// ... 网络请求和文件保存代码保持不变
// 直接使用已创建的音频上下文
this.previewAudioElement.src = filePath;
this.previewAudioElement.onEnded(() => {
uni.getFileSystemManager().unlink({ filePath, fail: () => {} });
});
// 立即播放,确保与用户点击事件在同一调用栈中
setTimeout(() => {
this.previewAudioElement.play();
}, 0);
uni.showToast({ title: '音频预览播放中', icon: 'success' });
} catch (error) {
// ... 错误处理保持不变
} finally {
this.previewLoading = false;
}
}

