Tworzenie aplikacji internetowych w komponencie WebView

Użyj WebView, aby dostarczyć aplikację internetową lub stronę internetową jako część aplikacji klienta. Klasa WebView jest rozszerzeniem klasy View na Androidzie, które 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 udostępnianiu w aplikacji informacji, które mogą wymagać aktualizacji, np. umowy z użytkownikiem lub przewodnika. W aplikacji na Androida możesz utworzyć Activity, który zawiera WebView, a następnie użyć go do wyświetlania 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 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 WebView, która będzie wczytywać tę stronę.

W tym dokumencie opisujemy, 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 WebView w starszych wersjach Androida

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

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

Kotlin

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

Groovy

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

Więcej informacji znajdziesz w WebViewprzykładzie w GitHubie.

Dodawanie widoku WebView do aplikacji

Aby dodać WebView do aplikacji, możesz umieścić element <WebView> w układzie aktywności lub ustawić całe okno Activity jako WebViewonCreate().

Dodawanie widoku 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ę w WebView, użyj loadUrl(), jak pokazano 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 metodzie 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 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 pokazano w tym przykładzie:

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

Możesz dostosować WebView na jeden z tych sposobów:

  • Włączanie obsługi pełnego ekranu za pomocą elementu 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 do użytkownika okien dialogowych JavaScript. Więcej informacji o debugowaniu w tym kontekście znajdziesz w artykule Debugowanie aplikacji internetowych.
  • obsługi zdarzeń, które mają wpływ na renderowanie treści, takich jak błędy podczas przesyłania formularzy lub nawigacja za pomocą WebViewClient; Możesz też użyć tej podklasy do przechwytywania wczytywania adresów URL.
  • Włączanie JavaScriptu przez modyfikację WebSettings.
  • Używanie JavaScriptu do uzyskiwania dostępu do obiektów platformy Androida, które zostały wstawione do elementu WebView.

Używanie JavaScriptu w WebView

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

Włącz obsługę języka JavaScript

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 potem 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 różnych innych ustawień, które mogą być przydatne. Jeśli na przykład tworzysz aplikację internetową przeznaczoną specjalnie dla WebView w aplikacji na Androida, możesz zdefiniować niestandardowy ciąg tekstowy agenta użytkownika za pomocą setUserAgentString(), a następnie wysłać zapytanie o niestandardowego agenta użytkownika na stronie internetowej, aby sprawdzić, czy klient wysyłający żądanie do strony internetowej to Twoja aplikacja na Androida.

Wiązanie kodu JavaScript z kodem Androida

Podczas tworzenia aplikacji internetowej przeznaczonej specjalnie do WebVieww 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 funkcję addJavascriptInterface(), przekazując do niej instancję klasy do powiązania z kodem JavaScript oraz nazwę interfejsu, za pomocą której kod JavaScript może wywoływać dostęp do klasy.

Możesz na przykład dodać do 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 na przykład kod HTML i JavaScript, który tworzy komunikat typu toast przy użyciu 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 musisz inicjować interfejsu Android z poziomu JavaScriptu. Usługa WebView automatycznie udostępnia go na stronie internetowej. Gdy użytkownik kliknie przycisk, funkcja showAndroidToast() użyje interfejsu Android, aby wywołać metodę WebAppInterface.showToast().

Obsługa nawigacji na stronie

Gdy użytkownik kliknie link na stronie internetowej w WebView, Android domyślnie uruchamia aplikację obsługującą adresy URL. Zazwyczaj otwiera się domyślna przeglądarka internetowa i wczytuje docelowy adres URL. Możesz jednak zastąpić to działanie na urządzeniu WebView, aby linki otwierały się w WebView. Użytkownik może wtedy przechodzić w przód i w tył w historii stron internetowych, która jest utrzymywana przez Twój WebView.

Aby otwierać linki kliknięte przez użytkownika, podaj WebViewClient dla elementu WebView za pomocą setWebViewClient(). Wszystkie linki, które kliknie użytkownik, będą się otwierać w WebView. Jeśli chcesz mieć większą kontrolę nad tym, gdzie wczytuje się kliknięte połączenie, utwórz własny tag WebViewClient, który zastąpi metodę shouldOverrideUrlLoading(). W przykładzie poniżej 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 sprawdzi, czy host adresu URL pasuje do określonej domeny, jak zdefiniowano w poprzednim przykładzie. Jeśli adres URL pasuje, metoda zwraca wartość false i nie zastępuje ładowania adresu URL. Umożliwia to wczytanie adresu URL przez WebView w zwykły sposób. Jeśli host adresu URL nie pasuje, tworzony jest Intent, aby uruchomić domyślny Activity do obsługi adresów URL, który jest domyślną przeglądarką internetową użytkownika.

Obsługa niestandardowych adresów URL

WebView stosuje ograniczenia podczas wysyłania żądań zasobów i rozwiązywania linków używających niestandardowego schematu URL. Jeśli na przykład zaimplementujesz wywołania zwrotne, takie jak shouldOverrideUrlLoading() lub shouldInterceptRequest(), wtedy WebView wywołuje je tylko w przypadku prawidłowych adresów URL.

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

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

Nieprawidłowe adresy URL, takie jak w przykładzie powyżej, są w WebView obsługiwane w niejednolity sposób, 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ć w linku prostego ciągu znaków, jak w poprzednim przykładzie, możesz użyć niestandardowego schematu, takiego jak ten:

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

Następnie możesz 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 shouldOverrideUrlLoading() API jest przeznaczony głównie do uruchamiania intencji w przypadku określonych adresów URL. Podczas jego wdrażania pamiętaj, aby zwracać wartość false w przypadku adresów URL, które obsługuje WebView. Nie musisz jednak ograniczać się do uruchamiania intencji. W przedstawionych powyżej przykładach kodu możesz zastąpić intencje uruchamiania dowolnym niestandardowym działaniem.

Gdy WebView zastępuje wczytywanie adresu URL, automatycznie gromadzi historię odwiedzonych stron internetowych. Możesz przechodzić w historii wstecz i do przodu za pomocą przycisków goBack()goForward().

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

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 korzysta z AndroidX w wersji AppCompat1.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ść „prawda”, jeśli użytkownik ma historię stron internetowych, które może odwiedzić. Podobnie możesz użyć klawisza canGoForward(), aby sprawdzić, czy istnieje historia do przodu. Jeśli nie przeprowadzisz tego sprawdzenia, po dotarciu użytkownika do końca historii przyciskigoBack()goForward() nie będą wykonywać żadnych działań.

Obsługa zmian konfiguracji urządzenia

Podczas działania aplikacji zmiany stanu aktywności występują, gdy zmienia się konfiguracja urządzenia, np. gdy użytkownik obraca urządzenie lub zamyka edytor IME. Te zmiany powodują zniszczenie działania obiektu WebView i utworzenie nowego działania, które tworzy też nowy obiekt WebView wczytujący adres URL zniszczonego obiektu. Aby zmodyfikować domyślne działanie aktywności, możesz zmienić sposób obsługi orientation w pliku manifestu. Więcej informacji o obsłudze zmian konfiguracji w czasie działania znajdziesz w artykule Obsługa zmian konfiguracji.

Zarządzanie oknami

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

Aby zwiększyć bezpieczeństwo aplikacji, najlepiej uniemożliwić otwieranie wyskakujących okienek i nowych okien. Najbezpieczniejszym sposobem wdrożenia tego działania jest przekazanie "true" do setSupportMultipleWindows() ale nie zastępowanie metody onCreateWindow() od której zależy setSupportMultipleWindows(). Ta logika uniemożliwia wczytywanie stron, które w swoich linkach używają znaku target="_blank".