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对音频播放有更严格的用户交互限制。

问题分析:

  1. iOS要求音频播放必须由用户主动触发,不能通过异步回调自动播放
  2. 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;
  }
}
回到顶部