| 2021.6.16 修正
iOS从其他场景切回页面后AudioContext未能resume导致音频无法播放的处理方案- 补充
iOS使用AudioContext播放常见无声的原因(物理静音按钮被开启)
起因
XX,快来看看,这个 iPhone7 Plus 手机完全播不出来声音。
XX,快来看看,这个 小米 10 手机播的声音有电流声(滋滋响)。
XX,快来看看,这个 iPhone XR 手机用着用着声音就没了。
此时我的内心是:我就用了一个库 Howler.js ,怎么正常播放个音频就就这么难呢 🦄🐴🐎 ~
根据过往的经验,我准备列一个表来针对性的评测,解决或解释问题 0.0,下面就开始了!
准备工作
好学的我时尚地用 web container 做了一个测试页面,用于测试不同的 config 在不同手机|系统上的表现。
上、下的元素点击后,分别会通过 AudioContext 和 Audio 标签播放,
(最后老老实实用了本地的 http-server,web container 对兼容性确实要求高,很多手机打不开~)
结果及问题
iOS
| 手机 | 系统 | AudioContext | Audio标签 | 备注 |
|---|---|---|---|---|
| iPhone XR | iOS 14.4.2 | 可以播放 | 可以播放 | |
| iPhone 7 Plus | iOS 14.2 | 需要有Audio标签播放后,才能播放 | 可以播放 | issue1407 ,预测影响范围:14.0.1 ~ 14.2 |
| iPhone X | iOS 13.5.1 | 可以播放 | 可以播放 | |
| iPhone 8 Plus | iOS 12.1.4 | 可以播放 | 可以播放 | |
| iPhone 6s | iOS 11.3 | 可以播放 | 可以播放 | |
| iPhone 5s | iOS 10.1.1 | 可以播放(但是杂音大一些) | 可以播放 |
iOS 14.2 这个 issue1407 bug 属于内核级的问题,根据 issue 可以看出是明明音频 loaded 了,但是状态未发生改变,导致 howler 播放失效,这个问题倾向直接和测试、产品解释一下,不建议修改代码。
其中 iOS 14.4.2, iOS 13.5.1 在 App 有交互且使用 WebAudioContext 的情况下,会碰上从原生界面切换回来无法正常播放的问题,issue1234。
目前采用的解决方案如下:(2021.6.16更新:之前方案后验证也有问题,目前最新方案为从原生界面/后台进入页面后,主动停止audioContext)
// 调用 play 前同步 Howler.state
// ❌
import { Howler } from 'howler'
if (getPlatform() === 'ios' && (Howler.ctx.state === 'running' || Howler.ctx.state === 'suspended')) {
(Howler as any).state = Howler.ctx.state;
}
sound.play();
// 🙆♂️
function HowlerSuspend() {
try {
Howler.state = 'suspended';
Howler.ctx?.suspend();
} catch (e) {
console.log('HowlerSuspend error', e);
}
}
// 可用 visibityChange 事件代替
hybridAddListener('onEntryPageEvent', () => {
// 重新进入页面,将Howler的state置为 suspended
if (getPlatform() === 'ios') {
HowlerSuspend();
}
});
Android
| 手机 | 系统 | AudioContext | Audio标签 | 备注 |
|---|---|---|---|---|
| Mi 10 | MIUI 12.5.3(Android 11) | 可以播放(有电流声) | 浏览器里不能播放,Webview里可以 | |
| Mi 11 | MIUI 12.5.7(Android 11) | 可以播放(有很大的电流声) | 浏览器里不能播放,Webview里可以 | |
| Mi 9 | MIUI 12.0.5(Android 10) | 可以播放 | 浏览器里不能播放,Webview里可以 | |
| Mi PAD 4 PLUS | MIUI 10.3.2(Android 8.1.0) | 可以播放 | 浏览器里不能播放,Webview里可以 | |
| Redmi Note 5A | MIUI 9.2(Android 7.1.2) | 可以播放 | 可以播放 | |
| Oppo Reno2 | ColorOS v7.1(Android 10) | 可以播放 | 浏览器里不能播放,Webview里可以 | |
| Oppo R15 | ColorOS v6.0.1(Android 9) | 可以播放 | 浏览器里不能播放,Webview里可以 | |
| Oppo K5 | ColorOS v7.1(Android 10) | 可以播放 | 浏览器里不能播放,Webview里可以 | |
| Vivo X30 Pro | OriginOS 1.0(Android 10) | 可以播放 | 浏览器里可以播放,Webview里部分场景不可以 | |
| Vivo X21A | Funtouch OS_9 (Android 9) | 可以播放 | 浏览器里可以播放,Webview里部分场景不可以 | |
| Vivo Y66 | Funtouch OS_3 (Android 6.0.1) | 可以播放(Webview可以,原生浏览器里不行) | 浏览器里可以播放,Webview部分场景里不可以 | |
| Vivo X21A | Funtouch OS_9 (Android 9) | 可以播放 | 浏览器里可以播放,Webview里部分场景不可以 | |
| Galaxy S9 | Android 8 | 可以播放 | 可以播放 | |
| Honor 10 | EMUI 10.0.0(Android 10) | 可以播放 | 可以播放 | |
| Huawei P20 pro | EMUI 10.0.0(Android 10) | 可以播放 | 不点其他区域,首次点击不能播放 | |
| Huawei P40 | EMUI 11.0.0(Android 10) | 可以播放 | 可以播放 | |
| Huawei Mate40 Pro | EMUI11.0.0(Android 10) | 可以播放 | 可以播放 | |
| OnePlus 7T Pro | HydrogenOS v10.0.4 (Android 10) | 浏览器(chrome53)里不可以播放,Webview(chrome77)里可以播放 | 可以播放 | |
| 坚果3 | SmartisanOS v6.7.3 (Android 7.1.2) | 可以播放 | 可以播放 | |
| 锤子T2 | SmartisanOS v4.1.0 (Android 5.1.1) | 浏览器(chrome62)里可以播放,Webview(chrome53)里不能播放 | 可以播放 |
米系
ISSUE1: 小米自带浏览器无法播放
小米的自带浏览器中在 canplaythrough 事件中,无法获取到音频的 duration ,导致 howler.js 源码中播放时,seek >= stop 成立,直接 return 了
ISSUE2: 电流声大
小米10、11 webview 中偶尔会出现滋滋响的电流声,断断续续的杂音,一开始以为是 sampleRate 与音频的采样率不一致导致的,后来经过很多反复的尝试,并未达到预想效果。网上查了很多资料,有说硬件的问题,但是Android 原生播放的音频并不会有杂音,最终通过判断 ua 加上 html5: true 的配置播放,效果好了很多。
Oppo
自带浏览器中的问题同小米,Webview 内表现良好
Vivo
ISSUE1: Webview 中部分场景无法使用 html5: true 播放
因为使用的场景都是点击后,等待渲染或事件触发后再播放音频,触发了 audio 只能靠用户的行为来触发的限制。
Uncaught (in promise) DOMException: play() can only be initiated by a user gesture.
使用 AudioContext 播放音频没有这个限制。
当然可能可以通过原生设置 webview config 来解决,待验证:
webSettings.setMediaPlaybackRequiresUserGesture(false)
Huawei
整体表现良好
OnePlus 和 锤子/坚果
这几款手机让我发现
Webview的Chrome版本在自带浏览器中和应用的Webview中是不一致的- 尽管
Can I Use上AudioContext是可以在Android 5上使用的,但实际验证下来Chrome53确实未能播放
关于 Chrome 版本不一致真的没有想到,一直以为 App 内的 Webview 是与原生浏览器复用内核的。后来查阅了一些资料后发现
App内的Webview版本是跟随系统的,所以系统升级,版本也就升级了。- 浏览器的版本是跟随应用版本的,依赖应用升级
解决方案
- 进入页面播放音频场景:
- 最佳:调用客户端交互方法/平台api实现
- 备选:产品沟通引导点击触发音频
- 备选:产品沟通去掉该场景
- 大部分场景使用
AudioContext播放- 注意部分 iOS 系统的 bug
- iOS 注意处理从原生场景切换回来的场景
- 注意iOS静音物理按键关闭后,AudioContext不能发出声音
- 个别机型/浏览器版本,根据
ua做特殊处理,加入html5: true的配置 - 需要在浏览器中使用的场景,规避
canplaythrough获取不到duration的坑,建议自己fork一个howler
音频交互确实存在大量的坑,本文只涉及了和 howler 有关的一部分.
各位看官做需求时多多小心,祝好!