管理 WebView 对象

Android 提供了多种 API 来帮助您管理 WebView 对象,这些对象可在您的应用中显示 Web 内容。

本页介绍了如何使用这些 API 来处理 WebView 对象,从而提升应用的稳定性和安全性。

Version API

从 Android 7.0(API 级别 24)开始,用户可以在多个 用于在 WebView 对象中显示 Web 内容的不同软件包。 AndroidX.webkit 库包含 getCurrentWebViewPackage() 用于获取与显示 Web 的软件包相关的信息的方法 。此方法在分析仅发生的错误时非常有用 如果您的应用尝试使用特定软件包的 WebView 的实现。

要使用此方法,请添加以下代码段中显示的逻辑:

Kotlin

val webViewPackageInfo = WebViewCompat.getCurrentWebViewPackage(appContext)
Log.d("MY_APP_TAG", "WebView version: ${webViewPackageInfo.versionName}")

Java

PackageInfo webViewPackageInfo = WebViewCompat.getCurrentWebViewPackage(appContext);
Log.d("MY_APP_TAG", "WebView version: " + webViewPackageInfo.versionName);
<ph type="x-smartling-placeholder">

Google 安全浏览服务

为了给您的用户提供更安全的浏览体验,WebView 对象使用 Google 安全浏览、 这样一来,当用户尝试转到 可能不安全的网站

虽然 EnableSafeBrowsing 的默认值为 true, 您可能只想有条件地启用安全浏览功能 禁用它。Android 8.0(API 级别 26)及更高版本支持使用 setSafeBrowsingEnabled() 针对个别 WebView 对象开启/关闭安全浏览功能。

如果您希望所有 WebView 个对象都停用安全浏览功能 请将以下 <meta-data> 元素添加到应用的 清单文件:

<ph type="x-smartling-placeholder">
<manifest>
    <application>
        <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
                   android:value="false" />
        ...
    </application>
</manifest>

定义程序化操作

WebView 的实例尝试加载 被 Google 归类为已知威胁,默认为 WebView 会显示一个插页,警告用户存在已知威胁。通过该屏幕 用户仍然可以选择加载网址或返回到 安全。

如果您以 Android 8.1(API 级别 27)或更高版本为目标平台,则可以定义 应用如何以程序化方式响应以下已知威胁 方式:

  • 您可以控制应用是否向“安全”应用报告已知威胁 浏览。
  • 您可以让应用自动执行特定操作,例如 就像返回到安全网页一样:每次遇到包含 被归类为已知威胁
。 <ph type="x-smartling-placeholder">

以下代码段展示了如何指示应用的 在遇到已知警报后,WebView一律返回安全状态 威胁:

MyWebActivity.java

Kotlin

private lateinit var superSafeWebView: WebView
private var safeBrowsingIsInitialized: Boolean = false

// ...

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    superSafeWebView = WebView(this)
    superSafeWebView.webViewClient = MyWebViewClient()
    safeBrowsingIsInitialized = false

    if (WebViewFeature.isFeatureSupported(WebViewFeature.START_SAFE_BROWSING)) {
        WebViewCompat.startSafeBrowsing(this, ValueCallback<Boolean> { success ->
            safeBrowsingIsInitialized = true
            if (!success) {
                Log.e("MY_APP_TAG", "Unable to initialize Safe Browsing!")
            }
        })
    }
}

Java

private WebView superSafeWebView;
private boolean safeBrowsingIsInitialized;

// ...

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    superSafeWebView = new WebView(this);
    superSafeWebView.setWebViewClient(new MyWebViewClient());
    safeBrowsingIsInitialized = false;

    if (WebViewFeature.isFeatureSupported(WebViewFeature.START_SAFE_BROWSING)) {
        WebViewCompat.startSafeBrowsing(this, new ValueCallback<Boolean>() {
            @Override
            public void onReceiveValue(Boolean success) {
                safeBrowsingIsInitialized = true;
                if (!success) {
                    Log.e("MY_APP_TAG", "Unable to initialize Safe Browsing!");
                }
            }
        });
    }
}

MyWebViewClient.java

Kotlin

class MyWebViewClient : WebViewClientCompat() {
    // Automatically go "back to safety" when attempting to load a website that
    // Google identifies as a known threat. An instance of WebView calls this
    // method only after Safe Browsing is initialized, so there's no conditional
    // logic needed here.
    override fun onSafeBrowsingHit(
            view: WebView,
            request: WebResourceRequest,
            threatType: Int,
            callback: SafeBrowsingResponseCompat
    ) {
        // The "true" argument indicates that your app reports incidents like
        // this one to Safe Browsing.
        if (WebViewFeature.isFeatureSupported(WebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY)) {
            callback.backToSafety(true)
            Toast.makeText(view.context, "Unsafe web page blocked.", Toast.LENGTH_LONG).show()
        }
    }
}

Java

public class MyWebViewClient extends WebViewClientCompat {
    // Automatically go "back to safety" when attempting to load a website that
    // Google identifies as a known threat. An instance of WebView calls this
    // method only after Safe Browsing is initialized, so there's no conditional
    // logic needed here.
    @Override
    public void onSafeBrowsingHit(WebView view, WebResourceRequest request,
            int threatType, SafeBrowsingResponseCompat callback) {
        // The "true" argument indicates that your app reports incidents like
        // this one to Safe Browsing.
        if (WebViewFeature.isFeatureSupported(WebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY)) {
            callback.backToSafety(true);
            Toast.makeText(view.getContext(), "Unsafe web page blocked.",
                    Toast.LENGTH_LONG).show();
        }
    }
}

HTML5 Geolocation API

对于以 Android 6.0(API 级别 23)及更高版本为目标平台的应用,Geolocation API 仅受 HTTPS 等安全源支持。任何向 非安全起点上的 Geolocation API 会被自动拒绝,而不会调用 相应的 onGeolocationPermissionsShowPrompt() 方法。

停用指标收集

WebView可将匿名诊断数据上传到 Google。系统会按应用收集数据 每个实例化 WebView 的应用。您可以选择停用此功能 方法是在清单的 <application> 元素:

<manifest>
    <application>
    ...
    <meta-data android:name="android.webkit.WebView.MetricsOptOut"
               android:value="true" />
    </application>
</manifest>

只有在用户同意并且 您的应用不会选择停用详细了解如何选择停用诊断数据 请参阅 WebView 中的用户隐私 报告

Termination Handling

Teriation Handling API 可处理针对 WebView 对象都会消失 或由于渲染器进程崩溃而造成崩溃。通过使用此 API 让您的应用继续执行,即使渲染器进程消失也是如此。

<ph type="x-smartling-placeholder">

如果在加载特定网页时渲染程序崩溃了, 再次加载同一页面可能会导致新的 WebView 对象 呈现相同的崩溃行为。

以下代码段说明了如何在 Activity:

Kotlin

    
inner class MyRendererTrackingWebViewClient : WebViewClient() {
    private var mWebView: WebView? = null

    override fun onRenderProcessGone(view: WebView, detail: RenderProcessGoneDetail): Boolean {
        if (!detail.didCrash()) {
            // Renderer is killed because the system ran out of memory. The app
            // can recover gracefully by creating a new WebView instance in the
            // foreground.
            Log.e("MY_APP_TAG", ("System killed the WebView rendering process " +
                "to reclaim memory. Recreating..."))

            mWebView?.also { webView ->
                val webViewContainer: ViewGroup = findViewById(R.id.my_web_view_container)
                webViewContainer.removeView(webView)
                webView.destroy()
                mWebView = null
            }

            // By this point, the instance variable "mWebView" is guaranteed to
            // be null, so it's safe to reinitialize it.

            return true // The app continues executing.
        }

        // Renderer crashes because of an internal error, such as a memory
        // access violation.
        Log.e("MY_APP_TAG", "The WebView rendering process crashed!")

        // In this example, the app itself crashes after detecting that the
        // renderer crashed. If you handle the crash more gracefully and let
        // your app continue executing, you must destroy the current WebView
        // instance, specify logic for how the app continues executing, and
        // return "true" instead.
        return false
    }
}

Java

public class MyRendererTrackingWebViewClient extends WebViewClient {
    private WebView mWebView;

    @Override
    public boolean onRenderProcessGone(WebView view,
            RenderProcessGoneDetail detail) {
        if (!detail.didCrash()) {
            // Renderer is killed because the system ran out of memory. The app
            // can recover gracefully by creating a new WebView instance in the
            // foreground.
            Log.e("MY_APP_TAG", "System killed the WebView rendering process " +
                    "to reclaim memory. Recreating...");

            if (mWebView != null) {
                ViewGroup webViewContainer =
                        (ViewGroup) findViewById(R.id.my_web_view_container);
                webViewContainer.removeView(mWebView);
                mWebView.destroy();
                mWebView = null;
            }

            // By this point, the instance variable "mWebView" is guaranteed to
            // be null, so it's safe to reinitialize it.

            return true; // The app continues executing.
        }

        // Renderer crashes because of an internal error, such as a memory
        // access violation.
        Log.e("MY_APP_TAG", "The WebView rendering process crashed!");

        // In this example, the app itself crashes after detecting that the
        // renderer crashed. If you handle the crash more gracefully and let
        // your app continue executing, you must destroy the current WebView
        // instance, specify logic for how the app continues executing, and
        // return "true" instead.
        return false;
    }
}

Renderer Importance API

WebView 对象存在时 营业场所 多进程模式,因此您可以灵活地控制应用处理 以及内存不足的情况您可以使用 Renderer Importance API Android 8.0 中,为分配给特定 WebView 对象。特别要指出的是,您可能希望 当呈现器显示应用的 已终止 WebView 个对象。举例来说,如果您 预计不会长时间显示 WebView 对象, 系统可以回收渲染器正在使用的内存。

以下代码段展示了如何为渲染程序分配优先级 与应用的 WebView 对象关联的进程:

Kotlin

val myWebView: WebView = ...
myWebView.setRendererPriorityPolicy(RENDERER_PRIORITY_BOUND, true)

Java

WebView myWebView;
myWebView.setRendererPriorityPolicy(RENDERER_PRIORITY_BOUND, true);

在此代码段中,渲染程序的优先级为 绑定 — 应用的默认优先级。true 将渲染程序的优先级降低至 RENDERER_PRIORITY_WAIVED 当关联的 WebView 对象不再可见时。在其他 true 参数表示您的应用并不在意, 系统会使渲染器进程保持活动状态事实上,这个较低的优先级 可能会导致渲染器进程在内存不足时终止 情况。

<ph type="x-smartling-placeholder">

如需详细了解系统如何处理内存不足的情况,请参阅 进程和应用 生命周期