Carregar conteúdo no app

É possível fornecer ao seu app conteúdo baseado na Web, como HTML, JavaScript e CSS, que você compila estaticamente no app em vez de buscar na Internet.

O conteúdo no app não precisa de acesso à Internet nem consome a largura de banda do usuário. Se o conteúdo for projetado especificamente para WebView, ou seja, depender da comunicação com um app nativo, os usuários não poderão carregá-lo acidentalmente em um navegador da Web.

No entanto, o conteúdo no app tem algumas desvantagens. Atualizar o conteúdo baseado na Web exige o envio de uma nova atualização do app. Além disso, existe a possibilidade de conteúdo incompatível entre o que está em um site e o que está no app no dispositivo, caso os usuários tenham versões desatualizadas do app.

WebViewAssetLoader

WebViewAssetLoader é uma maneira flexível e com alto desempenho de carregar conteúdo no app em um objeto WebView. Essa classe é compatível com o seguinte:

  • Carregar conteúdo com um URL HTTP(S) para compatibilidade com a política de mesma origem.
  • Carregando sub-recursos, como JavaScript, CSS, imagens e iframes.

Inclua WebViewAssetLoader no seu arquivo de atividade principal. Confira a seguir um exemplo de carregamento de conteúdo simples da Web da pasta de recursos:

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

Seu app precisa configurar uma instância de WebViewAssetLoader para atender às necessidades. A próxima seção tem um exemplo.

Criar recursos no app

WebViewAssetLoader depende de instâncias PathHandler para carregar recursos correspondentes a um determinado caminho de recurso. Embora você possa implementar essa interface para extrair recursos conforme necessário pelo app, mas a biblioteca Webkit agrupa AssetsPathHandler e ResourcesPathHandler para carregar recursos e recursos Android, respectivamente.

Para começar, crie recursos para o app. Geralmente, o seguinte se aplica:

  • Arquivos de texto como HTML, JavaScript e CSS pertencem aos recursos.
  • As imagens e outros arquivos binários pertencem aos recursos.

Para adicionar arquivos da Web baseados em texto a um projeto, faça o seguinte:

  1. No Android Studio, clique com o botão direito do mouse na pasta app > src > main e escolha New > Directory.
    Uma imagem mostrando menus de criação do diretório do Android Studio
    Figura 1. Crie uma pasta de recursos para seu projeto.
  2. Nomeie a pasta como "Recursos".
    Uma imagem que mostra a pasta de recursos
    Figura 2. Nomeie a pasta de recursos.
  3. Clique com o botão direito do mouse na pasta assets e depois em Novo > Arquivo. Digite index.html e pressione a tecla Return ou Enter.
  4. Repita a etapa anterior para criar um arquivo vazio para a stylesheet.css.
  5. Preencha os arquivos vazios que você criou com o conteúdo nos próximos dois exemplos de código.
```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;
}
```

Para adicionar um arquivo da Web baseado em imagem ao projeto, faça o seguinte:

  1. Faça o download do arquivo Android_symbol_green_RGB.png para sua máquina local.

  2. Renomeie o arquivo como android_robot.png.

  3. Mova manualmente o arquivo para o diretório main/res/drawable do projeto no seu disco rígido.

A Figura 4 mostra a imagem que você adicionou e o texto dos exemplos de código anteriores renderizados em um app.

Uma imagem que mostra a saída renderizada do app
Figura 4. Arquivo HTML e arquivo de imagem renderizados no app.

Para concluir o app, faça o seguinte:

  1. Registre os gerenciadores e configure o AssetLoader adicionando o seguinte código ao método 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. Carregue o conteúdo adicionando o seguinte código ao método onCreate():

    Kotlin

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

    Java

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

Combine o conteúdo no app com os recursos do seu site

Talvez o app precise carregar uma combinação de conteúdo dele e da Internet, como uma página HTML no app estilizada pelo CSS do site. WebViewAssetLoader oferece suporte a esse caso de uso. Se nenhuma das instâncias PathHandler registradas conseguir encontrar um recurso para o caminho especificado, WebView vai voltar ao carregamento de conteúdo da Internet. Se você misturar conteúdo no app com recursos do seu site, reserve caminhos de diretório, como /assets/ ou /resources/, para recursos no app. Evite armazenar recursos do site nesses locais.

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

Confira a demonstração de WebView no GitHub (em inglês) para um exemplo de uma página HTML no app que busca dados JSON hospedados na Web.

loadDataWithBaseURL

Quando o app só precisa carregar uma página HTML e não precisa interceptar subrecursos, use o loadDataWithBaseURL(), que não requer recursos de app. Você pode usá-lo conforme mostrado neste exemplo de código:

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

Escolha os valores do argumento com cuidado. Considere o seguinte:

  • baseUrl: é o URL para o qual seu conteúdo HTML é carregado. Precisa ser um URL HTTP(S).
  • data: é o conteúdo HTML que você quer mostrar, como uma string.
  • mimeType: geralmente precisa ser definido como text/html.
  • encoding: não é utilizado quando baseUrl é um URL HTTP(S). Portanto, ele pode ser definido como null.
  • historyUrl: definido como o mesmo valor que baseUrl.

Recomendamos usar um URL HTTP(S) como baseUrl. Isso ajuda a garantir que seu app obedeça à política de mesma origem.

Se você não encontrar um baseUrl adequado para seu conteúdo e preferir usar loadData(), codifique o conteúdo com codificação por cento ou codificação em Base64. É altamente recomendável escolher a codificação Base64 e usar APIs do Android para codificar isso de forma programática, conforme mostrado neste exemplo de código:

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

O que deve ser evitado

Há várias outras maneiras de carregar conteúdo no app, mas recomendamos que você faça isso:

  • Os URLs file:// e data: são considerados origens opacas, o que significa que não podem aproveitar APIs eficientes da Web, como fetch() ou XMLHttpRequest. A loadData() usa URLs data: internamente. Portanto, recomendamos o uso de WebViewAssetLoader ou loadDataWithBaseURL().
  • Embora WebSettings.setAllowFileAccessFromFileURLs() e WebSettings.setAllowUniversalAccessFromFileURLs() possam contornar os problemas com URLs file://, recomendamos não defini-los como true porque isso deixa seu app vulnerável a explorações baseadas em arquivos. Recomendamos defini-las explicitamente como false em todos os níveis da API para ter mais segurança.
  • Pelos mesmos motivos, recomendamos não usar URLs file://android_assets/ e file://android_res/. As classes AssetsHandler e ResourcesHandler devem ser substituições simples.
  • Evite usar MIXED_CONTENT_ALWAYS_ALLOW. Em geral, essa configuração não é necessária e enfraquece a segurança do seu app. Recomendamos carregar o conteúdo do app no mesmo esquema (HTTP ou HTTPS) dos recursos do seu site e usar MIXED_CONTENT_COMPATIBILITY_MODE ou MIXED_CONTENT_NEVER_ALLOW, conforme apropriado.