阅读 614

【前端避坑】Howler.js 移动端兼容性评测

| 2021.6.16 修正

  • iOS 从其他场景切回页面后 AudioContext 未能 resume 导致音频无法播放的处理方案
  • 补充 iOS 使用 AudioContext 播放常见无声的原因(物理静音按钮被开启)

起因

XX,快来看看,这个 iPhone7 Plus 手机完全播不出来声音。

XX,快来看看,这个 小米 10 手机播的声音有电流声(滋滋响)。

XX,快来看看,这个 iPhone XR 手机用着用着声音就没了。

此时我的内心是:我就用了一个库 Howler.js ,怎么正常播放个音频就就这么难呢 🦄🐴🐎 ~

根据过往的经验,我准备列一个表来针对性的评测,解决或解释问题 0.0,下面就开始了!

准备工作

好学的我时尚地用 web container 做了一个测试页面,用于测试不同的 config 在不同手机|系统上的表现。

测试页面

上、下的元素点击后,分别会通过 AudioContextAudio 标签播放,

(最后老老实实用了本地的 http-serverweb container 对兼容性确实要求高,很多手机打不开~)

结果及问题

iOS

手机系统AudioContextAudio标签备注
iPhone XRiOS 14.4.2可以播放可以播放
iPhone 7 PlusiOS 14.2需要有Audio标签播放后,才能播放可以播放issue1407 ,预测影响范围:14.0.1 ~ 14.2
iPhone XiOS 13.5.1可以播放可以播放
iPhone 8 PlusiOS 12.1.4可以播放可以播放
iPhone 6siOS 11.3可以播放可以播放
iPhone 5siOS 10.1.1可以播放(但是杂音大一些)可以播放

iOS 14.2 这个 issue1407 bug 属于内核级的问题,根据 issue 可以看出是明明音频 loaded 了,但是状态未发生改变,导致 howler 播放失效,这个问题倾向直接和测试、产品解释一下,不建议修改代码。

其中 iOS 14.4.2, iOS 13.5.1App 有交互且使用 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

手机系统AudioContextAudio标签备注
Mi 10MIUI 12.5.3(Android 11)可以播放(有电流声)浏览器里不能播放,Webview里可以
Mi 11MIUI 12.5.7(Android 11)可以播放(有很大的电流声)浏览器里不能播放,Webview里可以
Mi 9MIUI 12.0.5(Android 10)可以播放浏览器里不能播放,Webview里可以
Mi PAD 4 PLUSMIUI 10.3.2(Android 8.1.0)可以播放浏览器里不能播放,Webview里可以
Redmi Note 5AMIUI 9.2(Android 7.1.2)可以播放可以播放
Oppo Reno2ColorOS v7.1(Android 10)可以播放浏览器里不能播放,Webview里可以
Oppo R15ColorOS v6.0.1(Android 9)可以播放浏览器里不能播放,Webview里可以
Oppo K5ColorOS v7.1(Android 10)可以播放浏览器里不能播放,Webview里可以
Vivo X30 ProOriginOS 1.0(Android 10)可以播放浏览器里可以播放,Webview里部分场景不可以
Vivo X21AFuntouch OS_9 (Android 9)可以播放浏览器里可以播放,Webview里部分场景不可以
Vivo Y66Funtouch OS_3 (Android 6.0.1)可以播放(Webview可以,原生浏览器里不行)浏览器里可以播放,Webview部分场景里不可以
Vivo X21AFuntouch OS_9 (Android 9)可以播放浏览器里可以播放,Webview里部分场景不可以
Galaxy S9Android 8可以播放可以播放
Honor 10EMUI 10.0.0(Android 10)可以播放可以播放
Huawei P20 proEMUI 10.0.0(Android 10)可以播放不点其他区域,首次点击不能播放
Huawei P40EMUI 11.0.0(Android 10)可以播放可以播放
Huawei Mate40 ProEMUI11.0.0(Android 10)可以播放可以播放
OnePlus 7T ProHydrogenOS v10.0.4 (Android 10)浏览器(chrome53)里不可以播放,Webview(chrome77)里可以播放可以播放
坚果3SmartisanOS v6.7.3 (Android 7.1.2)可以播放可以播放
锤子T2SmartisanOS v4.1.0 (Android 5.1.1)浏览器(chrome62)里可以播放,Webview(chrome53)里不能播放可以播放

米系

ISSUE1: 小米自带浏览器无法播放

小米的自带浏览器中在 canplaythrough 事件中,无法获取到音频的 duration ,导致 howler.js 源码中播放时,seek >= stop 成立,直接 return

image.png

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 和 锤子/坚果

这几款手机让我发现

  • WebviewChrome 版本在自带浏览器中和应用的 Webview 中是不一致的
  • 尽管 Can I UseAudioContext 是可以在 Android 5 上使用的,但实际验证下来 Chrome53 确实未能播放

关于 Chrome 版本不一致真的没有想到,一直以为 App 内的 Webview 是与原生浏览器复用内核的。后来查阅了一些资料后发现

  • App 内的 Webview 版本是跟随系统的,所以系统升级,版本也就升级了。
  • 浏览器的版本是跟随应用版本的,依赖应用升级

解决方案

  • 进入页面播放音频场景:
    • 最佳:调用客户端交互方法/平台api实现
    • 备选:产品沟通引导点击触发音频
    • 备选:产品沟通去掉该场景
  • 大部分场景使用 AudioContext 播放
    • 注意部分 iOS 系统的 bug
    • iOS 注意处理从原生场景切换回来的场景
    • 注意iOS静音物理按键关闭后,AudioContext不能发出声音
  • 个别机型/浏览器版本,根据 ua 做特殊处理,加入 html5: true 的配置
  • 需要在浏览器中使用的场景,规避 canplaythrough 获取不到 duration 的坑,建议自己 fork 一个 howler

音频交互确实存在大量的坑,本文只涉及了和 howler 有关的一部分.

各位看官做需求时多多小心,祝好!

文章分类
前端
文章标签