Tworzenie aplikacji internetowych w komponencie WebView

Użyj WebView, aby dostarczyć aplikację internetową lub stronę internetową jako część aplikacji klienckiej. Klasa WebView jest rozszerzeniem klasy View Androida, która umożliwia wyświetlanie stron internetowych jako części układu aktywności. Nie zawiera funkcji w pełni rozwiniętej przeglądarki, takich jak elementy sterujące nawigacją czy pasek adresu. Domyślnie WebView wyświetla tylko stronę internetową.

WebView może pomóc Ci w podaniu w aplikacji informacji, które mogą wymagać aktualizacji, takich jak umowa użytkownika lub przewodnik użytkownika. W aplikacji na Androida możesz utworzyć element Activity zawierający WebView, a następnie użyć go do wyświetlenia dokumentu hostowanego online.

WebView może też pomóc, gdy aplikacja udostępnia użytkownikowi dane, które wymagają połączenia z internetem, np. e-maile. W takim przypadku może się okazać, że łatwiej jest utworzyć w aplikacji na Androida komponent WebView, który wyświetla stronę internetową ze wszystkimi danymi użytkownika, niż wysyłać żądanie sieciowe, a następnie analizować dane i renderować je w układzie Androida. Zamiast tego możesz zaprojektować stronę internetową dostosowaną do urządzeń z Androidem, a następnie zaimplementować w aplikacji na Androida element WebView, który będzie ją wczytywać.

Z tego dokumentu dowiesz się, jak zacząć korzystać z WebView, jak powiązać JavaScript ze strony internetowej z kodem po stronie klienta w aplikacji na Androida, jak obsługiwać nawigację po stronie i jak zarządzać oknami podczas korzystania z WebView.

Praca z komponentem WebView we wcześniejszych wersjach Androida

Aby bezpiecznie korzystać z nowszych funkcji WebView na urządzeniu, na którym działa aplikacja, dodaj bibliotekę Jetpack Webkit. Jest to biblioteka statyczna, którą możesz dodać do aplikacji, aby używać interfejsów API android.webkit niedostępnych we wcześniejszych wersjach platformy.

Dodaj go do pliku build.gradle w ten sposób:

Kotlin

dependencies {
    implementation("androidx.webkit:webkit:1.8.0")
}

Dynamiczny

dependencies {
    implementation ("androidx.webkit:webkit:1.8.0")
}

Więcej informacji znajdziesz w przykładzie WebView na GitHubie.

Dodawanie WebView do aplikacji

Aby dodać WebView do aplikacji, możesz uwzględnić element <WebView> w układzie aktywności lub ustawić całe okno Activity jako WebView w onCreate.

Dodawanie WebView w układzie aktywności

Aby dodać WebView do aplikacji w układzie, dodaj ten kod do pliku XML układu aktywności:

<WebView
    android:id="@+id/webview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
/>

Aby wczytać stronę internetową w elemencie WebView, użyj loadUrl, jak w tym przykładzie:

Kotlin

val myWebView: WebView = findViewById(R.id.webview)
myWebView.loadUrl("http://www.example.com")

Java

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.loadUrl("http://www.example.com");
.

Dodawanie WebView w onCreate()

Aby dodać WebView do aplikacji w metodzie onCreate() aktywności, użyj logiki podobnej do tej:

Kotlin

val myWebView = WebView(activityContext)
setContentView(myWebView)

Java

WebView myWebView = new WebView(activityContext);
setContentView(myWebView);

Następnie wczytaj stronę:

Kotlin

myWebView.loadUrl("http://www.example.com")

Java

myWebView.loadUrl("https://www.example.com");

Możesz też wczytać adres URL z ciągu znaków HTML:

Kotlin

// Create an unencoded HTML string, then convert the unencoded HTML string into
// bytes. Encode it with base64 and load the data.
val unencodedHtml =
     "<html><body>'%23' is the percent code for ‘#‘ </body></html>";
val encodedHtml = Base64.encodeToString(unencodedHtml.toByteArray(), Base64.NO_PADDING)
myWebView.loadData(encodedHtml, "text/html", "base64")

Java

// Create an unencoded HTML string, then convert the unencoded HTML string into
// bytes. Encode it with base64 and load the data.
String unencodedHtml =
     "<html><body>'%23' is the percent code for ‘#‘ </body></html>";
String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(),
        Base64.NO_PADDING);
myWebView.loadData(encodedHtml, "text/html", "base64");

Aplikacja musi mieć dostęp do internetu. Aby uzyskać dostęp do internetu, poproś o uprawnienie INTERNET w pliku manifestu, jak w tym przykładzie:

<manifest ... >
    <uses-permission android:name="android.permission.INTERNET" />
    ...
</manifest>

Możesz dostosować WebView, wykonując dowolną z tych czynności:

  • Włączanie obsługi pełnego ekranu za pomocą WebChromeClient. Ta klasa jest też wywoływana, gdy WebView potrzebuje uprawnień do zmiany interfejsu aplikacji hosta, np. do tworzenia lub zamykania okien albo wysyłania użytkownikowi okien dialogowych JavaScriptu. Więcej informacji o debugowaniu w tym kontekście znajdziesz w artykule Debugowanie aplikacji internetowych.
  • obsługę zdarzeń, które wpływają na renderowanie treści, takich jak błędy podczas przesyłania formularzy lub nawigacji za pomocą WebViewClient; Możesz też użyć tej podklasy do przechwytywania wczytywania adresów URL.
  • Włączanie JavaScriptu przez zmodyfikowanie pliku WebSettings.
  • używanie JavaScriptu do uzyskiwania dostępu do obiektów platformy Androida, które zostały wstrzyknięte do elementu WebView;

Używanie JavaScriptu w komponencie WebView

Jeśli strona internetowa, którą chcesz wczytać w elemencie WebView, używa JavaScriptu, musisz włączyć JavaScript w elemencie WebView. Po włączeniu JavaScriptu możesz tworzyć interfejsy między kodem aplikacji a kodem JavaScript.

Włącz obsługę JavaScriptu

JavaScript jest domyślnie wyłączony w WebView. Możesz ją włączyć za pomocą WebSettings dołączonego do WebView. Pobierz WebSettings za pomocą getSettings, a następnie włącz JavaScript za pomocą setJavaScriptEnabled.

Przyjrzyj się temu przykładowi:

Kotlin

val myWebView: WebView = findViewById(R.id.webview)
myWebView.settings.javaScriptEnabled = true

Java

WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);

WebSettings zapewnia dostęp do wielu innych ustawień, które mogą Ci się przydać. Jeśli na przykład tworzysz aplikację internetową przeznaczoną specjalnie dla WebView w aplikacji na Androida, możesz zdefiniować niestandardowy ciąg znaków klienta użytkownika za pomocą setUserAgentString, a następnie wysłać zapytanie do niestandardowego klienta użytkownika na stronie internetowej, aby sprawdzić, czy klient żądający strony internetowej jest Twoją aplikacją na Androida.

Wiązanie kodu JavaScript z kodem Androida

Podczas tworzenia aplikacji internetowej przeznaczonej specjalnie dla WebView w aplikacji na Androida możesz tworzyć interfejsy między kodem JavaScript a kodem Androida po stronie klienta. Na przykład kod JavaScript może wywołać metodę w kodzie Androida, aby wyświetlić Dialog, zamiast używać funkcji alert() JavaScriptu.

Aby powiązać nowy interfejs między kodem JavaScript a kodem Androida, wywołaj metodę addJavascriptInterface, przekazując jej instancję klasy do powiązania z kodem JavaScript oraz nazwę interfejsu, który kod JavaScript może wywołać, aby uzyskać dostęp do klasy.

Więcej informacji o komunikacji między kodem JavaScript a kodem natywnym, w tym o nowocześniejszych i bezpieczniejszych interfejsach API, znajdziesz w artykule Uzyskiwanie dostępu do natywnych interfejsów API za pomocą JSBridge.

Możesz na przykład uwzględnić w aplikacji na Androida tę klasę:

Kotlin

/** Instantiate the interface and set the context.  */
class WebAppInterface(private val mContext: Context) {

    /** Show a toast from the web page.  */
    @JavascriptInterface
    fun showToast(toast: String) {
        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show()
    }
}

Java

public class WebAppInterface {
    Context mContext;

    /** Instantiate the interface and set the context. */
    WebAppInterface(Context c) {
        mContext = c;
    }

    /** Show a toast from the web page. */
    @JavascriptInterface
    public void showToast(String toast) {
        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
    }
}

W tym przykładzie klasa WebAppInterface umożliwia stronie internetowej utworzenie wiadomości Toast za pomocą metody showToast().

Możesz powiązać tę klasę z kodem JavaScript, który działa w WebView, za pomocą addJavascriptInterface(), jak pokazano w tym przykładzie:

Kotlin

val webView: WebView = findViewById(R.id.webview)
webView.addJavascriptInterface(WebAppInterface(this), "Android")

Java

WebView webView = (WebView) findViewById(R.id.webview);
webView.addJavascriptInterface(new WebAppInterface(this), "Android");

Spowoduje to utworzenie interfejsu o nazwie Android dla kodu JavaScript działającego w WebView. W tym momencie aplikacja internetowa ma dostęp do klasy WebAppInterface. Oto przykładowy kod HTML i JavaScript, który tworzy wyskakującą wiadomość za pomocą nowego interfejsu, gdy użytkownik kliknie przycisk:

<input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" />

<script type="text/javascript">
    function showAndroidToast(toast) {
        Android.showToast(toast);
    }
</script>

Nie trzeba inicjować interfejsu Android z poziomu JavaScriptu. WebView automatycznie udostępnia go na Twojej stronie internetowej. Gdy użytkownik kliknie przycisk, funkcja showAndroidToast() używa interfejsu Android, aby wywołać metodę WebAppInterface.showToast().

Obsługa nawigacji na stronie

Gdy użytkownik kliknie link na stronie internetowej w aplikacji WebView, Android domyślnie uruchamia aplikację, która obsługuje adresy URL. Zwykle otwiera się domyślna przeglądarka i wczytuje docelowy adres URL. Możesz jednak zastąpić to działanie w przypadku WebView, aby linki otwierały się w WebView. Następnie możesz umożliwić użytkownikowi przechodzenie wstecz i w przód w historii stron internetowych, która jest przechowywana przez WebView.

Aby otwierać linki kliknięte przez użytkownika, podaj WebViewClient dla WebView za pomocą setWebViewClient. Wszystkie linki, które użytkownik kliknie, będą się otwierać w aplikacji WebView. Jeśli chcesz mieć większą kontrolę nad tym, gdzie wczytuje się kliknięty link, utwórz własny obiekt WebViewClient, który zastępuje metodę shouldOverrideUrlLoading. W tym przykładzie założono, że MyWebViewClient jest klasą wewnętrzną klasy Activity.

Kotlin

private class MyWebViewClient : WebViewClient() {

    override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
        if (Uri.parse(url).host == "www.example.com") {
            // This is your website, so don't override. Let your WebView load
            // the page.
            return false
        }
        // Otherwise, the link isn't for a page on your site, so launch another
        // Activity that handles URLs.
        Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply {
            startActivity(this)
        }
        return true
    }
}

Java

private class MyWebViewClient extends WebViewClient {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
        if ("www.example.com".equals(request.getUrl().getHost())) {
      // This is your website, so don't override. Let your WebView load the
      // page.
      return false;
    }
    // Otherwise, the link isn't for a page on your site, so launch another
    // Activity that handles URLs.
    Intent intent = new Intent(Intent.ACTION_VIEW, request.getUrl());
    startActivity(intent);
    return true;
  }
}

Następnie utwórz instancję tego nowego WebViewClient dla WebView:

Kotlin

val myWebView: WebView = findViewById(R.id.webview)
myWebView.webViewClient = MyWebViewClient()

Java

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient(new MyWebViewClient());

Teraz, gdy użytkownik kliknie link, system wywoła metodę shouldOverrideUrlLoading, która sprawdza, czy host adresu URL pasuje do konkretnej domeny, jak określono w poprzednim przykładzie. Jeśli tak, metoda zwraca wartość „false” i nie zastępuje wczytywania adresu URL. Umożliwia WebView wczytanie adresu URL w zwykły sposób. Jeśli host adresu URL nie pasuje, tworzony jest obiekt Intent, który uruchamia domyślny obiekt Activity do obsługi adresów URL. Jest on rozpoznawany jako domyślna przeglądarka użytkownika.

Niestandardowe adresy URL z nickiem

WebView stosuje ograniczenia podczas żądania zasobów i rozpoznawania linków, które używają niestandardowego schematu URI adresu URL. Jeśli na przykład zaimplementujesz wywołania zwrotne takie jak shouldOverrideUrlLoading lub shouldInterceptRequest, WebView wywoła je tylko w przypadku prawidłowych adresów URL.

Na przykład WebView może nie wywoływać metody shouldOverrideUrlLoading w przypadku takich linków:

<a href="showProfile">Show Profile</a>

Nieprawidłowe adresy URL, takie jak ten w poprzednim przykładzie, są obsługiwane w WebView w sposób niespójny, dlatego zalecamy używanie prawidłowo sformatowanych adresów URL. Możesz użyć schematu niestandardowego lub adresu URL HTTPS dla domeny, którą kontroluje Twoja organizacja.

Zamiast używać prostego ciągu znaków w linku, jak w poprzednim przykładzie, możesz użyć niestandardowego schematu, takiego jak ten:

<a href="example-app:showProfile">Show Profile</a>

Możesz następnie obsłużyć ten adres URL w metodzie shouldOverrideUrlLoading w ten sposób:

Kotlin

// The URL scheme must be non-hierarchical, meaning no trailing slashes.
const val APP_SCHEME = "example-app:"

override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
    return if (url?.startsWith(APP_SCHEME) == true) {
        urlData = URLDecoder.decode(url.substring(APP_SCHEME.length), "UTF-8")
        respondToData(urlData)
        true
    } else {
        false
    }
}

Java

// The URL scheme must be non-hierarchical, meaning no trailing slashes.
private static final String APP_SCHEME = "example-app:";

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
    if (url.startsWith(APP_SCHEME)) {
        urlData = URLDecoder.decode(url.substring(APP_SCHEME.length()), "UTF-8");
        respondToData(urlData);
        return true;
    }
    return false;
}

Interfejs API shouldOverrideUrlLoading służy przede wszystkim do uruchamiania intencji dla konkretnych adresów URL. Podczas jego implementacji pamiętaj, aby zwracać false w przypadku adresów URL, które obsługuje WebView. Nie musisz jednak ograniczać się do uruchamiania intencji. W powyższych przykładowych kodach możesz zastąpić intencje uruchamiania dowolnym niestandardowym zachowaniem.

Gdy WebView zastępuje wczytywanie adresu URL, automatycznie gromadzi historię odwiedzonych stron internetowych. Możesz poruszać się po historii wstecz i do przodu za pomocą klawiszy goBack i goForward.

Na przykład poniżej pokazujemy, jak Activity może używać przycisku Wstecz na urządzeniu do przechodzenia wstecz:

Kotlin

override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
    // Check whether the key event is the Back button and if there's history.
    if (keyCode == KeyEvent.KEYCODE_BACK && myWebView.canGoBack()) {
        myWebView.goBack()
        return true
    }
    // If it isn't the Back button or there isn't web page history, bubble up to
    // the default system behavior. Probably exit the activity.
    return super.onKeyDown(keyCode, event)
}

Java

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    // Check whether the key event is the Back button and if there's history.
    if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) {
        myWebView.goBack();
        return true;
    }
    // If it isn't the Back button or there's no web page history, bubble up to
    // the default system behavior. Probably exit the activity.
    return super.onKeyDown(keyCode, event);
}

Jeśli Twoja aplikacja używa Jetpacka AppCompat w wersji 1.6.0 lub nowszej, możesz jeszcze bardziej uprościć poprzedni fragment kodu:

Kotlin

onBackPressedDispatcher.addCallback {
    // Check whether there's history.
    if (myWebView.canGoBack()) {
        myWebView.goBack()
    }
}

Java

onBackPressedDispatcher.addCallback {
    // Check whether there's history.
    if (myWebView.canGoBack()) {
        myWebView.goBack();
    }
}

Metoda canGoBack zwraca wartość true, jeśli istnieje historia stron internetowych, które użytkownik może odwiedzić. Podobnie możesz użyć canGoForward, aby sprawdzić, czy istnieje historia do przodu. Jeśli nie wykonasz tego sprawdzenia, po osiągnięciu przez użytkownika końca historii goBack i goForward nie będą nic robić.

Aby zachować historię nawigacji podczas ponownego tworzenia aktywności lub odzyskiwania procesu w tle, użyj metody saveState, aby przekształcić historię nawigacji WebView w obiekt Bundle, i metody restoreState, aby ją przywrócić. Wskazówki dotyczące bezpiecznego zarządzania limitami rozmiaru zapisanego stanu i unikania błędu TransactionTooLargeException znajdziesz w artykule Efektywne zarządzanie stanem komponentu WebView.

Obsługa zmian konfiguracji urządzenia

W czasie działania zmiany stanu aktywności występują, gdy zmienia się konfiguracja urządzenia, np. gdy użytkownicy obracają urządzenie lub odrzucają edytor metody wprowadzania (IME). Te zmiany powodują zniszczenie aktywności obiektu WebView i utworzenie nowej aktywności, co powoduje też utworzenie nowego obiektu WebView, który wczytuje adres URL zniszczonego obiektu. Możesz użyć saveState w onSaveInstanceState() i restoreState po ponownym utworzeniu, aby przywrócić kontekst nawigacji użytkownika. Strategie bezpiecznego zarządzania rozmiarem stanu znajdziesz w artykule Efektywne zarządzanie stanem komponentu WebView.

Aby zmodyfikować domyślne działanie aktywności, możesz zmienić sposób, w jaki obsługuje ona zmiany orientation w pliku manifestu. Więcej informacji o obsłudze zmian konfiguracji w czasie działania znajdziesz w artykule Obsługa zmian konfiguracji.

Zarządzaj oknami

Domyślnie prośby o otwarcie nowych okien są ignorowane. Dotyczy to zarówno okien otwieranych przez JavaScript, jak i atrybut docelowy w linku. Możesz dostosować WebChromeClient, aby określić własne zachowanie podczas otwierania wielu okien.

Aby zwiększyć bezpieczeństwo aplikacji, najlepiej jest uniemożliwić otwieranie wyskakujących okienek i nowych okien. Najbezpieczniejszym sposobem na wdrożenie tego działania jest przekazanie "true" do setSupportMultipleWindows, ale bez zastępowania metody onCreateWindow, od której zależy setSupportMultipleWindows(). Ta logika uniemożliwia wczytanie każdej strony, która używa target="_blank" w linkach.

Zarządzanie pamięcią i wydajnością

WebView działa na silniku opartym na Chromium z osobnymi procesami i kodem natywnym, dlatego podstawowe wykorzystanie pamięci jest większe niż w przypadku standardowych widoków Androida. Ponowne używanie instancji WebView i ich prawidłowe niszczenie jest niezbędne do utrzymania stabilności aplikacji.

Więcej informacji o diagnozowaniu i optymalizowaniu wykorzystania pamięci przez aplikację podczas korzystania z treści z internetu znajdziesz w artykule Zarządzanie pamięcią WebView i jej diagnozowanie.