原生 App 自己掌控媒体:全屏、自动播放、后台音频都正常。进了 WebView,这种掌控就没了。全屏需要宿主手写一个原生视图;自动播放除非宿主翻两个隐藏配置开关否则被拦;App 一切后台,音频就死。宿主自有的 runtime 提供媒体桥——一块跨设备一致的、可预期的播放面。
WebView 自己没法全屏
在 Android 上,HTML 的 requestFullscreen() 对 <video> 毫无作用,除非 App 的 WebChromeClient 重写了 onShowCustomView() 与 onHideCustomView()。WebView 把视频画面交给宿主后就干等;宿主不实现这个回调,全屏按钮就彻底失灵。跨版本更糟:从 Android 4.0 起,回调拿到的就是一个私有内部类 surface,想侦测播放结束或接自定义控件,就得用反射、按版本分支——这种脆弱性会在下一次系统更新时崩。
iOS 逼你全屏,否则静音
在 iOS 上,WKWebView 默认 allowsInlineMediaPlayback 为 false,于是即便写了 playsinline,视频一播放就被拽进系统全屏播放器。自动播放同样被门控:默认的 mediaTypesRequiringUserActionForPlayback 下,静音自动播放仍要手势,"视频当 banner" 的模式会悄悄卡住。画中画只有当内联播放器本就是 AVPlayerViewController 时才有效——而 App 切后台时它无法自动进入,因为 visibilitychange 不算用户交互。
媒体只接了一半时会崩什么
- 全屏是个死按钮。 Android 上点了没反应,除非宿主接了
onShowCustomView。 - 内联视频弹成全屏。 iOS 默认无视
playsinline,除非宿主在初始化阶段就设好开关——事后改会静默失败。 - 自动播放卡住。 没手势就没播放,唯一的逃生通道是创建 WebView 前翻两个隐藏原生开关。
- 后台音频死掉。 App 一进后台,WebView 媒体就暂停;画中画是唯一出路,且不会自动。
- 跨版本混乱。 Android 的私有 surface 类与 iOS 的开关在每个系统版本都不同。
runtime 解法
宿主自有的 runtime 掌控媒体。宿主原生实现全屏交接,向页面暴露统一的播放 API,在配置阶段一次性设好内联与自动播放开关,并把后台音频桥接到系统媒体会话。一块播放面,所有设备、所有系统版本——而不是一个在工程师手机上能跑、到用户那就崩的网页。
| 维度 | WebView | 宿主自有 runtime |
|---|---|---|
| 全屏 | 除非宿主重写 onShowCustomView 否则失效 | 宿主驱动的交接 |
| iOS 内联播放 | 默认无视 playsinline | 配置阶段设好开关 |
| 自动播放 | 无手势即被拦 | 可预期策略 |
| 后台音频 | 进后台即暂停 | 系统媒体会话桥 |
| 画中画 | 不自动 | 宿主可控 |
| 跨版本 | 按构建各自崩 | 稳定 |
原生 App 从不会失去对媒体的掌控。裸 WebView 却经常如此。runtime 把一块可预期的播放器交到页面手里——并在 App 进后台时让音频继续活。
参考来源
- Android Developers — WebChromeClient#onShowCustomView — 宿主实现全屏视频必须重写的回调。
- webkit.org — iOS 新视频策略 — 为什么 WebKit 对
playsinline与自动播放设门控。 - Apple 开发者论坛 — WKWebView 画中画与后台播放 — DTS 工程师谈内联到画中画的交接与
visibilitychange限制。 - MDN — 自动播放指南 — 每个引擎都执行的用户手势与静音自动播放规则。
- Thomas Visser — WKWebView 中的视频自动播放 — 让内联自动播放生效的两个 WKWebView 开关。