Android fornisce diverse API per aiutarti a gestire gli oggetti WebView che visualizzano contenuti web nella tua app.
Questa pagina descrive come utilizzare queste API per lavorare in modo più efficace con gli oggetti WebView, migliorando la stabilità e la sicurezza della tua app.
API Version
A partire da Android 7.0 (livello API 24), gli utenti possono scegliere tra diversi pacchetti per visualizzare i contenuti web in un oggetto WebView.
La libreria Jetpack Webkit
include il
getCurrentWebViewPackage()
metodo per recuperare le informazioni relative al pacchetto che visualizza i contenuti web
nella tua app. Questo metodo è utile per analizzare gli errori che si verificano solo
quando l'app tenta di visualizzare i contenuti web utilizzando l'implementazione di WebView di un pacchetto specifico.
Per utilizzare questo metodo, aggiungi la logica mostrata nel seguente snippet di codice:
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);
Servizio Google Navigazione sicura
Per offrire agli utenti un'esperienza di navigazione più sicura, WebView
gli oggetti verificano gli URL utilizzando
Google Navigazione sicura,
che consente alla tua app di mostrare agli utenti un avviso quando tentano di navigare verso un
sito web non sicuro.
Sebbene il valore predefinito di EnableSafeBrowsing sia true, in alcuni casi potresti voler abilitare la navigazione sicura solo in modo condizionale o disabilitarla. Android 8.0 (livello API 26) e versioni successive supportano l'utilizzo di
setSafeBrowsingEnabled()
per attivare o disattivare la navigazione sicura per un singolo oggetto WebView.
Se vuoi che tutti gli oggetti WebView disattivino i controlli di navigazione sicura, aggiungi il seguente elemento <meta-data> al file manifest dell'app:
<manifest> <application> <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing" android:value="false" /> ... </application> </manifest>
Definire le azioni programmatiche
Quando un'istanza di WebView tenta di caricare una pagina classificata da Google come minaccia nota, WebView mostra per impostazione predefinita un interstitial che avvisa gli utenti della minaccia nota. Questa schermata offre agli utenti la possibilità di caricare comunque l'URL o di tornare a una pagina precedente sicura.
Se utilizzi come target Android 8.1 (livello API 27) o versioni successive, puoi definire in modo programmatico il modo in cui la tua app risponde a una minaccia nota nei seguenti modi:
- Puoi controllare se la tua app segnala le minacce note alla navigazione sicura.
- Puoi fare in modo che la tua app esegua automaticamente una determinata azione, ad esempio tornare alla sicurezza, ogni volta che rileva un URL classificato come minaccia nota.
I seguenti snippet di codice mostrano come indicare alle istanze di WebView della tua app di tornare sempre alla sicurezza dopo aver rilevato una minaccia nota:
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
Per le app che utilizzano come target Android 6.0 (livello API 23) e versioni successive, l'API Geolocation è supportata solo su origini sicure, come HTTPS. Qualsiasi richiesta all'API Geolocation su origini non sicure viene rifiutata automaticamente senza richiamare il metodo onGeolocationPermissionsShowPrompt() corrispondente.
Disattivare la raccolta delle metriche
WebView ha la possibilità di caricare dati diagnostici anonimi su Google quando l'utente fornisce il proprio consenso. I dati vengono raccolti per ogni app che crea un'istanza di WebView. Puoi disattivare questa
funzionalità creando il seguente tag nell'elemento
<application> del manifest:
<manifest> <application> ... <meta-data android:name="android.webkit.WebView.MetricsOptOut" android:value="true" /> </application> </manifest>
I dati vengono caricati da un'app solo se l'utente fornisce il consenso e l'app non disattiva la funzionalità. Per ulteriori informazioni sulla disattivazione della generazione di report sui dati diagnostici, consulta Privacy dell'utente nei report di WebView.
API Termination Handling
L'API Termination Handling gestisce i casi in cui il processo di rendering per un oggetto WebView viene interrotto, perché il sistema lo termina per recuperare la memoria necessaria o perché il processo di rendering si arresta in modo anomalo. Utilizzando questa API, consenti alla tua app di continuare l'esecuzione, anche se il processo di rendering viene interrotto.
Se un renderer si arresta in modo anomalo durante il caricamento di una determinata pagina web, il tentativo di caricare di nuovo la stessa pagina può causare lo stesso comportamento di arresto anomalo del rendering in un nuovo oggetto WebView.
Il seguente snippet di codice illustra come utilizzare questa API all'interno di un
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
Quando gli oggetti WebView
operano in
modalità multiprocesso, hai una certa flessibilità nel modo in cui la tua app gestisce
le situazioni di memoria insufficiente. Puoi utilizzare l'API Renderer Importance, introdotta in Android 8.0, per impostare una policy di priorità per il renderer assegnato a un determinato oggetto WebView. In particolare, potresti voler che la parte principale della tua app continui l'esecuzione quando viene terminato un renderer che visualizza gli oggetti WebView della tua app. Ad esempio, potresti farlo se prevedi di non mostrare l'oggetto WebView per un lungo periodo di tempo, in modo che il sistema possa recuperare la memoria utilizzata dal renderer.
Il seguente snippet di codice mostra come assegnare una priorità al processo di rendering associato agli oggetti WebView della tua app:
Kotlin
val myWebView: WebView = ... myWebView.setRendererPriorityPolicy(RENDERER_PRIORITY_BOUND, true)
Java
WebView myWebView; myWebView.setRendererPriorityPolicy(RENDERER_PRIORITY_BOUND, true);
In questo snippet specifico, la priorità del renderer è uguale o è vincolata alla priorità predefinita dell'app. L'argomento true riduce la priorità del renderer a RENDERER_PRIORITY_WAIVED quando l'oggetto WebView associato non è più visibile. In altre parole, un argomento true indica che alla tua app non interessa se il sistema mantiene attivo il processo di rendering. Infatti, questo livello di priorità inferiore rende probabile che il processo di rendering venga terminato in situazioni di memoria insufficiente.
Per scoprire di più sulla diagnosi e sull'ottimizzazione del footprint della memoria della tua app quando utilizzi contenuti web, consulta Gestire ed eseguire la diagnosi della memoria di WebView. Per scoprire di più su come il sistema gestisce le situazioni di memoria insufficiente nei processi, consulta Processi e ciclo di vita delle app.
Conservare lo stato e la cronologia di navigazione
Quando il sistema recupera le risorse in background o il dispositivo subisce una modifica della configurazione, l'attività di un'app e i relativi oggetti WebView potrebbero essere eliminati. Per conservare il contesto di navigazione dell'utente, utilizza ilsaveState(Bundle)
metodo all'interno di onSaveInstanceState() per serializzare la cronologia di navigazione in un
Bundle, quindi ripristinala utilizzando restoreState(Bundle).
Poiché Android applica un limite rigoroso di 1 MB all'intero processo per savedInstanceState, la serializzazione di sessioni di navigazione di grandi dimensioni con l'API del framework standard può generare un'eccezione TransactionTooLargeException.
Per applicare i limiti di dimensione o eliminare in sicurezza le voci della cronologia in avanti, utilizza il metodo Jetpack Webkit WebViewCompat.saveState().
Per strategie dettagliate e best practice sulla gestione efficiente dello stato, consulta Gestire in modo efficiente lo stato di WebView.