Wczytaj treści w aplikacji

Możesz udostępniać w aplikacji treści internetowe (np. HTML, JavaScript i CSS), które są statycznie kompilowane w aplikację, a nie pobierane przez internet.

Treści w aplikacji nie wymagają dostępu do internetu ani nie wykorzystują przepustowości użytkownika. Jeśli treści są przeznaczone tylko dla WebView (czyli zależą od komunikacji z aplikacją natywną), użytkownicy nie mogą ich przypadkowo wczytać w przeglądarce.

Treści w aplikacji mają jednak pewne wady. Aktualizowanie treści w internecie wymaga przesłania nowej aktualizacji aplikacji. Jeśli użytkownicy mają nieaktualne wersje aplikacji, istnieje ryzyko, że informacje na stronie i w aplikacji na Twoim urządzeniu nie będą się zgadzać.

WebViewAssetLoader

WebViewAssetLoader to elastyczny i wydajny sposób wczytywania treści w aplikacji do obiektu WebView. Ta klasa obsługuje:

  • Wczytywanie treści z adresem URL HTTP(S) w celu zapewnienia zgodności z zasadą tego samego pochodzenia.
  • Wczytywanie zasobów podrzędnych takich jak JavaScript, CSS, obrazy i elementy iframe.

Umieść WebViewAssetLoader w głównym pliku aktywności. Oto przykład wczytywania prostych treści internetowych z folderu zasobów:

Kotlin

private class LocalContentWebViewClient(private val assetLoader: WebViewAssetLoader) : WebViewClientCompat() {
    @RequiresApi(21)
    override fun shouldInterceptRequest(
        view: WebView,
        request: WebResourceRequest
    ): WebResourceResponse? {
        return assetLoader.shouldInterceptRequest(request.url)
    }

    // To support API < 21.
    override fun shouldInterceptRequest(
        view: WebView,
        url: String
    ): WebResourceResponse? {
        return assetLoader.shouldInterceptRequest(Uri.parse(url))
    }
}

Java

private static class LocalContentWebViewClient extends WebViewClientCompat {

    private final WebViewAssetLoader mAssetLoader;

    LocalContentWebViewClient(WebViewAssetLoader assetLoader) {
        mAssetLoader = assetLoader;
    }

    @Override
    @RequiresApi(21)
    public WebResourceResponse shouldInterceptRequest(WebView view,
                                     WebResourceRequest request) {
        return mAssetLoader.shouldInterceptRequest(request.getUrl());
    }

    @Override
    @SuppressWarnings("deprecation") // To support API < 21.
    public WebResourceResponse shouldInterceptRequest(WebView view,
                                     String url) {
        return mAssetLoader.shouldInterceptRequest(Uri.parse(url));
    }
}

Twoja aplikacja musi skonfigurować instancję WebViewAssetLoader zgodnie ze swoimi potrzebami. W następnej sekcji znajdziesz przykład.

Tworzenie komponentów i zasobów w aplikacji

WebViewAssetLoader korzysta z instancji PathHandler do wczytywania zasobów odpowiadających danej ścieżce zasobu. Możesz wdrożyć ten interfejs, aby pobierać zasoby odpowiednio do potrzeb aplikacji, ale pakiety biblioteki Webkit AssetsPathHandler i ResourcesPathHandler służą do wczytywania zasobów i zasobów Androida.

Na początek utwórz zasoby i zasoby swojej aplikacji. Ogólnie obowiązują te zasady:

  • Pliki tekstowe, takie jak HTML, JavaScript i CSS, należą do zasobów.
  • Obrazy i inne pliki binarne należą do zasobów.

Aby dodać tekstowe pliki internetowe do projektu:

  1. W Android Studio kliknij prawym przyciskiem myszy folder app > src > main (aplikacja), a następnie wybierz New > Directory (Nowy > Katalog).
    Obraz przedstawiający menu menu tworzenia katalogu w Android Studio
    Rysunek 1. Utwórz folder zasobów dla projektu.
  2. Nazwij folder „assets”.
    Obraz przedstawiający folder komponentów
    Rysunek 2. Nadaj nazwę folderowi zasobów.
  3. Kliknij prawym przyciskiem myszy folder assets, a następnie kliknij Nowy > Plik. Wpisz index.html i naciśnij klawisz Return lub Enter.
  4. Powtórz poprzedni krok, aby utworzyć pusty plik dla stylesheet.css.
  5. Wypełnij puste pliki utworzone przez siebie treściami podanymi w następnych 2 przykładach kodu.
```html
<!-- index.html content -->

<html>
  <head>
    <!-- Tip: Use relative URLs when referring to other in-app content to give
              your app code the flexibility to change the scheme or domain as
              necessary. -->
    <link rel="stylesheet" href="/assets/stylesheet.css">
  </head>
  <body>
    <p>This file is loaded from in-app content.</p>
    <p><img src="/res/drawable/android_robot.png" alt="Android robot" width="100"></p>
  </body>
</html>
```

```css
<!-- stylesheet.css content -->

body {
  background-color: lightblue;
}
```

Aby dodać do projektu plik internetowy oparty na obrazach, wykonaj te czynności:

  1. Pobierz plik Android_symbol_green_RGB.png na swój komputer lokalny.

  2. Zmień nazwę pliku na android_robot.png.

  3. Przenieś ręcznie plik do katalogu main/res/drawable projektu na dysku twardym.

Rysunek 4 przedstawia dodany obraz i tekst z poprzedniego przykładowego kodu wyrenderowany w aplikacji.

Obraz pokazujący dane wyjściowe wyrenderowane przez aplikację
Rysunek 4. Plik HTML i plik graficzny w aplikacji wyrenderowane w aplikacji.

Aby dokończyć proces tworzenia aplikacji, wykonaj te czynności:

  1. Zarejestruj moduły obsługi i skonfiguruj AssetLoader, dodając ten kod do metody onCreate():

    Kotlin

    val assetLoader = WebViewAssetLoader.Builder()
                           .addPathHandler("/assets/", AssetsPathHandler(this))
                           .addPathHandler("/res/", ResourcesPathHandler(this))
                           .build()
    webView.webViewClient = LocalContentWebViewClient(assetLoader)
    

    Java

    final WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
             .addPathHandler("/assets/", new WebViewAssetLoader.AssetsPathHandler(this))
             .addPathHandler("/res/", new WebViewAssetLoader.ResourcesPathHandler(this))
             .build();
    mWebView.setWebViewClient(new LocalContentWebViewClient(assetLoader));
    
  2. Wczytaj treść, dodając ten kod do metody onCreate():

    Kotlin

    webView.loadUrl("https://appassets.androidplatform.net/assets/index.html")
    

    Java

    mWebView.loadUrl("https://appassets.androidplatform.net/assets/index.html");
    

Połącz treści w aplikacji z zasobami z Twojej strony

Aplikacja może wczytać zarówno treści w aplikacji, jak i treści z internetu, np. stronę HTML w aplikacji stylizowaną na kod CSS witryny. WebViewAssetLoader obsługuje ten przypadek użycia. Jeśli żadna z zarejestrowanych instancji PathHandler nie może znaleźć zasobu na danej ścieżce, WebView przełącza się na wczytywanie treści z internetu. Jeśli łączysz zawartość aplikacji z zasobami ze swojej witryny, zarezerwuj ścieżki do katalogu, np. /assets/ lub /resources/, na potrzeby zasobów w aplikacji. Unikaj przechowywania w nich zasobów ze swojej witryny.

Kotlin

val assetLoader = WebViewAssetLoader.Builder()
                        .setDomain("example.com") // Replace this with your website's domain.
                        .addPathHandler("/assets/", AssetsPathHandler(this))
                        .build()

webView.webViewClient = LocalContentWebViewClient(assetLoader)
val inAppHtmlUrl = "https://example.com/assets/index.html"
webView.loadUrl(inAppHtmlUrl)
val websiteUrl = "https://example.com/website/data.json"

// JavaScript code to fetch() content from the same origin.
val jsCode = "fetch('$websiteUrl')" +
        ".then(resp => resp.json())" +
        ".then(data => console.log(data));"

webView.evaluateJavascript(jsCode, null)

Java

final WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
           .setDomain("example.com") // Replace this with your website's domain.
           .addPathHandler("/assets/", new AssetsPathHandler(this))
           .build();

mWebView.setWebViewClient(new LocalContentWebViewClient(assetLoader));
String inAppHtmlUrl = "https://example.com/assets/index.html";
mWebView.loadUrl(inAppHtmlUrl);
String websiteUrl = "https://example.com/website/data.json";

// JavaScript code to fetch() content from the same origin.
String jsCode = "fetch('" + websiteUrl + "')" +
      ".then(resp => resp.json())" +
      ".then(data => console.log(data));";

mWebView.evaluateJavascript(jsCode, null);

W prezentacji WebView na GitHubie znajdziesz przykład strony HTML w aplikacji pobierającej dane JSON hostowane przez internet.

loadDataWithBaseURL

Jeśli Twoja aplikacja musi tylko wczytać stronę HTML i nie musi przechwytywać zasobów podrzędnych, możesz użyć metody loadDataWithBaseURL(), która nie wymaga komponentów z linkiem do aplikacji. Możesz go użyć w sposób przedstawiony w tym przykładowym kodzie:

Kotlin

val html = "<html><body><p>Hello world</p></body></html>"
val baseUrl = "https://example.com/"

webView.loadDataWithBaseURL(baseUrl, html, "text/html", null, baseUrl)

Java

String html = "<html><body><p>Hello world</p></body></html>";
String baseUrl = "https://example.com/";

mWebView.loadDataWithBaseURL(baseUrl, html, "text/html", null, baseUrl);

Wartości argumentów należy dobierać z rozwagą. Weź pod uwagę następujące kwestie:

  • baseUrl: to adres URL, pod którym są wczytywane treści HTML. Musi to być adres URL HTTP(S).
  • data: treść HTML, która ma być wyświetlana w postaci ciągu znaków.
  • mimeType: zazwyczaj należy ustawić wartość text/html.
  • encoding: ta wartość nie jest używana, gdy baseUrl jest adresem URL HTTP(S), więc może mieć wartość null.
  • historyUrl: ustawiona jest ta sama wartość co baseUrl.

Zdecydowanie zalecamy używanie adresu URL HTTP(S) jako adresu baseUrl, ponieważ pomaga to mieć pewność, że aplikacja jest zgodna z zasadami dotyczącymi tego samego źródła.

Jeśli nie możesz znaleźć odpowiedniego parametru baseUrl dla swoich treści i wolisz używać loadData(), musisz zakodować treści za pomocą kodowania procentowego lub standardowego Base64. Zdecydowanie zalecamy wybór kodowania Base64 i korzystanie z interfejsów API Androida do programowego kodowania, jak pokazano w poniższym przykładzie kodu:

Kotlin

val encodedHtml: String = Base64.encodeToString(html.toByteArray(), Base64.NO_PADDING)

webView.loadData(encodedHtml, mimeType, "base64")

Java

String encodedHtml = Base64.encodeToString(html.getBytes(), Base64.NO_PADDING);

mWebView.loadData(encodedHtml, mimeType, "base64");

Czego unikać

Istnieje kilka innych sposobów wczytywania treści w aplikacji, ale zdecydowanie zalecamy korzystanie z nich:

  • Adresy URL file:// i data: są uważane za nieprzejrzyste źródła, co oznacza, że nie można w nich korzystać z zaawansowanych internetowych interfejsów API, takich jak fetch() czy XMLHttpRequest. Funkcja loadData() wewnętrznie używa adresów URL data:, dlatego zalecamy, aby zamiast nich używać WebViewAssetLoader lub loadDataWithBaseURL().
  • Chociaż metody WebSettings.setAllowFileAccessFromFileURLs() i WebSettings.setAllowUniversalAccessFromFileURLs() umożliwiają rozwiązanie problemów z adresami URL file://, odradzamy ustawianie w tych wartościach wartości true, ponieważ w ten sposób aplikacja może być podatna na wykorzystywanie plików. Aby zapewnić najsilniejsze zabezpieczenia, zalecamy jawne ustawienie ich na false na wszystkich poziomach interfejsu API.
  • Z tych samych powodów nie zalecamy stosowania adresów URL file://android_assets/ i file://android_res/. Klasy AssetsHandler i ResourcesHandler powinny być zamiennikami dodawanymi.
  • Unikaj używania właściwości MIXED_CONTENT_ALWAYS_ALLOW. To ustawienie zasadniczo nie jest konieczne i zmniejsza bezpieczeństwo aplikacji. Zalecamy ładowanie treści w aplikacji przy użyciu tego samego schematu (HTTP lub HTTPS) co w przypadku zasobów witryny oraz stosowania odpowiednio MIXED_CONTENT_COMPATIBILITY_MODE lub MIXED_CONTENT_NEVER_ALLOW.