Tworzenie aplikacji internetowych w komponencie WebView

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

WebView może Ci pomóc podać w aplikacji informacje, które być może trzeba zaktualizować, np. umowę użytkownika lub przewodnik. W aplikacji na Androida możesz utworzyć Activity, który zawiera WebView, a następnie użyć go do wyświetlenia dokumentu hostowanego online.

WebView może też pomóc, gdy aplikacja przekazuje dane użytkownikowi, które do pobrania danych, np. poczty e-mail, wymaga połączenia z internetem. W takim przypadku może się okazać, że łatwiej jest utworzyć w aplikacji na Androida stronę WebView, która wyświetla stronę internetową ze wszystkimi danymi użytkownika, niż wykonywać żądanie sieciowe, a potem przeanalizować je i wyrenderować zgodnie z układem Androida. Zamiast tego możesz zaprojektować stronę internetową dostosowaną do urządzeń z Androidem, a potem zaimplementować w aplikacji na Androida kod WebView, który wczytuje stronę internetową.

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 WebView na starszych wersjach Androida

Aby bezpiecznie korzystać z najnowszych funkcji WebView na urządzeniu, na którym działa Twoja aplikacja, dodaj bibliotekę AndroidX Webkit. Jest to biblioteka statyczna, którą możesz dodać do aplikacji, aby używać interfejsów API android.webkit, które nie są dostępne na wcześniejszych wersjach platformy.

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

Kotlin

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

Odlotowy

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

Aby dowiedzieć się więcej, zapoznaj się z przykładem WebView na GitHubie.

Dodawanie komponentu WebView do aplikacji

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

Dodawanie komponentu 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 kodu 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 komponentu WebView w funkcji onCreate()

Aby zamiast tego 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 pokazano 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łącz obsługę pełnego ekranu za pomocą WebChromeClient. Ta klasa jest też wywoływana, gdy WebView potrzebuje uprawnień do zmiany interfejsu użytkownika aplikacji hosta, na przykład do tworzenia lub zamykania okien lub wysyłania do użytkownika okien JavaScript. Więcej informacji o debugowaniu w tym kontekście znajdziesz w artykule Debugowanie aplikacji internetowych.
  • Obsługa zdarzeń, które wpływają na renderowanie treści, takich jak błędy podczas przesyłania formularzy czy nawigacji za pomocą WebViewClient. Tej podklasy możesz też użyć do przechwycenia ładowania adresów URL.
  • Aby włączyć JavaScript, zmodyfikuj plik WebSettings.
  • Korzystanie z JavaScriptu w celu uzyskiwania dostępu do obiektów platformy Androida wstrzykniętych w elemencie WebView.

Używanie JavaScriptu w komponencie WebView

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

Włączanie JavaScript

JavaScript jest domyślnie wyłączony w pliku WebView. Możesz ją włączyć za pomocą WebSettings dołączonego do WebView. Pobierz WebSettings za pomocą getSettings(), a następnie włącz JavaScript w 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 innych ustawień, które mogą Ci się przydać. Jeśli np. tworzysz aplikację internetową dostosowaną do WebView w Twojej aplikacji na Androida, możesz zdefiniować niestandardowy ciąg klienta użytkownika za pomocą setUserAgentString(), a następnie wysłać zapytanie do niestandardowego klienta użytkownika na Twojej stronie internetowej, aby sprawdzić, czy klient, który chce wyświetlić Twoją stronę, to właśnie Twoja aplikacja na Androida.

Powiąż kod JavaScript z kodem Androida

Podczas tworzenia aplikacji internetowej zaprojektowanej pod kątem WebView w Twojej aplikacji na Androida możesz utworzyć interfejsy między kodem JavaScript a kodem na Androida po stronie klienta. Na przykład kod JavaScript może wywoływać metodę w kodzie Androida w celu wyświetlenia Dialog, zamiast korzystać z funkcji alert() JavaScriptu.

Aby powiązać nowy interfejs między kodem JavaScriptu a kodem Androida, wywołaj addJavascriptInterface() i przekaż jej wystąpienie klasy w celu powiązania z JavaScriptem i nazwą interfejsu, którą JavaScript może wywołać, by uzyskać dostęp do klasy.

Na przykład w aplikacji na Androida możesz umieścić te zajęcia:

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 przy użyciu metody showToast().

Możesz powiązać tę klasę z JavaScriptem, który działa w obrębie WebView za pomocą addJavascriptInterface(), jak 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 JavaScriptu działającego w interfejsie WebView. W tym momencie aplikacja internetowa ma dostęp do klasy WebAppInterface. Oto na przykład kod HTML i JavaScript, który po kliknięciu przycisku przez użytkownika tworzy w nowym interfejsie komunikat tego typu:

<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 je na Twojej 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 klika link na stronie internetowej w WebView, domyślnie Android uruchamia aplikację, która obsługuje adresy URL. Zwykle domyślna przeglądarka otwiera i wczytuje docelowy adres URL. Możesz jednak zastąpić ten sposób działania w przypadku elementu WebView, aby linki otwierały się w obrębie WebView. Możesz pozwolić użytkownikowi na poruszanie się wstecz i do przodu w historii strony internetowej przechowywanej w Twoim interfejsie WebView.

Aby otwierać linki kliknięte przez użytkownika, podaj WebViewClient dla: WebView za pomocą setWebViewClient(). Wszystkie linki, które klika użytkownik, wczytują się na urządzeniu WebView. Jeśli chcesz mieć większą kontrolę nad tym, gdzie wczytuje się kliknięty link, utwórz własny tag WebViewClient, który zastąpi 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ę 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 określonej domeny, tak jak zostało to określone w poprzednim przykładzie. Jeśli jednak pasują, metoda zwraca wartość false (fałsz) i nie zastępuje wczytywanego adresu URL. Umożliwia to WebView ładowanie adresu URL w zwykły sposób. Jeśli host URL-a się nie zgadza, tworzony jest element Intent, który uruchamia domyślną przeglądarkę Activity do obsługi adresów URL, która otwiera się w domyślnej przeglądarce internetowej użytkownika.

Obsługa niestandardowych adresów URL

WebView stosuje ograniczenia w przypadku żądań zasobów i rozwiązywania problemów z linkami, które korzystają ze schematu niestandardowego adresu URL. Jeśli na przykład implementujesz wywołania zwrotne 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 linków takich:

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

Nieprawidłowe adresy URL, takie jak ten w poprzednim przykładzie, są obsługiwane w niespójny sposób w zasadzie WebView, dlatego zalecamy korzystanie z prawidłowego adresu URL. W przypadku domeny, którą kontroluje Twoja organizacja, możesz użyć schematu niestandardowego lub adresu URL HTTPS.

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

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

Możesz obsługiwać 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 służy przede wszystkim do uruchamiania intencji dla określonych adresów URL. Podczas implementacji pamiętaj, aby zwracać wartość false w przypadku adresów URL obsługiwanych przez WebView. Nie ograniczaj się jednak do wprowadzania intencji. Intencje uruchamiania możesz zastąpić dowolnym zachowaniem niestandardowym z poprzednich przykładów kodu.

Gdy WebView zastępuje wczytywanie adresu URL, automatycznie gromadzi historię odwiedzonych stron internetowych. Aby poruszać się wstecz i do przodu, używaj funkcji goBack() i goForward().

Na przykład poniżej widać, jak Activity może użyć przycisku Wstecz na urządzeniu, by przejść 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 Androida X AppCompat w wersji 1.6.0 lub nowszej, możesz jeszcze bardziej uprościć poprzedni fragment:

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ę strony, którą może odwiedzić. Możesz też użyć canGoForward(), aby sprawdzić, czy istnieje już historia przekazywania danych. Jeśli tego nie zrobisz, to gdy użytkownik dotrze do końca historii, goBack() i goForward() nie będą już robić żadnych działań.

Wprowadzanie zmian w konfiguracji urządzenia

W czasie działania aplikacji zmiana stanu aktywności następuje, gdy zmieni się konfiguracja urządzenia, np. gdy użytkownik obróci urządzenie lub zamknie edytor metod wprowadzania. Te zmiany powodują zniszczenie działania obiektu WebView i utworzenie nowej aktywności. Spowoduje to również utworzenie nowego obiektu WebView, który wczytuje adres URL zniszczonego obiektu. Aby zmodyfikować domyślne działanie aktywności, możesz zmienić sposób obsługi zmian orientation w pliku manifestu. Więcej informacji o obsłudze zmian konfiguracji w czasie działania znajdziesz w sekcji Obsługa zmian konfiguracji.

Zarządzanie oknami

Domyślnie żądania otwarcia nowych okien są ignorowane. Bez względu na to, czy są one otwierane przez JavaScript, czy przez atrybut docelowy linku. Możesz dostosować WebChromeClient w taki sposób, by otwierać wiele okien według własnego uznania.

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