WebView-Objekte verwalten

Android bietet mehrere APIs, mit denen Sie die WebView-Objekte verwalten können, die Webinhalte in Ihrer App anzeigen.

Auf dieser Seite wird beschrieben, wie Sie diese APIs verwenden können, um effektiver mit WebView-Objekten zu arbeiten und so die Stabilität und Sicherheit Ihrer App zu verbessern.

Version API

Ab Android 7.0 (API-Level 24) können Nutzer zwischen mehreren verschiedenen Paketen auswählen, um Webinhalte in einem WebView-Objekt anzuzeigen. Die Jetpack Webkit Bibliothek enthält die getCurrentWebViewPackage() Methode zum Abrufen von Informationen zum Paket, das Webinhalte in Ihrer App anzeigt. Diese Methode ist nützlich, wenn Sie Fehler analysieren, die nur auftreten, wenn Ihre App versucht, Webinhalte mit einer bestimmten Paketimplementierung von WebView anzuzeigen.

Wenn Sie diese Methode verwenden möchten, fügen Sie die im folgenden Code-Snippet gezeigte Logik hinzu:

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

Google Safe Browsing-Dienst

Um Ihren Nutzern ein sichereres Surferlebnis zu bieten, werden URLs in WebView Objekten mit Google Safe Browsingüberprüft. So kann Ihre App Nutzern eine Warnung anzeigen, wenn sie versuchen, eine potenziell unsichere Website aufzurufen.

Der Standardwert von EnableSafeBrowsing ist zwar „true“, es gibt aber Fälle, in denen Sie Safe Browsing nur bedingt aktivieren oder deaktivieren möchten. Unter Android 8.0 (API-Level 26) und höher kann setSafeBrowsingEnabled() verwendet werden, um Safe Browsing für ein einzelnes WebView Objekt zu aktivieren oder zu deaktivieren.

Wenn alle WebView-Objekte von Safe Browsing -Prüfungen ausgeschlossen werden sollen, fügen Sie der Manifestdatei Ihrer App das folgende <meta-data>-Element hinzu:

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

Programmatische Aktionen definieren

Wenn eine Instanz von WebView versucht, eine Seite zu laden, die von Google als bekannte Bedrohung eingestuft wird, wird in WebView standardmäßig eine Interstitial-Anzeige eingeblendet, in der Nutzer vor der bekannten Bedrohung gewarnt werden. Auf diesem Bildschirm können Nutzer die URL trotzdem laden oder zu einer vorherigen sicheren Seite zurückkehren.

Wenn Sie Android 8.1 (API-Level 27) oder höher verwenden, können Sie programmatisch definieren, wie Ihre App auf eine bekannte Bedrohung reagiert. Dazu haben Sie folgende Möglichkeiten:

  • Sie können festlegen, ob Ihre App bekannte Bedrohungen an Safe Browsing meldet.
  • Sie können festlegen, dass Ihre App automatisch eine bestimmte Aktion ausführt, z. B. zur Sicherheit zurückkehren, wenn sie auf eine URL stößt, die als bekannte Bedrohung eingestuft ist.

Die folgenden Code-Snippets zeigen, wie Sie die WebView-Instanzen Ihrer App anweisen, nach dem Auftreten einer bekannten Bedrohung immer zur Sicherheit zurückzukehren:

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

Bei Apps, die auf Android 6.0 (API-Level 23) und höher ausgerichtet sind, wird die Geolocation API nur für sichere Ursprünge wie HTTPS unterstützt. Alle Anfragen an die Geolocation API für nicht sichere Ursprünge werden automatisch abgelehnt, ohne dass die entsprechende Methode onGeolocationPermissionsShowPrompt() aufgerufen wird.

Erfassung von Messwerten deaktivieren

WebView kann anonyme Diagnosedaten an Google hochladen, wenn der Nutzer seine Einwilligung gibt. Die Daten werden für jede App, die eine WebView-Instanz erstellt, app-spezifisch erfasst. Sie können diese Funktion deaktivieren, indem Sie im Manifest das folgende Tag im <application> Element erstellen:

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

Daten werden nur dann von einer App hochgeladen, wenn der Nutzer einwilligt und die App die Funktion nicht deaktiviert. Weitere Informationen zum Deaktivieren der Berichterstellung von Diagnosedaten finden Sie unter Datenschutz der Nutzer in der WebView Berichterstellung.

Termination Handling API

Die Termination Handling API verarbeitet Fälle, in denen der Renderer-Prozess für ein WebView-Objekt beendet wird, entweder weil das System den Renderer beendet, um benötigten Arbeitsspeicher freizugeben, oder weil der Renderer-Prozess abstürzt. Mit dieser API kann Ihre App weiter ausgeführt werden, auch wenn der Renderer-Prozess beendet wird.

Wenn ein Renderer beim Laden einer bestimmten Webseite abstürzt, kann der Versuch, dieselbe Seite noch einmal zu laden, dazu führen, dass ein neues WebView-Objekt dasselbe Rendering-Absturzverhalten zeigt.

Das folgende Code-Snippet zeigt, wie Sie diese API in einer Activity verwenden:

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

Wenn WebView Objekte im Mehrprozessmodus ausgeführt werden, haben Sie einige Flexibilität bei der Verarbeitung von Situationen, in denen der Arbeitsspeicher nicht ausreicht. Mit der Renderer Importance API, die in Android 8.0 eingeführt wurde, können Sie eine Prioritätsrichtlinie für den Renderer festlegen, der einem bestimmten WebView-Objekt zugewiesen ist. Insbesondere möchten Sie möglicherweise, dass der Hauptteil Ihrer App weiter ausgeführt wird, wenn ein Renderer beendet wird, der die WebView-Objekte Ihrer App anzeigt. Das kann beispielsweise sinnvoll sein, wenn Sie erwarten, dass das WebView-Objekt längere Zeit nicht angezeigt wird, damit das System Arbeitsspeicher freigeben kann, der vom Renderer verwendet wurde.

Das folgende Code-Snippet zeigt, wie Sie dem Renderer-Prozess, der mit den WebView-Objekten Ihrer App verknüpft ist, eine Priorität zuweisen:

Kotlin

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

Java

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

In diesem Snippet ist die Priorität des Renderers dieselbe wie die Standardpriorität für die App oder an diese gebunden. Das Argument true verringert die Priorität des Renderers auf RENDERER_PRIORITY_WAIVED, wenn das zugehörige WebView-Objekt nicht mehr sichtbar ist. Mit anderen Worten: Ein true-Argument gibt an, dass es Ihrer App egal ist, ob das System den Renderer-Prozess aktiv hält. Tatsächlich ist es bei dieser niedrigeren Priorität wahrscheinlich, dass der Renderer-Prozess in Situationen, in denen der Arbeitsspeicher nicht ausreicht, beendet wird.

Weitere Informationen zum Diagnostizieren und Optimieren des Speicherbedarfs Ihrer App bei der Verwendung von Webinhalten finden Sie unter Arbeitsspeicher von WebView verwalten und diagnostizieren. Weitere Informationen dazu, wie das System Situationen mit wenig Arbeitsspeicher prozessübergreifend verarbeitet, finden Sie unter Prozesse und App Lebenszyklus.

Status und Navigationsverlauf beibehalten

Wenn das System Hintergrundressourcen freigibt oder sich die Konfiguration des Geräts ändert, werden die Aktivität einer App und ihre WebView-Objekte möglicherweise zerstört. Um den Navigationskontext des Nutzers beizubehalten, verwenden Sie die saveState(Bundle) Methode in onSaveInstanceState(), um den Navigationsverlauf in ein Bundle zu serialisieren, und stellen Sie ihn dann mit restoreState(Bundle) wieder her.

Da Android für savedInstanceState ein strenges Limit von 1 MB für den gesamten Prozess erzwingt, kann die Serialisierung großer Browsersitzungen mit der Standard-Framework-API zu einer TransactionTooLargeException führen. Wenn Sie Größenlimits erzwingen oder Einträge im Vorwärtsverlauf sicher löschen möchten, verwenden Sie die Jetpack Webkit-Methode WebViewCompat.saveState().

Ausführliche Strategien und Best Practices für die effiziente Verwaltung des Status finden Sie unter WebView-Status effizient verwalten.