In-App-Inhalte laden

Sie können webbasierte Inhalte wie HTML, JavaScript und CSS bereitstellen, damit Ihre Anwendung diese statisch in die App kompiliert, anstatt sie über das Internet abzurufen.

Für In-App-Inhalte ist kein Internetzugriff erforderlich und es wird keine Bandbreite in Anspruch genommen. Wenn die Inhalte speziell für WebView entwickelt wurden – das heißt, dass sie von der Kommunikation mit einer nativen App abhängig sind –, können Nutzer sie nicht versehentlich in einem Webbrowser laden.

In-App-Inhalte haben jedoch auch Nachteile. Wenn du webbasierte Inhalte aktualisieren möchtest, musst du ein neues App-Update veröffentlichen. Außerdem besteht die Möglichkeit, dass Inhalte auf einer Website und in der App auf deinem Gerät nicht übereinstimmen, wenn Nutzer veraltete App-Versionen haben.

WebViewAssetLoader

WebViewAssetLoader ist eine flexible und leistungsfähige Möglichkeit, In-App-Inhalte in ein WebView-Objekt zu laden. Diese Klasse unterstützt Folgendes:

  • Laden von Inhalten mit einer HTTP(S)-URL aus Gründen der Kompatibilität mit der Same-Origin-Policy.
  • Laden von untergeordneten Ressourcen wie JavaScript, CSS, Bildern und iFrames

Füge WebViewAssetLoader in deine Hauptaktivitätsdatei ein. Im Folgenden finden Sie ein Beispiel für das Laden einfacher Webinhalte aus dem Ordner „Assets“:

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));
    }
}

Ihre Anwendung muss entsprechend ihren Anforderungen eine WebViewAssetLoader-Instanz konfigurieren. Der nächste Abschnitt enthält ein Beispiel.

In-App-Assets und -Ressourcen erstellen

WebViewAssetLoader benötigt PathHandler-Instanzen, um Ressourcen zu laden, die einem bestimmten Ressourcenpfad entsprechen. Sie können diese Schnittstelle zwar implementieren, um Ressourcen nach Bedarf für Ihre App abzurufen, aber die Webkit-Bibliothek Bundles AssetsPathHandler und ResourcesPathHandler zum Laden von Android-Assets bzw. -Ressourcen.

Erstellen Sie zuerst Assets und Ressourcen für Ihre Anwendung. Im Allgemeinen gilt Folgendes:

  • Textdateien wie HTML, JavaScript und CSS gehören zu Assets.
  • Bilder und andere Binärdateien gehören in die Ressourcen.

So fügen Sie einem Projekt textbasierte Webdateien hinzu:

  1. Klicken Sie in Android Studio mit der rechten Maustaste auf den Ordner app > src > main und wählen Sie New > Directory aus.
    Bild, das die Menüs „Verzeichnis erstellen“ in Android Studio zeigt
    Abbildung 1: Erstellen Sie einen Asset-Ordner für Ihr Projekt.
  2. Nennen Sie den Ordner „Assets“.
    Ein Bild, auf dem der Asset-Ordner zu sehen ist
    Abbildung 2. Benennen Sie den Ordner „Assets“.
  3. Klicken Sie mit der rechten Maustaste auf den Ordner Assets und dann auf Neu > Datei. Geben Sie index.html ein und drücken Sie die Eingabetaste oder die Eingabetaste.
  4. Wiederholen Sie den vorherigen Schritt, um eine leere Datei für stylesheet.css zu erstellen.
  5. Füllen Sie die leeren Dateien, die Sie erstellt haben, mit den Inhalten aus den nächsten beiden Codebeispielen aus.
```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;
}
```

So fügen Sie Ihrem Projekt eine bildbasierte Webdatei hinzu:

  1. Laden Sie die Datei Android_symbol_green_RGB.png auf Ihren lokalen Computer herunter.

  2. Benennen Sie die Datei in android_robot.png um.

  3. Verschieben Sie die Datei manuell in das main/res/drawable-Verzeichnis Ihres Projekts auf Ihrer Festplatte.

Abbildung 4 zeigt das von Ihnen hinzugefügte Bild und den Text aus den vorherigen Codebeispielen, der in einer App gerendert wurde.

Ein Bild, das eine von einer App gerenderte Ausgabe zeigt
Abbildung 4: In-App-HTML-Datei und Bilddatei, die in einer App gerendert werden

So stellen Sie die App fertig:

  1. Registrieren Sie die Handler und konfigurieren Sie AssetLoader, indem Sie der Methode onCreate() den folgenden Code hinzufügen:

    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. Laden Sie den Inhalt, indem Sie der Methode onCreate() den folgenden Code hinzufügen:

    Kotlin

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

    Java

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

In-App-Inhalte mit Ressourcen von Ihrer Website kombinieren

Ihre App muss möglicherweise eine Mischung aus In-App-Inhalten und Inhalten aus dem Internet laden, z. B. eine In-App-HTML-Seite, die mit dem CSS Ihrer Website gestaltet wurde. WebViewAssetLoader unterstützt diesen Anwendungsfall. Wenn keine der registrierten PathHandler-Instanzen eine Ressource für den angegebenen Pfad finden kann, greift WebView auf das Laden von Inhalten aus dem Internet zurück. Wenn Sie In-App-Inhalte mit Ressourcen von Ihrer Website mischen, reservieren Sie Verzeichnispfade wie /assets/ oder /resources/ für In-App-Ressourcen. Speichern Sie an diesen Orten keine Ressourcen von Ihrer Website.

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);

In der WebView-Demo auf GitHub finden Sie ein Beispiel für eine In-App-HTML-Seite, die im Web gehostete JSON-Daten abruft.

LoadDataWithBaseURL

Wenn Ihre Anwendung nur eine HTML-Seite laden und keine Unterressourcen abfangen muss, sollten Sie loadDataWithBaseURL() verwenden, für das keine App-Assets erforderlich sind. Sie können es wie im folgenden Codebeispiel gezeigt verwenden:

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);

Wählen Sie Argumentwerte sorgfältig aus. Hier einige Tipps:

  • baseUrl: Das ist die URL, unter der Ihre HTML-Inhalte geladen werden. Dies muss eine HTTP(S)-URL sein.
  • data: Dies ist der HTML-Inhalt, der als String angezeigt werden soll.
  • mimeType: Muss normalerweise auf text/html festgelegt werden.
  • encoding: Wird nicht verwendet, wenn baseUrl eine HTTP(S)-URL ist, kann sie also auf null festgelegt werden.
  • historyUrl: Wird auf denselben Wert wie baseUrl festgelegt.

Wir empfehlen dringend, eine HTTP(S)-URL als baseUrl zu verwenden, damit Ihre App der Richtlinie für denselben Ursprung entspricht.

Wenn Sie keinen geeigneten baseUrl für Ihren Inhalt finden und lieber loadData() verwenden möchten, müssen Sie den Inhalt mit Prozentcodierung oder Base64-Codierung codieren. Wir empfehlen dringend, die Base64-Codierung zu wählen und Android APIs zu verwenden, um dies programmatisch zu codieren, wie im folgenden Codebeispiel gezeigt:

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");

Was du vermeiden solltest

Es gibt mehrere andere Möglichkeiten, In-App-Inhalte zu laden, die wir Ihnen jedoch ausdrücklich empfehlen:

  • file://-URLs und data:-URLs gelten als opake Ursprünge, d. h. sie können keine leistungsstarken Web-APIs wie fetch() oder XMLHttpRequest nutzen. loadData() verwendet intern data:-URLs. Wir empfehlen daher, stattdessen WebViewAssetLoader oder loadDataWithBaseURL() zu verwenden.
  • Obwohl WebSettings.setAllowFileAccessFromFileURLs() und WebSettings.setAllowUniversalAccessFromFileURLs() Probleme mit file://-URLs umgehen können, raten wir davon ab, sie auf true zu setzen, da Ihre Anwendung dann anfällig für dateibasierte Exploits wird. Für höchste Sicherheit empfehlen wir, sie auf allen API-Ebenen explizit auf false festzulegen.
  • Aus denselben Gründen raten wir von file://android_assets/- und file://android_res/-URLs ab. Die Klassen AssetsHandler und ResourcesHandler sind als Drop-in-Ersatzwerte gedacht.
  • Vermeiden Sie die Verwendung von MIXED_CONTENT_ALWAYS_ALLOW. Diese Einstellung ist in der Regel nicht erforderlich und beeinträchtigt die Sicherheit Ihrer App. Wir empfehlen, Ihre In-App-Inhalte über das gleiche Schema (HTTP oder HTTPS) wie die Ressourcen Ihrer Website zu laden und je nach Bedarf MIXED_CONTENT_COMPATIBILITY_MODE oder MIXED_CONTENT_NEVER_ALLOW zu verwenden.