Android 4.4(API 级别 19)引入了基于 Chromium 的新版 WebView
。此项变更会升级 WebView
的性能并标准化对 HTML5、CSS3 和 JavaScript 的支持,以与最新的网络浏览器保持一致。在 Android 4.4 及更高版本的设备上运行时,使用 WebView
的所有应用都将继承这些升级。
本文档介绍了关于 WebView
的其他变更,如果您将 targetSdkVersion
设置为“19”或更高版本,则应注意这些变更。
注意:如果您的 targetSdkVersion
设置为“18”或更低版本,则 WebView
将在“怪异模式”下尽可能紧密地运行,以避免上述某些行为变更,同时仍为应用提供性能和网络标准升级。不过要注意这一点,Android 4.4 根本不支持单列和窄列布局以及默认缩放级别,可能会存在未发现的其他行为差异,因此请务必在 Android 4.4 或更高版本上测试您的应用,即使 targetSdkVersion
设置为“18”或更低版本也是如此。
为了帮助您解决将应用迁移到 Android 4.4 中的 WebView
时可能遇到的任何问题,您可以调用 setWebContentsDebuggingEnabled()
以通过桌面设备上的 Chrome 启用远程调试。借助 WebView
中的这项新功能,您可以在 WebView
中运行时检查和分析网页内容、脚本和网络活动。如需了解详情,请参阅 Android 上的远程调试。
用户代理更改
如果您根据用户代理向 WebView
提供内容,则应注意用户代理字符串可能稍有变化,现在包含 Chrome 版本:
Mozilla/5.0 (Linux; Android 4.4; Nexus 4 Build/KRT16H) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36
如果您需要检索用户代理,但不需要为应用存储它或不想实例化 WebView
,则应使用静态方法 getDefaultUserAgent()
。不过,如果您打算替换 WebView
中的用户代理字符串,则可以改用 getUserAgentString()
。
多线程和线程拦截
如果您从应用的界面线程之外的任何线程针对 WebView
调用方法,可能会导致意外结果。例如,如果您的应用使用多个线程,则可以使用 runOnUiThread()
方法确保代码在界面线程中执行:
Kotlin
runOnUiThread { // Code for WebView goes here }
Java
runOnUiThread(new Runnable() { @Override public void run() { // Code for WebView goes here } });
另外,请确保您一定不会拦截界面线程。某些应用在等待 JavaScript 回调时会出现此错误。例如,不要使用如下代码:
Kotlin
// This code is BAD and will block the UI thread webView.loadUrl("javascript:fn()") while (result == null) { Thread.sleep(100) }
Java
// This code is BAD and will block the UI thread webView.loadUrl("javascript:fn()"); while(result == null) { Thread.sleep(100); }
您可以改用新方法 evaluateJavascript()
来异步运行 JavaScript。
自定义网址处理
新的 WebView
会在请求资源和解析使用自定义网址架构的链接时应用其他限制。例如,如果您实现 shouldOverrideUrlLoading()
或 shouldInterceptRequest()
之类的回调,那么 WebView
仅针对有效网址调用它们。
如果您使用的是自定义网址架构或基准网址,并注意到您的应用接收的对这些回调的调用较少或无法在 Android 4.4 上加载资源,请确保请求按照 RFC 3986 的规定指定有效的网址。
例如,新的 WebView
可能不会针对如下链接调用您的 shouldOverrideUrlLoading()
方法:
<a href="showProfile">Show Profile</a>
用户点击此类链接后出现的结果可能会有所不同:
- 如果您通过调用
loadData()
或者包含无效或为 null 的基本网址的loadDataWithBaseURL()
来加载网页,则不会在该页面上收到此类链接的shouldOverrideUrlLoading()
回调。注意:当您使用
loadDataWithBaseURL()
且基准网址无效或设为 null 时,您加载的内容中的所有链接都必须为绝对链接。 - 如果您通过调用
loadUrl()
加载页面或使用loadDataWithBaseURL()
提供有效基准网址,则会收到页面上的此类链接的shouldOverrideUrlLoading()
回调,但您收到的网址将是绝对网址(相对于当前页面)。例如,您收到的网址将为"http://www.example.com/showProfile"
(而不只是"showProfile"
)。
您可以使用如下所示的自定义架构,而不是如上所述在链接中使用简单字符串:
<a href="example-app:showProfile">Show Profile</a>
然后,您可以在 shouldOverrideUrlLoading()
方法中处理此网址,如下所示:
Kotlin
// The URL scheme should be non-hierarchical (no trailing slashes) const val APP_SCHEME = "example-app:" override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean { return if (url?.startsWith(APP_SCHEME) == true) { urlData = URLDecoder.decode(url.substring(APP_SCHEME.length), "UTF-8") respondToData(urlData) true } else { false } }
Java
// The URL scheme should be non-hierarchical (no trailing slashes) private static final String APP_SCHEME = "example-app:"; @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (url.startsWith(APP_SCHEME)) { urlData = URLDecoder.decode(url.substring(APP_SCHEME.length()), "UTF-8"); respondToData(urlData); return true; } return false; }
如果您无法更改 HTML,则可以使用 loadDataWithBaseURL()
并设置由自定义架构和有效主机组成的基准网址,例如 "example-app://<valid_host_name>/"
。例如:
Kotlin
webView.loadDataWithBaseURL("example-app://example.co.uk/", HTML_DATA, null, "UTF-8", null)
Java
webView.loadDataWithBaseURL("example-app://example.co.uk/", HTML_DATA, null, "UTF-8", null);
有效主机名称应符合 RFC 3986 并且必须在末尾包含尾部斜杠,否则系统可能会丢弃来自已加载网页的所有请求。
视口更改
视口 target-densitydpi 不再受支持
以前,WebView
支持名为 target-densitydpi
的视口属性来帮助网页指定其预期的屏幕密度。此属性已不再受支持,您应迁移到使用标准解决方案来处理图片和 CSS,如WebView 中的精美像素风格界面中所述。
在视口较小时放大视口
以前,如果将视口宽度的值设置为小于或等于“320”,则它会设置为“device-width”;如果将视口高度的值设置为小于或等于 WebView
高度,则它会设置为“device-height”。不过,在新的 WebView
中运行时,系统会使用宽度值或高度值,并放大 WebView
以填充屏幕宽度。
不支持多视口标记
以前,如果您在网页中添加了多个视口标记,则 WebView
会合并所有标记中的属性。在新的 WebView
中,系统仅使用最后一个视口并忽略所有其他视口。
默认缩放级别已弃用
用于在页面上获取并设置初始缩放级别的 getDefaultZoom()
和 setDefaultZoom()
方法不再受支持,您应改为在网页中定义相应的视口。
注意:Android 4.4 及更高版本根本不支持这些 API。即使您的 targetSdkVersion
设置为“18”或更低版本,这些 API 也不会造成任何影响。
如需了解如何在 HTML 中定义视口属性,请参阅 WebView 中的精美像素风格界面。
如果无法在 HTML 中设置视口的宽度,则应调用 setUseWideViewPort()
以确保页面具有较大的视口。例如:
Kotlin
webView.settings.apply { useWideViewPort = true loadWithOverviewMode = true }
Java
WebSettings settings = webView.getSettings(); settings.setUseWideViewPort(true); settings.setLoadWithOverviewMode(true);
样式更改
后台 CSS 简写形式会替换 background-size
Chrome 浏览器和其他浏览器采用这种方法已经有一段时间了,但现在如果您也指定了 background
样式,则 WebView
也会替换 background-size
的 CSS 设置。例如,此处的尺寸会重置为默认值:
.some-class { background-size: contain; background: url('images/image.png') no-repeat; }
解决方法是直接在两个属性之间来回切换。
.some-class { background: url('images/image.png') no-repeat; background-size: contain; }
尺寸采用 CSS 像素来代替屏幕像素
以前,尺寸参数(如 window.outerWidth
和 window.outerHeight
)会以实际屏幕像素返回值。在新的 WebView
中,这些参数会根据 CSS 像素返回值。
尝试针对尺寸调整元素或其他计算使用并计算物理尺寸(以像素为单位)的做法通常不妥。不过,如果您停用了缩放功能并将初始比例设置为 1.0,则可以使用 window.devicePixelRatio
获取比例,然后用 CSS 像素值乘以该值。您也可以改为创建 JavaScript 绑定,从 WebView
本身查询像素尺寸。
如需了解详情,请参阅 quirksmode.org。
NARROW_COLUMNS 和 SINGLE_COLUMN 不再受支持
新的 WebView
不支持 WebSettings.LayoutAlgorithm
的 NARROW_COLUMNS
值。
注意:Android 4.4 及更高版本根本不支持这些 API。即使您的 targetSdkVersion
设置为“18”或更低版本,这些 API 也不会造成任何影响。
您可以通过以下方式处理此更改:
- 更改应用的样式:
如果您可以控制页面上的 HTML 和 CSS,则可能会发现更改内容设计可能是最可靠的方法。例如,在您引用许可的屏幕上,您可能需要在
<pre>
标记内封装文本,可以使用以下样式执行此操作:<pre style="word-wrap: break-word; white-space: pre-wrap;">
如果您尚未为页面定义视口属性,这可能会非常有帮助。
- 使用新的
TEXT_AUTOSIZING
布局算法:如果您使用窄列来提高移动设备上的各种桌面版网站的可读性,并且无法更改 HTML 内容,则新的
TEXT_AUTOSIZING
算法可能是NARROW_COLUMNS
的合适替代方法。
此外,新的 WebView
也不支持之前已弃用的 SINGLE_COLUMN
值。
在 JavaScript 中处理触摸事件
如果您的网页直接在 WebView
中处理触摸事件,请确保您还一并处理 touchcancel
事件。在以下情况下,系统将调用 touchcancel
,如果未收到调用,可能会导致出现问题:
- 用户触摸某个元素(系统因此调用
touchstart
和touchmove
)并滚动页面,从而导致系统抛出touchcancel
。 - 用户触摸某个元素(系统调用
touchstart
),但系统未调用event.preventDefault()
,导致touchcancel
提前抛出(因此WebView
假定您不想使用触摸事件)。