在 WebView 里,一个网页要拿相机或定位,必须过两道独立的门——系统权限和 WebView 权限;而在 Android 上,如果 App 没实现那个回调,第二道门默认就是拒绝。更糟的是,Android 的 WebView 把 Web 权限处理的全部控制权交给宿主 App,不像真正的浏览器有按站点的同意——一旦系统授予了定位,App 就能把定位/相机分享给 WebView 里加载的任何页面,不再有任何提示。iOS 上,WKWebView 在 App 的提示之外又加了一道提示,而 TikTok、Instagram 这类内置浏览器官方不支持。宿主自有 runtime 提供统一的权限代理,把原生与 Web 同意对齐、强制按源授权、要求安全上下文,并只弹一次一致的提示。

两道门,第二道默认说「不」

Android 上,页面调用 getUserMedia 或 navigator.geolocation 会撞上两道检查。第一道是系统权限——CAMERA、RECORD_AUDIO 或定位在 manifest 里声明、并在运行时(API 23+)被授予。第二道是 WebView 权限:就算系统已经授权,页面在 App 这边仍表现为 WebChromeClient.onPermissionRequest(相机/麦克风)或 onGeolocationPermissionsShowPrompt(定位),而默认答案是否。没实现这个回调,症状会让人抓狂——权限已授予、getUserMedia 已调用、却什么都没发生,没有对话框,只有被 reject 的 promise。定位只对安全来源调用:面向 Android 7.0+ 的 App,HTTP 页面在任何人点击之前就被自动拒绝。

没有「按站点」同意,不像浏览器

一项 2025 年关于 Android WebView 权限执行的研究揭示了结构性缺口:传统浏览器会针对每个站点弹出提示、让用户逐个授予或拒绝,而 Android 的 WebView 把整个决定权委托给宿主 App。如果 App 实现了回调,它可以为任意源授予或拒绝,而系统里没有内置的按站点记忆或面向用户的控制。更深的隐患是:一旦系统把定位权限授予了 App,App 就能把这份定位交给 WebView 里加载的任何网站、无需用户再次同意——用户根本看不到是哪个页面在访问什么。开发者必须自己把这套同意 UI 做出来,否则它不存在。

iOS:第二道提示,与静默降级

iOS 上,WKWebView 的相机/麦克风访问要求 App 实现 requestMediaCapturePermissionFor(iOS 15+)以及相应 entitlement;在此之前,WebRTC 在 WKWebView 里根本不可用。即便实现了,用户仍会看到两道提示——一道来自 App,一道来自站点——而文档给出的解法是让 App 通过 delegate 跳过第二道,这意味着用户按页面的选择由 App 而非页面决定。当页面通过自定义 scheme handler 加载时,提示会把 App 的名字当作来源显示,让用户困惑。而微信、抖音、Instagram 这类内置浏览器带有额外约束、UI 也会坏,官方明确不予支持。

没有权限代理,你会失去什么

  • 单一事实来源。 原生与 Web 的权限状态各住各处,会漂移、会矛盾。
  • 按源控制。 Android WebView 没有按站点的授权记忆;App 要么全给、要么全拒。
  • 用户可见性。 用户无法判断是哪个页面在访问相机或定位,因为同意活在 App 代码里。
  • 安全上下文强制。 HTTP 页面在现代 Android 上静默失去定位,原因不可见。
  • 一致的提示。 iOS 双重提示;内置浏览器干脆直接坏掉。

runtime 解法

宿主自有 runtime 在页面之下提供统一的权限代理。它维护一份原生与 Web 共用的同意存储,像浏览器那样强制按源授权,在任何敏感 API 被提供之前要求安全上下文,并呈现一次一致、用户可读的提示——点名页面与资源。App 的系统授权与页面的 Web 请求在同一处被调解,于是同意可见、可撤销,也绝不会被悄悄分享给未知来源。

维度WebView宿主自有 runtime
同意模型App 决定一切,无按站点记忆统一存储,按源授权
Android 第二道门onPermissionRequest 默认拒绝代理按源授予,可见
iOS 提示两道提示,App 可跳过站点提示一次一致提示
安全上下文HTTP 定位静默拒绝强制,并解释
用户可见性看不到哪个页面在访问什么提示点名页面与资源
内置浏览器不支持,UI 坏一个 runtime,到处支持

浏览器之所以值得信任,是因为它会在正确的时刻、向正确的对象、问正确的问题。原生 WebView 把这份决定推进不透明的 App 代码,然后默认说「不」。runtime 把浏览器的同意契约夺回来——按源、可见、可撤销——于是一个需要相机的页面,拿到的是用户真正看得懂的提示。

参考来源