前言
如果你是一名 Android 开发者,那么你大概率经历过这样一个时代:WebView 经常被看成“套壳”“H5 页面”“临时方案”,甚至只要一个 App 里用了大量 WebView,很多人就会下意识觉得它“不够原生”。
老实说,淘宝 App 的糟糕使用体验,我一直觉得就是 Web 没有优化好导致的。
这次 Google I/O 上,官方明确表达了:Build native Android experiences for web apps using WebView。
当然,它的主旨并不是“如何简单地把网页塞进 App 里”。它真正讨论的是:当你已经拥有一个成熟的 Web App,如何利用 Android WebView,把它逐步改造成一个更接近原生体验的 Android 应用。
换句话说,这不是一条“WebView 套壳教程”,而是一条“如何让 Web App 拥有 Android 原生能力”的工程路线。
WebView 的问题
很多 Android 开发者对 WebView 的第一印象,通常停留在:
webView.loadUrl("https://example.com")
但这只是 WebView 最基础的能力。真正难的地方,不是把网页显示出来,而是让这个网页在 Android App 里“像一个真正的 App”。
一个普通网页放进 WebView 之后,马上会遇到很多体验断层,例如:
- 文件上传是不是能自然调用系统照片选择器?
- 网页触发下载时,是否能交给 Android 的下载管理器?
- 网页需要通知用户时,能不能触发原生通知?
- 网络不稳定时,能不能做缓存和离线支持?
- Web 代码和 Android 原生代码之间,能不能安全、稳定地双向通信?
这些问题,其实都有解法——JS Bridge。只要通过 JS 接口调用原生方法,理论上都能搞定。
但是!这样做很麻烦。大部分开发者在开发 Web App 的时候,一定是先使用一大堆的第三方库或者 Web 打包工具才能完成开发。
不能开箱即用,才是问题的核心,而这,正是这次主题真正关心的重点。
Android 官方文档也强调,WebView 本质上是 Android View 的一个扩展,它默认只负责显示网页,并不包含完整浏览器的地址栏、导航控制等能力。
因此,一旦你想让它承载一个完整的 Web App,就必须额外处理导航、JavaScript、原生通信、窗口管理等问题。
补齐原生体验
Google 介绍了一个更贴近真实业务的示例:一个 Web 形态的笔记应用。
这个选择其实很典型,因为笔记类应用看起来很简单,但它刚好能覆盖 WebView App 常见的几个关键能力:通知、图片选择、文件下载、缓存、离线、以及 Web 与 Native 的互相调用。
Google 想用一个完整场景告诉你:如果你已经有一个 Web App,那么可以通过 WebView 和 Android 原生能力,把它一步一步做得更像真正的 Android App。
一、互相通信
一个 WebView App 想获得原生能力,最重要的一步就是打通 Web 与 Native 的通信。
否则网页永远只是网页,Android 也只是外面那个壳。
在传统 WebView 中,我们经常会看到 addJavascriptInterface() 这种方式:Android 向 WebView 注入一个对象,JavaScript 可以通过这个对象调用 Android 侧的方法(上面提到的 JS Bridge)。
例如网页里点击一个按钮,然后让 Android 侧弹出 Toast、打开系统能力,或者执行某个原生逻辑。
但这里有一个非常重要的安全前提:你必须清楚 WebView 中加载的内容是否可信。
Android 官方文档明确提醒,addJavascriptInterface() 可能带来安全风险,因为它允许 JavaScript 控制 Android App 暴露出来的对象。
因此它更适合用在你完全控制 HTML 和 JavaScript 来源的场景中,而不适合让用户随便打开第三方网页。
这也正是 WebView 工程里最容易被忽略的问题:WebView 不是浏览器。浏览器会帮你处理大量安全边界,而当你把网页放进自己的 App 时,很多边界就需要你自己设计。
我在 15 - 17 年,有过很长一段时间的 Web App 开发经验,我记得当时我们就是编写了一个超级大的
JavascriptInterface,然后注入WebView,里面的 Web 也是我们本地的网页(没错,本地的,定期会从服务器下载新的网页),现在想起来,搞不好那个已经具备了小程序的原型,只不过可惜技术方面没有继续往下探究了。因为 Web 体验不好,无法无缝地嵌入到地图中。
二、从 Web 触发原生通知
Google 介绍了一个很关键的能力,是让 Web 代码触发原生 Android 通知。
这件事很能说明 WebView 的定位:Web 管业务逻辑和页面,Android 管系统能力。
比如笔记应用里,用户在 Web 页面创建了一个提醒,最终通过 Android 原生通知弹出来。这跟"网页弹个 alert"完全是两回事——它走的是 Android 的通知权限、通知渠道、系统通知样式,是真正的系统级体验。
边界也很清楚:Web 发出意图,Native 执行系统能力。谁也别越界。
三、原生的文件能力
文件选择,尤其是图片选择,在 WebView 里也是个老大难问题。
Web 里一个 <input type="file"> 就搞定了,但放到 Android App 里,弹出来的选择器体验千差万别——有的 App 直接调起一个简陋的文件浏览器,有的甚至什么都弹不出来。
我之前用过一些 App,上传头像的时候弹出来的选择界面跟系统风格完全不搭,一眼就知道这是 WebView 里硬塞进去的。Google 在讲演里给出的方案是接入 Android 系统的 PhotoPicker,体验直接跟系统相册拉齐。
说白了,WebView 不是让 Web 脱离 Android 各玩各的,而是让 Web 借用 Android 已经打磨好的系统能力。头像、笔记图片、商品图片、评论配图——这些都是高频场景,处理得好不好,用户一眼就能感知到。
四、原生的下载体验
下载文件也是 WebView App 里绕不开的场景。
你肯定遇到过这种情况:在某个 App 里点了个下载链接,进度条一闪而过,然后——没了。
文件去哪了?不知道。下载成功了吗?不确定。去文件管理器翻半天,才在某个犄角旮旯找到。
Google 给出的做法是把下载交给系统的 DownloadManager。这样一来,用户在系统通知栏里就能看到下载进度,下载完了能在系统下载列表里找到,体验跟浏览器里下载东西是一样的。
思路跟前面一样:Web 负责说"我要下载这个",具体的下载体验交给 Android 来做。PDF、报表、图片、附件,这些场景太多了,如果每次都自己造轮子处理文件流,既费劲又容易出问题。
一切,都是为了用户体验!
五、拦截网络请求
还有一个更偏工程实战的点:拦截网络请求,做缓存或离线支持。
很多人对 WebView 的理解就是"必须联网才能用"。但你想想真实场景——地铁、电梯、弱网、海外——如果 WebView 一断网就白屏,用户根本不会给你第二次机会。
WebView 提供了拦截网络请求的能力,开发者可以在 Android 侧对部分资源做缓存,或者在断网时返回本地内容。做到这一步,Web App 才真正不是一个"远程网页窗口"。
当然,缓存这件事不是越多越好。你得想清楚哪些资源值得缓存、哪些接口必须实时请求、用户离线时该看到什么、恢复网络后怎么同步。这些问题没有标准答案,得根据具体业务来定。
这么看,我在 15 年左右做的工作还可以,当时对 Web App 做了版本管理,并且可以完全离线运行。
六、标签页导航
最后说一个容易被低估的问题:导航和标签页。
Web App 不是单页面那么简单。它可能会弹新窗口、跳外部链接、触发登录页、跳到第三方授权——如果这些行为没处理好,用户就会遇到各种糟心事:点了链接突然被甩到外部浏览器,按返回键不知道回到哪,登录完了页面回不来。
这些事,正常浏览器都帮你干了,但 WebView 默认只是个"显示网页的 View",你得自己配置 WebViewClient、WebChromeClient 来接管这些行为。
所以很多时候大家觉得 WebView 是个低成本方案,其实恰恰相反。loadUrl() 当然便宜,但要让它像一个正经 App 一样跑起来,需要处理的边边角角一点都不少。
WebView 的正确打开方式
看完整个讲演,我觉得它表达的核心意思其实很简单:WebView 不是原生的替代品,但也不是低质量方案。
它适合的场景很明确——你已经有成熟的 Web 体系、Web 团队和 Web 迭代流程,现在想把这些能力带到 Android 上。它不是让你随便写个网页然后套个壳就完事了。
所谓的"补齐原生体验",说白了就是:Web 和 Native 各有各的边界,谁也别假装能干对方的活。Web 擅长业务页面和快速迭代,Native 擅长系统能力——通知、文件、下载、缓存、导航、安全这些。而 WebView 就是把这两边串起来的那根线。
Android 开发者关注什么
对于 Android 开发者来说,我们可能过去一直有这样一个认知:只要一个功能是 Web 做的,Android 侧就只是“套壳”。
但现在越来越多产品会采用混合架构,Android 开发者需要负责的不只是写原生页面,还包括设计 Web 与 Native 的边界。
比如:
- 哪些能力应该留在 Web?
- 哪些能力必须放在 Native?
- JSBridge 如何设计才安全?
- 文件选择、下载、通知如何接入系统?
WebView的缓存策略如何设计?- 返回键和页面历史如何统一?
- WebView 生命周期如何管理?
这些问题如果处理得好,WebView App 就可能非常接近原生体验;如果处理不好,它就会变成用户熟悉的那种“套壳网页”。
不要妖魔化 WebView
这次讲演,它没有把 WebView 描述成原生开发的替代品,也没有把 WebView 当成低质量方案,而是将 WebView 视为将 Web 内容带入 Android 生态的能力容器。
在国内,WebView 的名声之所以不好,很大程度上是因为它被大量用在了"快速出活"的场景里——不是为了给用户更好的体验,而是为了尽快满足业务需求、完成 KPI。
结果就是,很多 App 里的 WebView 页面体验粗糙、加载缓慢、交互生硬,久而久之,用户和开发者都形成了"WebView 开发的 App 是次等品"的印象。
但这不是 WebView 的问题,而是使用方式的问题。
它适合内容变化快、业务迭代快、已有成熟 Web 体系的产品。
但它不适合所有场景,比如高度依赖复杂动画、重图形、低延迟交互、复杂后台任务、深度硬件能力的应用,纯原生或者其他跨平台方案仍然可能更合适。
一点想法
"WebView = 套壳"这个印象,可能还要持续很久。
但如果你看过这次讲演就会发现,WebView 本身不背这个锅。loadUrl() 一把梭当然会变成套壳,但如果你认真处理通知、文件选择、下载、缓存、离线、导航、JSBridge 和安全边界,它完全可以成为 Web App 进入 Android 生态的一条正经路径。
未来的 Android 应用,纯 Native 或纯 Web 的划分会越来越模糊。业务层用 Web 保持快速迭代,系统体验由 Native 补齐,二者通过 WebView 协作——这大概率会成为很多产品的常态。
Build native Android experiences for web apps using WebView
www.youtube.com/watch?v=4_S…