Gérer les objets WebView

Android fournit plusieurs API pour vous aider à gérer les objets WebView qui affichent du contenu Web dans votre application.

Cette page explique comment utiliser ces API pour travailler plus efficacement avec les objets WebView, ce qui améliore la stabilité et la sécurité de votre application.

Version de l'API

À partir d'Android 7.0 (niveau d'API 24), les utilisateurs peuvent choisir parmi plusieurs packages différents pour afficher du contenu Web dans un objet WebView. La bibliothèque Jetpack Webkit inclut la getCurrentWebViewPackage() méthode pour récupérer des informations sur le package qui affiche du contenu Web dans votre application. Cette méthode est utile lors de l'analyse des erreurs qui se produisent uniquement lorsque votre application tente d'afficher du contenu Web à l'aide de l'implémentation WebView d'un package particulier.

Pour utiliser cette méthode, ajoutez la logique indiquée dans l'extrait de code suivant :

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

Service de navigation sécurisée Google

Pour offrir à vos utilisateurs une expérience de navigation plus sûre, WebView les objets valident les URL à l'aide de la navigation sécurisée Google, ce qui permet à votre application d'afficher un avertissement lorsque les utilisateurs tentent d'accéder à un site Web potentiellement dangereux.

Bien que la valeur par défaut de EnableSafeBrowsing soit "true", vous pouvez dans certains cas n'activer la navigation sécurisée que de manière conditionnelle ou la désactiver. Android 8.0 (niveau d'API 26) et versions ultérieures permettent d'utiliser setSafeBrowsingEnabled() pour activer ou désactiver la navigation sécurisée pour un objet WebView individuel.

Si vous souhaitez que tous les WebView objets désactivent les vérifications de la navigation sécurisée, ajoutez l'élément <meta-data> suivant au fichier manifeste de votre application :

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

Définir des actions programmatiques

Lorsqu'une instance de WebView tente de charger une page classée par Google comme menace connue, WebView affiche par défaut un interstitiel qui avertit les utilisateurs de la menace connue. Cet écran permet aux utilisateurs de charger l'URL malgré tout ou de revenir à une page précédente sécurisée.

Si vous ciblez Android 8.1 (niveau d'API 27) ou une version ultérieure, vous pouvez définir par programmation la manière dont votre application répond à une menace connue de différentes manières :

  • Vous pouvez contrôler si votre application signale les menaces connues à la navigation sécurisée.
  • Vous pouvez faire en sorte que votre application effectue automatiquement une action particulière, par exemple revenir à la sécurité, chaque fois qu'elle rencontre une URL classée comme menace connue.

Les extraits de code suivants montrent comment demander aux instances WebView de votre application de toujours revenir à la sécurité après avoir rencontré une menace connue :

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

API HTML5 Geolocation

Pour les applications ciblant Android 6.0 (niveau d'API 23) et versions ultérieures, l'API Geolocation n'est compatible qu'avec les origines sécurisées, telles que HTTPS. Toute requête adressée à l'API Geolocation sur des origines non sécurisées est automatiquement refusée sans appeler la méthode onGeolocationPermissionsShowPrompt() correspondante.

Désactiver la collecte de métriques

WebView peut importer des données de diagnostic anonymes dans Google lorsque l'utilisateur donne son consentement. Les données sont collectées par application pour chaque application qui instancie un WebView. Vous pouvez désactiver cette fonctionnalité en créant la balise suivante dans l'élément du manifeste <application> :

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

Les données ne sont importées à partir d'une application que si l'utilisateur y consent et que l'application ne se désactive pas. Pour en savoir plus sur la désactivation des rapports de données de diagnostic, consultez Confidentialité des utilisateurs dans les rapports WebView.

API Termination Handling

L'API Termination Handling gère les cas où le processus de rendu d'un objet WebView disparaît, soit parce que le système arrête le moteur de rendu pour récupérer la mémoire nécessaire, soit parce que le processus de rendu plante. En utilisant cette API, vous permettez à votre application de continuer à s'exécuter, même si le processus de rendu disparaît.

Si un moteur de rendu plante lors du chargement d'une page Web particulière, une nouvelle tentative de chargement de cette même page peut entraîner le même comportement de plantage de rendu pour un nouvel objet WebView.

L'extrait de code suivant montre comment utiliser cette API dans une 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;
    }
}

API Renderer Importance

Lorsque les objets WebView fonctionnent en mode multiprocessus, vous disposez d'une certaine flexibilité quant à la manière dont votre application gère les situations de mémoire insuffisante. Vous pouvez utiliser l'API Renderer Importance, introduite dans Android 8.0, pour définir une règle de priorité pour le moteur de rendu attribué à un objet WebView particulier. En particulier, vous pouvez souhaiter que la partie principale de votre application continue de s'exécuter lorsqu'un moteur de rendu qui affiche les objets WebView de votre application est arrêté. Vous pouvez le faire, par exemple, si vous prévoyez de ne pas afficher l'objet WebView pendant une longue période afin que le système puisse récupérer la mémoire utilisée par le moteur de rendu.

L'extrait de code suivant montre comment attribuer une priorité au processus de rendu associé aux objets WebView de votre application :

Kotlin

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

Java

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

Dans cet extrait particulier, la priorité du moteur de rendu est la même que la priorité par défaut de l'application, ou y est liée. L'argument true réduit la priorité du moteur de rendu à RENDERER_PRIORITY_WAIVED lorsque l'objet WebView associé n'est plus visible. En d'autres termes, un argument true indique que votre application ne se soucie pas de savoir si le système maintient le processus de rendu actif. En fait, ce niveau de priorité inférieur rend probable l'arrêt du processus de rendu en cas de mémoire insuffisante.

Pour en savoir plus sur la manière dont le système gère les situations de mémoire insuffisante, consultez Processus et cycle de vie des applications.