Zarządzaj obiektami WebView

Android udostępnia kilka interfejsów API, które ułatwiają zarządzanie obiektami WebView, które wyświetlają treści z internetu w aplikacji.

Na tej stronie opisujemy, jak używać tych interfejsów API do efektywniejszej pracy z obiektami WebView, co pozwoli zwiększyć stabilność i bezpieczeństwo aplikacji.

Interfejs API wersji

Od Androida 7.0 (poziom interfejsu API 24) użytkownicy mają do wyboru kilka różnych pakietów do wyświetlania treści z internetu w obiekcie WebView. Biblioteka AndroidX.webkit zawiera metodę getCurrentWebViewPackage() do pobierania informacji związanych z pakietem, który wyświetla zawartość internetową Twojej aplikacji. Ta metoda jest przydatna podczas analizy błędów występujących tylko wtedy, gdy aplikacja próbuje wyświetlić treści internetowe za pomocą implementacji WebView w danym pakiecie.

Aby skorzystać z tej metody, dodaj funkcję logiczną przedstawioną w tym fragmencie kodu:

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

Usługa Bezpieczne przeglądanie Google

Aby zapewnić użytkownikom większe bezpieczeństwo podczas przeglądania internetu, WebView umożliwia weryfikację adresów URL za pomocą Bezpiecznego przeglądania Google. Dzięki temu aplikacja wyświetla użytkownikom ostrzeżenie, gdy próbują wejść na potencjalnie niebezpieczną stronę.

Chociaż domyślna wartość EnableSafeBrowsing to „prawda”, w niektórych przypadkach możesz chcieć włączyć Bezpieczne przeglądanie tylko warunkowo lub tylko je wyłączyć. Android 8.0 (poziom interfejsu API 26) i nowsze obsługują użycie setSafeBrowsingEnabled() do przełączania Bezpiecznego przeglądania w przypadku pojedynczego obiektu WebView.

Jeśli chcesz, żeby wszystkie obiekty WebView nie były sprawdzane przez Bezpieczne przeglądanie, dodaj do pliku manifestu aplikacji ten element <meta-data>:

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

Definiowanie działań związanych z automatyzacją

Gdy wystąpienie elementu WebView próbuje wczytać stronę sklasyfikowaną przez Google jako znane zagrożenie, WebView domyślnie wyświetla reklamę pełnoekranową z ostrzeżeniem o znanym zagrożeniu. Umożliwia on użytkownikom wczytanie adresu URL mimo to lub powrót do bezpiecznej strony.

Jeśli kierujesz aplikację na Androida 8.1 (poziom interfejsu API 27) lub nowszego, możesz automatycznie określić, jak aplikacja ma reagować na znane zagrożenie w ten sposób:

  • Możesz określić, czy Twoja aplikacja zgłasza znane zagrożenia do Bezpiecznego przeglądania.
  • Możesz ustawić automatyczne wykonywanie przez aplikację określonego działania – na przykład powrót do zabezpieczeń – za każdym razem, gdy napotka adres URL sklasyfikowany jako znany zagrożenie.

Z poniższych fragmentów kodu dowiesz się, jak nakazać wystąpieniam w aplikacji funkcję WebView zawsze wracaniu do stanu zabezpieczeń po napotkaniu znanego zagrożenia:

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

Interfejs API geolokalizacji HTML5

W przypadku aplikacji kierowanych na Androida 6.0 (poziom interfejsu API 23) i nowsze wersje interfejs API geolokalizacji jest obsługiwany tylko w przypadku bezpiecznych źródeł, takich jak HTTPS. Wszystkie żądania wysyłane do interfejsu Geolocation API z niezabezpieczonych źródeł są automatycznie odrzucane bez wywoływania odpowiedniej metody onGeolocationPermissionsShowPrompt().

Zrezygnuj z zbierania danych

WebView może przesyłać do Google anonimowe dane diagnostyczne, jeśli użytkownik wyrazi na to zgodę. W przypadku każdej aplikacji, która tworzy instancję WebView, zbierane są dane dotyczące poszczególnych aplikacji. Możesz zrezygnować z tej funkcji, tworząc w elemencie <application> pliku manifestu następujący tag:

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

Dane są przesyłane z aplikacji tylko wtedy, gdy użytkownik wyrazi zgodę, a aplikacja tego nie zrobi. Więcej informacji o rezygnowaniu z raportów danych diagnostycznych znajdziesz w artykule Prywatność użytkowników w raportach WebView.

Zakończenie obsługi interfejsu API

Interfejs End handling API obsługuje przypadki, w których proces renderowania obiektu WebView wyłącza się, ponieważ system zamyka mechanizm renderowania, by odzyskać niezbędną pamięć, lub w wyniku awarii tego procesu. Dzięki niemu aplikacja będzie mogła działać nadal, nawet jeśli proces renderowania zostanie wyłączony.

Jeśli mechanizm renderowania ulegnie awarii podczas wczytywania konkretnej strony internetowej, próba ponownego załadowania tej samej strony może spowodować, że nowy obiekt WebView będzie wykazywać ten sam błąd renderowania.

Ten fragment kodu pokazuje, jak używać tego interfejsu API w 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;
    }
}

Interfejs API mechanizmu renderowania

Gdy obiekty WebView działają w trybie wielu procesów, masz niewielką elastyczność w tym, jak aplikacja postępuje w przypadku braku pamięci. Za pomocą wprowadzonego w Androidzie 8.0 interfejsu Renderer Importance API możesz ustawić zasadę priorytetu dla mechanizmu renderowania przypisanego do konkretnego obiektu WebView. Być może główna część aplikacji ma być wykonywana po tym, jak mechanizm renderowania wyświetlający obiekty WebView aplikacji zostanie wyłączony. Możesz to zrobić na przykład wtedy, gdy spodziewasz się, że obiekt WebView nie będzie wyświetlany przez długi czas, aby system mógł odzyskać pamięć wykorzystywaną przez mechanizm renderowania.

Ten fragment kodu pokazuje, jak przypisać priorytet do procesu renderowania powiązanego z obiektami WebView w Twojej aplikacji:

Kotlin

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

Java

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

W tym fragmencie kodu priorytet mechanizmu renderowania jest taki sam jak domyślny lub powiązany z aplikacją. Argument true obniża priorytet mechanizmu renderowania do RENDERER_PRIORITY_WAIVED, gdy powiązany obiekt WebView nie jest już widoczny. Inaczej mówiąc, argument true wskazuje, że aplikacja nie dba o to, czy system utrzymuje aktywność mechanizmu renderowania. W przypadku braku pamięci proces renderowania może zakończyć się niepowodzeniem.

Więcej informacji o tym, jak system obsługuje sytuacje z małą ilością pamięci, znajdziesz w sekcji Procesy i cykl życia aplikacji.