Управление объектами WebView

Android предоставляет несколько API, которые помогут вам управлять объектами WebView , отображающими веб-контент в вашем приложении.

На этой странице описано, как использовать эти API для более эффективной работы с объектами WebView , повышая стабильность и безопасность вашего приложения.

Версия API

Начиная с Android 7.0 (уровень API 24), пользователи могут выбирать между несколькими различными пакетами для отображения веб-контента в объекте WebView . Библиотека AndroidX.webkit включает метод getCurrentWebViewPackage() для получения информации, относящейся к пакету, отображающему веб-контент в вашем приложении. Этот метод полезен при анализе ошибок, которые возникают только тогда, когда ваше приложение пытается отобразить веб-контент с помощью реализации WebView конкретного пакета.

Чтобы использовать этот метод, добавьте логику, показанную в следующем фрагменте кода:

Котлин

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

Ява

PackageInfo webViewPackageInfo = WebViewCompat.getCurrentWebViewPackage(appContext);
Log.d("MY_APP_TAG", "WebView version: " + webViewPackageInfo.versionName);

Служба безопасного просмотра Google

Чтобы обеспечить вашим пользователям более безопасный просмотр, объекты WebView проверяют URL-адреса с помощью безопасного просмотра Google , что позволяет вашему приложению показывать пользователям предупреждение, когда они пытаются перейти на потенциально небезопасный веб-сайт.

Хотя значение EnableSafeBrowsing по умолчанию равно true, в некоторых случаях вам может потребоваться включить безопасный просмотр только условно или отключить его. Android 8.0 (уровень API 26) и более поздних версий поддерживает использование setSafeBrowsingEnabled() для переключения безопасного просмотра для отдельного объекта WebView .

Если вы хотите, чтобы все объекты WebView отказались от проверок безопасного просмотра, добавьте следующий элемент <meta-data> в файл манифеста вашего приложения:

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

Определить программные действия

Когда экземпляр WebView пытается загрузить страницу, которая классифицируется Google как известная угроза, WebView по умолчанию показывает межстраничное объявление, предупреждающее пользователей об известной угрозе. Этот экран дает пользователям возможность в любом случае загрузить URL-адрес или безопасно вернуться на предыдущую страницу.

Если вы ориентируетесь на Android 8.1 (уровень API 27) или более поздней версии, вы можете программно определить, как ваше приложение реагирует на известную угрозу, следующими способами:

  • Вы можете контролировать, будет ли ваше приложение сообщать об известных угрозах безопасному просмотру.
  • Вы можете заставить свое приложение автоматически выполнять определенное действие (например, возвращаться в безопасное место) каждый раз, когда оно сталкивается с URL-адресом, классифицированным как известная угроза.

В следующих фрагментах кода показано, как дать указание экземплярам WebView вашего приложения всегда возвращаться в безопасное состояние после обнаружения известной угрозы:

MyWebActivity.java

Котлин

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!")
            }
        })
    }
}

Ява

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

Котлин

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()
        }
    }
}

Ява

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 API геолокации

Для приложений, предназначенных для Android 6.0 (уровень API 23) и более поздних версий, API геолокации поддерживается только в безопасных источниках, таких как HTTPS. Любой запрос к API геолокации в незащищенных источниках автоматически отклоняется без вызова соответствующего метода onGeolocationPermissionsShowPrompt() .

Отказаться от сбора показателей

WebView имеет возможность загружать анонимные диагностические данные в Google, когда пользователь дает свое согласие. Данные собираются отдельно для каждого приложения, которое создает экземпляр WebView . Вы можете отказаться от этой функции, создав следующий тег в элементе <application> манифеста:

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

Данные загружаются из приложения только в том случае, если пользователь дает согласие и приложение не отказывается от этого. Дополнительную информацию об отказе от отправки диагностических данных см. в разделе Конфиденциальность пользователей в отчетах WebView .

API обработки завершения

API обработки завершения обрабатывает случаи, когда процесс отрисовки для объекта WebView прекращается либо потому, что система завершает средство отрисовки, чтобы освободить необходимую память, либо из-за сбоя процесса отрисовки. Используя этот API, вы позволяете своему приложению продолжать работу, даже если процесс рендеринга завершается.

Если в средстве отрисовки происходит сбой при загрузке определенной веб-страницы, попытка загрузить эту же страницу еще раз может привести к тому, что новый объект WebView проявит такое же поведение при сбое отрисовки.

Следующий фрагмент кода иллюстрирует, как использовать этот API в Activity :

Котлин

    
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
    }
}

Ява

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;
    }
}

API важности рендерера

Когда объекты WebView работают в многопроцессном режиме , у вас есть некоторая гибкость в том, как ваше приложение обрабатывает ситуации нехватки памяти. Вы можете использовать API важности рендерера, представленный в Android 8.0, чтобы установить политику приоритетов для средства рендеринга, назначенного конкретному объекту WebView . В частности, вы можете захотеть, чтобы основная часть вашего приложения продолжала выполняться, когда средство визуализации, отображающее объекты WebView вашего приложения, завершается. Это можно сделать, например, если вы планируете не показывать объект WebView в течение длительного времени, чтобы система могла освободить память, которую использовал средство визуализации.

В следующем фрагменте кода показано, как назначить приоритет процессу рендеринга, связанному с объектами WebView вашего приложения:

Котлин

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

Ява

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

В этом конкретном фрагменте приоритет средства визуализации такой же, как (или связан с ним), приоритет по умолчанию для приложения. Аргумент true снижает приоритет средства визуализации до RENDERER_PRIORITY_WAIVED , когда связанный объект WebView больше не виден. Другими словами, true аргумент указывает на то, что ваше приложение не заботится о том, поддерживает ли система процесс рендеринга в рабочем состоянии. Фактически, этот более низкий уровень приоритета делает вероятным, что процесс рендеринга завершается в ситуациях нехватки памяти.

Дополнительные сведения о том, как система справляется с ситуациями нехватки памяти, см. в разделе Процессы и жизненный цикл приложения .