Carica contenuti in-app

Puoi fornire contenuti basati sul web, ad esempio HTML, JavaScript e CSS, da utilizzare per la tua app e che hai compilato in modo statico nell'app anziché recuperarli tramite internet.

I contenuti in-app non richiedono l'accesso a Internet né consumano la larghezza di banda dell'utente. Se i contenuti sono progettati specificamente solo per WebView, ovvero dipendono dalla comunicazione con un'app nativa, gli utenti non potranno caricarli accidentalmente in un browser web.

Tuttavia, i contenuti in-app presentano alcuni svantaggi. L'aggiornamento di contenuti basati sul web richiede l'invio di un nuovo aggiornamento dell'app ed esiste la possibilità che i contenuti non corrispondano tra i contenuti su un sito web e quelli nell'app sul dispositivo se gli utenti hanno versioni dell'app obsolete.

WebViewAssetLoader

WebViewAssetLoader è un modo flessibile ed efficace per caricare contenuti in-app in un oggetto WebView. Questo corso supporta quanto segue:

  • Caricamento di contenuti con un URL HTTP(S) per la compatibilità con il criterio same-origin.
  • Caricamento di risorse secondarie come JavaScript, CSS, immagini e iframe.

Includi WebViewAssetLoader nel file delle attività principale. Di seguito è riportato un esempio di caricamento di contenuti web semplici dalla cartella degli asset:

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

La tua app deve configurare un'istanza WebViewAssetLoader in base alle sue esigenze. La sezione successiva contiene un esempio.

Creare asset e risorse in-app

WebViewAssetLoader si basa sulle istanze PathHandler per caricare le risorse corrispondenti a un determinato percorso della risorsa. Sebbene sia possibile implementare questa interfaccia per recuperare le risorse in base alle esigenze della tua app, i bundle della libreria Webkit AssetsPathHandler e ResourcesPathHandler per caricare rispettivamente asset e risorse Android.

Per iniziare, crea asset e risorse per la tua app. In genere, si applica quanto segue:

  • I file di testo come HTML, JavaScript e CSS appartengono agli asset.
  • Le immagini e altri file binari appartengono alle risorse.

Per aggiungere file web basati su testo a un progetto:

  1. In Android Studio, fai clic con il pulsante destro del mouse sulla cartella app > src > principale, quindi scegli Nuovo > Directory.
    Un&#39;immagine che mostra i menu di creazione della directory di Android Studio
    Figura 1. Crea una cartella di asset per il tuo progetto.
  2. Assegna alla cartella il nome "assets".
    Un&#39;immagine che mostra la cartella degli asset
    Figura 2. Assegna un nome alla cartella degli asset.
  3. Fai clic con il pulsante destro del mouse sulla cartella assets, quindi fai clic su Nuovo > File. Inserisci index.html e premi il tasto Invio o Invio.
  4. Ripeti il passaggio precedente per creare un file vuoto per stylesheet.css.
  5. Compila i file vuoti che hai creato con i contenuti nei prossimi due esempi di codice.
```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;
}
```

Per aggiungere un file web basato su immagini al tuo progetto:

  1. Scarica il file Android_symbol_green_RGB.png sulla tua macchina locale.

  2. Rinomina il file come android_robot.png.

  3. Sposta manualmente il file nella directory main/res/drawable del progetto sul disco rigido.

La Figura 4 mostra l'immagine che hai aggiunto e il testo degli esempi di codice precedenti visualizzato in un'app.

Un&#39;immagine che mostra l&#39;output del rendering dell&#39;app
Figura 4. File HTML e file immagine in-app visualizzati in un'app.

Per completare l'app:

  1. Registra i gestori e configura AssetLoader aggiungendo il seguente codice al metodo 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. Carica i contenuti aggiungendo il codice seguente al metodo onCreate():

    Kotlin

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

    Java

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

Combina i contenuti in-app con le risorse del tuo sito web

La tua app potrebbe dover caricare una combinazione di contenuti in-app e contenuti da internet, ad esempio una pagina HTML in-app definita dal CSS del tuo sito web. WebViewAssetLoader supporta questo caso d'uso. Se nessuna delle istanze PathHandler registrate riesce a trovare una risorsa per il percorso specificato, WebView torna a caricare i contenuti da internet. Se combini contenuti in-app con risorse del tuo sito web, prenota percorsi della directory, come /assets/ o /resources/, per le risorse in-app. Evita di archiviare risorse del tuo sito web in quelle posizioni.

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

Consulta la demo di WebView su GitHub per un esempio di pagina HTML in-app che recupera dati JSON ospitati sul web.

LoadDataWithBaseURL

Se la tua app deve caricare solo una pagina HTML e non deve intercettare le risorse secondarie, valuta la possibilità di utilizzare loadDataWithBaseURL(), che non richiede asset per app. Puoi utilizzarlo come mostrato nel seguente esempio di codice:

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

Scegli con attenzione i valori degli argomenti. Tieni in considerazione:

  • baseUrl: questo è l'URL con cui vengono caricati i contenuti HTML. Deve essere un URL HTTP(S).
  • data: si tratta del contenuto HTML che vuoi visualizzare sotto forma di stringa.
  • mimeType: di solito deve essere impostato su text/html.
  • encoding: non è utilizzato quando baseUrl è un URL HTTP(S), quindi può essere impostato su null.
  • historyUrl: è impostato sullo stesso valore di baseUrl.

Ti consigliamo vivamente di utilizzare un URL HTTP(S) come baseUrl, poiché ciò contribuisce a garantire che la tua app rispetti le norme relative alla stessa origine.

Se non riesci a trovare un baseUrl adatto ai tuoi contenuti e preferisci utilizzare loadData(), devi codificare i contenuti con la codifica a percentuale o la codifica Base64. Ti consigliamo vivamente di scegliere la codifica Base64 e di utilizzare le API Android per codificarla in modo programmatico, come illustrato nel seguente esempio di codice:

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

Cose da evitare

Esistono molti altri modi per caricare contenuti in-app, che ti consigliamo vivamente di utilizzare:

  • Gli URL file:// e data: sono considerati origini opache, ossia non possono usufruire di potenti API web come fetch() o XMLHttpRequest. loadData() utilizza internamente data: URL, quindi ti invitiamo a usare WebViewAssetLoader o loadDataWithBaseURL().
  • Anche se WebSettings.setAllowFileAccessFromFileURLs() e WebSettings.setAllowUniversalAccessFromFileURLs() possono risolvere i problemi relativi agli URL file://, ti consigliamo di non impostarli su true perché così facendo la tua app diventa vulnerabile agli exploit basati su file. Ti consigliamo di impostarli esplicitamente su false in tutti i livelli API per la massima sicurezza.
  • Per gli stessi motivi, sconsigliamo di utilizzare URL file://android_assets/ e file://android_res/. Le classi AssetsHandler e ResourcesHandler sono pensate come sostituzioni automatiche.
  • Evita di utilizzare MIXED_CONTENT_ALWAYS_ALLOW. Questa impostazione generalmente non è necessaria e indebolisce la sicurezza della tua app. Ti consigliamo di caricare i contenuti in-app utilizzando lo stesso schema (HTTP o HTTPS) delle risorse del tuo sito web e di utilizzare MIXED_CONTENT_COMPATIBILITY_MODE o MIXED_CONTENT_NEVER_ALLOW, a seconda dei casi.