Вы можете предоставить веб-контент, например HTML, JavaScript и CSS, для использования в приложении, которое вы статически компилируете в приложение, а не получаете через интернет.
Для работы с контентом в приложении не требуется доступ к интернету, и он не расходует трафик. Если контент предназначен только для WebView, то есть для его работы требуется нативное приложение, пользователи не смогут случайно загрузить его в браузере.
Однако у контента в приложении есть и недостатки. Чтобы обновить веб-контент, нужно выпустить новое обновление приложения. Если пользователи не установят его, контент на сайте и в приложении на устройстве может различаться.
WebViewAssetLoader
WebViewAssetLoader – это гибкий и эффективный способ загрузки контента в приложении в объект WebView. Этот класс поддерживает следующие возможности:
- Загрузка контента с URL-адресом HTTP(S) для совместимости с правилом ограничения домена.
- Загрузка дополнительных ресурсов, таких как JavaScript, CSS, изображения и элементы iframe.
Добавьте WebViewAssetLoader в основной файл activity. Ниже приведен пример загрузки простого веб-контента из папки 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)); } }
Вам нужно настроить экземпляр WebViewAssetLoader в соответствии с требованиями приложения. Пример приведен в следующем разделе.
Создание объектов и ресурсов в приложении
WebViewAssetLoader использует экземпляры PathHandler для загрузки ресурсов, соответствующих заданному пути. Вы можете реализовать этот интерфейс, чтобы получать ресурсы, необходимые вашему приложению, но в библиотеке Webkit есть классы AssetsPathHandler и ResourcesPathHandler для загрузки объектов и ресурсов Android соответственно.
Для начала создайте объекты и ресурсы для приложения. Как правило, действуют следующие правила:
- Текстовые файлы, например HTML, JavaScript и CSS, относятся к объектам.
- Изображения и другие двоичные файлы должны находиться в ресурсах.
Чтобы добавить в проект текстовые веб-файлы, выполните следующие действия:
- В Android Studio нажмите правой кнопкой мыши на папку app > src > main и выберите New > Directory (Создать > Каталог).
Рисунок 1. Создайте папку assets для проекта. - Назовите ее assets.
Рисунок 2. Укажите название папки. - Нажмите правой кнопкой мыши на папку assets и выберите New (Создать) > File (Файл).
Введите
index.htmlи нажмите клавишу Return или Ввод.
Рисунок 3. Создайте файл index.html. - Повторите предыдущий шаг, чтобы создать пустой файл для
stylesheet.css. - Заполните созданные пустые файлы содержимым из следующих двух примеров кода.
```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;
}
```
Чтобы добавить в проект веб-файл на основе изображения, выполните следующие действия:
Скачайте файл
Android_symbol_green_RGB.pngна локальный компьютер.Переименуйте файл в
android_robot.png.Вручную переместите файл в каталог
main/res/drawableпроекта на жестком диске.
На рисунке 4 показано изображение, которое вы добавили, и текст из предыдущих примеров кода, отображаемых в приложении.
Чтобы завершить создание приложения, выполните следующие действия:
Зарегистрируйте обработчики и настройте
AssetLoader, добавив в метод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));
Загрузите контент, добавив следующий код в метод
onCreate():Kotlin
webView.loadUrl("https://appassets.androidplatform.net/assets/index.html")
Java
mWebView.loadUrl("https://appassets.androidplatform.net/assets/index.html");
Объединяйте контент в приложении с ресурсами на сайте
Приложению может потребоваться загрузить как контент в приложении, так и контент из интернета, например HTML-страницу в приложении, оформленную с помощью CSS вашего сайта.
WebViewAssetLoader поддерживает этот вариант использования. Если ни один из зарегистрированных экземпляров PathHandler не может найти ресурс по указанному пути, WebView загружает контент из интернета. Если вы смешиваете контент в приложении с ресурсами сайта, зарезервируйте пути к каталогам, например /assets/ или /resources/, для ресурсов в приложении. Не храните в них ресурсы своего сайта.
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);
Пример HTML-страницы в приложении, которая получает данные JSON, размещенные в интернете, можно найти в демоверсии WebView на GitHub.
loadDataWithBaseURL
Если вашему приложению нужно только загрузить HTML-страницу и не нужно перехватывать подресурсы, используйте loadDataWithBaseURL(). В этом случае объекты приложения не требуются. Пример использования приведен ниже.
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);
Тщательно выбирайте значения аргументов. Вот несколько советов:
baseUrl– URL, по которому загружается HTML-контент. Это должен быть URL HTTP(S).data– HTML-контент, который вы хотите показать, в виде строки.mimeType: обычно должно быть задано значениеtext/html.encoding: не используется, еслиbaseUrl– это URL HTTP(S), поэтому можно задать значениеnull.historyUrl: задается то же значение, что и дляbaseUrl.
Мы настоятельно рекомендуем использовать в качестве baseUrl URL-адрес HTTP(S), так как это помогает обеспечить соответствие вашего приложения правилу ограничения домена.
Если вы не можете найти подходящий тип baseUrl для своего контента и предпочитаете использовать loadData(), то вам необходимо закодировать контент с помощью процентного кодирования или кодирования Base64.
Мы настоятельно рекомендуем выбрать кодировку Base64 и использовать Android API, чтобы закодировать данные программно, как показано в примере ниже.
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");
Чего следует избегать
Есть и другие способы загрузки контента в приложении, но мы настоятельно не рекомендуем их использовать.
- URL-адреса
file://иdata:считаются непрозрачными источниками, то есть они не могут использовать мощные веб-API, такие какfetch()илиXMLHttpRequest.loadData()использует URLdata:, поэтому мы рекомендуем использоватьWebViewAssetLoaderилиloadDataWithBaseURL(). - Хотя
WebSettings.setAllowFileAccessFromFileURLs()иWebSettings.setAllowUniversalAccessFromFileURLs()могут устранить проблемы с URL-адресамиfile://, мы не рекомендуем устанавливать для них значениеtrue, поскольку это делает ваше приложение уязвимым для эксплойтов на основе файлов. Для обеспечения максимальной безопасности рекомендуем явно задать для них значениеfalseна всех уровнях API. - По тем же причинам мы не рекомендуем использовать URL-адреса
file://android_assets/иfile://android_res/. КлассыAssetsHandlerиResourcesHandlerпредназначены для замены. - Не используйте
MIXED_CONTENT_ALWAYS_ALLOW. Как правило, в этом нет необходимости, и это снижает безопасность приложения. Рекомендуем загружать контент в приложении по тому же протоколу (HTTP или HTTPS), что и ресурсы сайта, и использоватьMIXED_CONTENT_COMPATIBILITY_MODEилиMIXED_CONTENT_NEVER_ALLOW.