Puedes proporcionar contenido basado en la Web, como HTML, JavaScript y CSS, para que tu app use tu compilación estática en la app en lugar de recuperarla a través de Internet.
El contenido integrado en la app no requiere acceso a Internet ni consume el ancho de banda del usuario. Si el contenido está diseñado solo para WebView
(es decir, depende de la comunicación con una app nativa), los usuarios no podrán cargarlo accidentalmente en un navegador web.
Sin embargo, el contenido integrado en la app tiene algunas desventajas. La actualización del contenido basado en la Web requiere el envío de una nueva actualización de la app, y, si los usuarios tienen versiones desactualizadas de la app, existe la posibilidad de que haya contenido incoherente entre el contenido de un sitio web y el de la app del dispositivo.
WebViewAssetLoader
WebViewAssetLoader
es una forma flexible y eficaz de cargar contenido integrado en la app en un objeto WebView
. Esta clase admite lo siguiente:
- Carga contenido con una URL HTTP(S) para brindar compatibilidad con la política del mismo origen
- Carga de subrecursos, como JavaScript, CSS, iframes y imágenes
Incluye WebViewAssetLoader
en tu archivo de actividad principal. El siguiente es un ejemplo de carga de contenido web simple desde la carpeta de elementos:
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)); } }
Tu app debe configurar una instancia de WebViewAssetLoader
para satisfacer sus necesidades. La siguiente sección tiene un ejemplo.
Cómo crear recursos y recursos integrados en la app
WebViewAssetLoader
se basa en instancias de PathHandler
para cargar los recursos correspondientes a una ruta de recurso determinada. Si bien puedes implementar esta interfaz para recuperar recursos según sea necesario por tu app, la biblioteca de Webkit empaqueta AssetsPathHandler
y ResourcesPathHandler
para cargar recursos y recursos de Android, respectivamente.
Para comenzar, crea elementos y recursos para tu app. En general, se aplica lo siguiente:
- Los archivos de texto, como HTML, JavaScript y CSS, pertenecen a los recursos.
- Las imágenes y otros archivos binarios pertenecen a los recursos.
Para agregar archivos web basados en texto a un proyecto, haz lo siguiente:
- En Android Studio, haz clic con el botón derecho en la carpeta app > src > main y selecciona New > Directory.
- Asígnele el nombre "assets" a la carpeta.
- Haz clic con el botón derecho en la carpeta assets y selecciona New > File.
Ingresa
index.html
y presiona la tecla Retorno o Intro. - Repite el paso anterior a fin de crear un archivo vacío para
stylesheet.css
. - Completa los archivos vacíos que creaste con el contenido de las siguientes dos muestras 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 agregar un archivo web basado en imágenes a tu proyecto, haz lo siguiente:
Descarga el archivo
Android_symbol_green_RGB.png
a tu máquina local.Cambia el nombre del archivo a
android_robot.png
.Mueve el archivo de forma manual al directorio
main/res/drawable
del proyecto en el disco duro.
En la Figura 4, se muestra la imagen que agregaste y el texto de las muestras de código anteriores renderizadas en una app.
Para completar la app, haz lo siguiente:
Para registrar los controladores y configurar
AssetLoader
, agrega el siguiente código al métodoonCreate()
: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));
Para cargar el contenido, agrega el siguiente código al método
onCreate()
:Kotlin
webView.loadUrl("https://appassets.androidplatform.net/assets/index.html")
Java
mWebView.loadUrl("https://appassets.androidplatform.net/assets/index.html");
Combina el contenido de la app con recursos de tu sitio web
Es posible que tu app necesite cargar una combinación de contenido integrado en ella y contenido de Internet, como una página HTML en la app con el estilo del CSS de tu sitio web.
WebViewAssetLoader
admite este caso de uso. Si ninguna de las instancias de PathHandler
registradas puede encontrar un recurso para la ruta determinada, WebView
recurre a la carga de contenido de Internet. Si combinas contenido en la app con recursos de tu sitio web, reserva rutas de acceso a directorios, como /assets/
o /resources/
, para los recursos de la app. Evita almacenar recursos de tu sitio web en esas ubicaciones.
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 demostración de WebView
en GitHub para ver un ejemplo de una página HTML en la app que recupera datos JSON alojados en la Web.
loadDataWithBaseURL
Si tu app solo necesita cargar una página HTML y no necesita interceptar subrecursos, considera usar loadDataWithBaseURL()
, que no requiere recursos de app. Puedes usarlo como se indica en la siguiente muestra 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);
Elige los valores de los argumentos con cuidado. Ten en cuenta lo siguiente:
baseUrl
: Esta es la URL como se carga tu contenido HTML. Debe ser una URL HTTP(S).data
: Este es el contenido HTML que deseas mostrar, como una cadena.mimeType
: Por lo general, se debe establecer entext/html
.encoding
: No se usa cuandobaseUrl
es una URL HTTP(S), por lo que se puede configurar comonull
.historyUrl
: Se establece con el mismo valor quebaseUrl
.
Te recomendamos que uses una URL HTTP(S) como baseUrl
, ya que esto ayuda a garantizar que tu app cumpla con la política del mismo origen.
Si no encuentras un baseUrl
adecuado para tu contenido y prefieres usar loadData()
, debes codificar el contenido con codificación por ciento o codificación Base64.
Te recomendamos que elijas la codificación Base64 y uses las APIs de Android para codificarla de manera programática, como se muestra en la siguiente muestra 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");
Elementos que debes evitar
Existen varias otras formas de cargar contenido integrado en la app, pero te recomendamos que las uses:
- Se considera que las URLs
file://
y las URLsdata:
son orígenes opacos, lo que significa que no pueden aprovechar APIs web potentes comofetch()
oXMLHttpRequest
.loadData()
usa URLsdata:
de forma interna, por lo que te recomendamos que usesWebViewAssetLoader
oloadDataWithBaseURL()
en su lugar. - Si bien
WebSettings.setAllowFileAccessFromFileURLs()
yWebSettings.setAllowUniversalAccessFromFileURLs()
pueden solucionar los problemas con las URLs defile://
, te recomendamos que no las configures comotrue
, ya que esto dejará a tu app vulnerable a vulnerabilidades basadas en archivos. Te recomendamos configurarlas de forma explícita comofalse
en todos los niveles de API para obtener la mayor seguridad. - Por los mismos motivos, recomendamos no usar las URLs
file://android_assets/
yfile://android_res/
. Las clasesAssetsHandler
yResourcesHandler
están pensadas para ser reemplazos directos. - Evita usar
MIXED_CONTENT_ALWAYS_ALLOW
. Por lo general, esta configuración no es necesaria y debilita la seguridad de tu app. Te recomendamos que cargues el contenido integrado en la app en el mismo esquema (HTTP o HTTPS) como los recursos de tu sitio web y que usesMIXED_CONTENT_COMPATIBILITY_MODE
oMIXED_CONTENT_NEVER_ALLOW
, según corresponda.