Android udostępnia kilka interfejsów API, które pomagają zarządzać obiektami WebView wyświetlającymi treści internetowe w aplikacji.
Na tej stronie dowiesz się, jak używać tych interfejsów API, aby efektywniej pracować z obiektami WebView i zwiększyć stabilność oraz bezpieczeństwo aplikacji.
Version API
Od Androida 7.0 (poziom interfejsu API 24) użytkownicy mogą wybierać spośród kilku różnych pakietów do wyświetlania treści internetowych w obiekcie WebView.
Biblioteka Jetpack Webkit
zawiera
getCurrentWebViewPackage()
metodę, która służy do pobierania informacji o pakiecie wyświetlającym treści
internetowe w aplikacji. Ta metoda jest przydatna podczas analizowania błędów, które występują tylko
wtedy, gdy aplikacja próbuje wyświetlić treści internetowe za pomocą implementacji WebView w określonym pakiecie.
Aby użyć tej metody, dodaj logikę pokazaną 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);
Google Safe Browsing Service
Aby zapewnić użytkownikom bezpieczniejsze przeglądanie, WebView
obiekty weryfikują adresy URL za pomocą
Bezpiecznego przeglądania Google,
dzięki czemu aplikacja może wyświetlać ostrzeżenie, gdy użytkownicy próbują przejść do
potencjalnie niebezpiecznej witryny.
Domyślna wartość EnableSafeBrowsing to true, ale w niektórych przypadkach możesz chcieć włączyć Bezpieczne przeglądanie tylko warunkowo lub je wyłączyć. Android 8.0 (poziom interfejsu API 26) i nowsze wersje obsługują używanie
setSafeBrowsingEnabled()
do przełączania Bezpiecznego przeglądania w przypadku poszczególnych obiektów WebView.
Jeśli chcesz, aby wszystkie WebView obiekty nie były sprawdzane przez Bezpieczne przeglądanie
, dodaj ten element <meta-data> do pliku manifestu aplikacji:
<manifest> <application> <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing" android:value="false" /> ... </application> </manifest>
Określanie działań programowych
Gdy instancja WebView próbuje wczytać stronę, która jest klasyfikowana przez Google jako znane zagrożenie, WebView domyślnie wyświetla ona reklamę pełnoekranową ostrzegającą użytkowników przed tym zagrożeniem. Na tym ekranie użytkownicy mogą wczytać adres URL lub wrócić do poprzedniej bezpiecznej strony.
Jeśli kierujesz aplikację na Androida 8.1 (poziom interfejsu API 27) lub nowszego, możesz programowo określić, jak aplikacja ma reagować na znane zagrożenie:
- Możesz określić, czy aplikacja ma zgłaszać znane zagrożenia do Bezpiecznego przeglądania.
- Możesz sprawić, że aplikacja będzie automatycznie wykonywać określone działanie (np. wracać do bezpiecznego stanu) za każdym razem, gdy napotka adres URL sklasyfikowany jako znane zagrożenie.
Te fragmenty kodu pokazują, jak poinstruować instancje WebView w aplikacji, aby zawsze wracały do bezpiecznego stanu 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(); } } }
HTML5 Geolocation API
W przypadku aplikacji kierowanych na Androida 6.0 (poziom interfejsu API 23) i nowsze wersje Geolocation API jest obsługiwane tylko w bezpiecznych źródłach, takich jak HTTPS. Każde żądanie do Geolocation API w źródłach niebezpiecznych jest automatycznie odrzucane bez wywoływania odpowiedniej metody onGeolocationPermissionsShowPrompt().
Rezygnacja ze zbierania danych
Gdy użytkownik wyrazi zgodę, WebView może przesyłać anonimowe dane diagnostyczne do Google. Dane są zbierane w przypadku każdej aplikacji, która tworzy instancję WebView. Aby zrezygnować z tej
funkcji, utwórz ten tag w elemencie manifestu
<application>:
<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 na to zgodę i aplikacja nie zrezygnuje z tej funkcji. Więcej informacji o rezygnacji z raportowania danych diagnostycznych znajdziesz w artykule Prywatność użytkowników w raportach WebView reporting.
Termination Handling API
Termination Handling API obsługuje przypadki, w których proces renderowania obiektu WebView zostaje zakończony, ponieważ system go zamyka, aby odzyskać niezbędną pamięć, lub gdy proces renderowania ulega awarii. Dzięki temu interfejsowi API aplikacja może nadal działać, nawet jeśli proces renderowania zostanie zakończony.
Jeśli proces renderowania ulegnie awarii podczas wczytywania określonej strony internetowej, próba ponownego wczytania tej samej strony może spowodować, że nowy obiekt WebView będzie się zachowywać tak samo.
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; } }
Renderer Importance API
Gdy WebView obiekty
działają w
trybie wieloprocesowym, masz pewną elastyczność w sposobie, w jaki aplikacja obsługuje
sytuacje, w których brakuje pamięci. Możesz użyć Renderer Importance API, wprowadzonego w Androidzie 8.0, aby ustawić zasadę priorytetu dla procesu renderowania przypisanego do określonego obiektu WebView. W szczególności możesz chcieć, aby główna część aplikacji nadal działała, gdy proces renderowania wyświetlający obiekty WebView aplikacji zostanie zamknięty. Możesz to zrobić na przykład wtedy, gdy spodziewasz się, że obiekt WebView nie będzie wyświetlany przez dłuższy czas, aby system mógł odzyskać pamięć używaną przez proces renderowania.
Ten fragment kodu pokazuje, jak przypisać priorytet do procesu renderowania powiązanego z obiektami WebView aplikacji:
Kotlin
val myWebView: WebView = ... myWebView.setRendererPriorityPolicy(RENDERER_PRIORITY_BOUND, true)
Java
WebView myWebView; myWebView.setRendererPriorityPolicy(RENDERER_PRIORITY_BOUND, true);
W tym fragmencie priorytet procesu renderowania jest taki sam jak domyślny priorytet aplikacji lub jest z nim powiązany. Argument true zmniejsza priorytet procesu renderowania do RENDERER_PRIORITY_WAIVED, gdy powiązany obiekt WebView nie jest już widoczny. Innymi słowy, argument true oznacza, że aplikacja nie dba o to, czy system utrzymuje proces renderowania przy życiu. W rzeczywistości ten niższy poziom priorytetu sprawia, że proces renderowania jest bardziej narażony na zamknięcie w sytuacjach, w których brakuje pamięci.
Więcej informacji o diagnozowaniu i optymalizowaniu wykorzystania pamięci przez aplikację podczas korzystania z treści internetowych znajdziesz w artykule Zarządzanie pamięcią komponentu WebView i jej diagnozowanie. Więcej informacji o tym, jak system radzi sobie z sytuacjami, w których brakuje pamięci, znajdziesz w artykule Procesy i cykl życia aplikacji.
Zachowywanie stanu i historii nawigacji
Gdy system odzyskuje zasoby działające w tle lub urządzenie zmienia konfigurację, aktywność aplikacji i jej obiekty WebView mogą zostać zniszczone. Aby zachować kontekst nawigacji użytkownika, użyj metody saveState(Bundle)
w onSaveInstanceState()aby serializować historię nawigacji do
Bundle, a następnie przywrócić ją za pomocą restoreState(Bundle).
Ponieważ Android wymusza ścisły limit 1 MB w całym procesie dla savedInstanceState, serializowanie dużych sesji przeglądania za pomocą standardowego interfejsu API platformy może spowodować wyjątek TransactionTooLargeException.
Aby wymusić limity rozmiaru lub bezpiecznie usunąć wpisy z historii do przodu, użyj metody Jetpack Webkit WebViewCompat.saveState().
Szczegółowe strategie i sprawdzone metody skutecznego zarządzania stanem znajdziesz w artykule Skuteczne zarządzanie stanem WebView.