Задержка навигации — критически важный показатель для удобства пользователя. Чтобы помочь разработчикам уменьшить эту задержку, WebView предоставляет API для спекулятивной загрузки, позволяя вашему приложению получать или отображать контент до того, как пользователь явно перейдет к нему.
WebView поддерживает три основных типа спекулятивной загрузки: Preconnect, Prefetch и Prerender.
Внедрение стратегии спекулятивной загрузки позволяет достичь следующих результатов:
- Значительное сокращение задержки загрузки веб-контента: перенос времени запуска сети на более ранний этап жизненного цикла приложения.
- Более высокая вероятность успешной навигации: предварительная подготовка сети и кэша снижает вероятность сбоев навигации из-за временных проблем с сетью.
- Улучшенная воспринимаемая скорость отклика: предварительная отрисовка, в частности, обеспечивает мгновенные переходы, благодаря которым приложение ощущается значительно быстрее.
Выберите стратегию спекулятивной загрузки
Ключевое различие между этими стратегиями заключается в их масштабе: API Preconnect основан на источнике, то есть ему требуется только целевой домен. API Prefetch и Prerender основаны на URL-адресе, то есть им требуется точный путь к веб-странице.
Поскольку функция Preconnect работает на уровне источника, ее можно запустить гораздо раньше в жизненном цикле приложения, даже до того, как станет известно, на какой именно контент или страницу перейдет пользователь.
В приведенной ниже таблице сравниваются эти три стратегии, чтобы помочь вам выбрать наиболее подходящую для вашего случая:
| Особенность | Предварительное подключение | Предварительная загрузка | Предварительная отрисовка |
|---|---|---|---|
| Основная цель | Разогрейте соединение. | Кэшировать только HTML (без JavaScript и CSS) | Предварительно отрендерить всю страницу |
| Объем | Уровень профиля (общий для всех WebViews) | Уровень профиля (общий для всех WebViews) | Уровень WebView (привязанный к конкретному WebView) |
| API Jetpack WebKit | androidx.webkit.Profile | androidx.webkit.Profile | androidx.webkit.WebViewCompat |
| Основные методы API | preconnect(...) | prefetchUrlAsync(...) | prerenderUrlAsync(...) |
| Конфигурация | Н/Д | PrefetchCache.setMaxPrefetches()PrefetchCache.setPrefetchTtlSeconds() | setMaxPrerenders() |
| Использование ресурсов | Низкий (сеть) | Среда (сеть, память) | Высокий уровень (процессор, память, сеть) |
| Когда использовать | Когда источник целевого объекта известен, но конкретный URL-адрес еще не определен. | Когда точный URL-адрес известен и навигация вероятна, а кэширование используется совместно всеми WebView. | Когда известен точный URL-адрес и навигация в рамках конкретного WebView является максимально надежной. |
| Преимущества | Более быстрая установка соединения для любого URL-адреса на исходном сервере. | Более быстрая загрузка сети для соответствующих URL-адресов. | Мгновенная навигация сразу после активации. |
Установите связь с истоками
Предварительное подключение ускоряет будущие загрузки за счет заблаговременного выполнения DNS-запросов и установления TCP/TLS-соединения для указанного источника.
В отличие от Prefetch и Prerender, которые требуют указания точного URL-адреса назначения, Preconnect работает исключительно на основе источника. Это позволяет выполнить вызов Preconnect гораздо раньше, чем Prefetch и Prerender.
Эта стратегия, требующая минимальных ресурсов и реализуемая на уровне профиля, снижает начальную задержку для любого WebView, использующего этот профиль, при условии, что исходный ресурс еще не был посещен. Соединение остается открытым примерно 30 секунд, что положительно сказывается на последующих HTTP-запросах, навигации или подресурсах между источниками, исключая накладные расходы на установление соединения.
Выполнение
Для инициирования предварительного подключения вызовите метод preconnect(String url) для экземпляра Profile . Этот API-функция должна вызываться в потоке пользовательского интерфейса и требует поддержки ` WebViewFeature.PRECONNECT .
API работает непосредственно с источником, но для удобства можно указать полный URL-адрес (например, https://www.example.com/index.html ). Это автоматически рассматривается как вызов к источнику (например, https://www.example.com ). Можно подключиться к нескольким источникам, вызывая этот API несколько раз.
Котлин
// Must be called on the @UiThread
if (WebViewFeature.isFeatureSupported(WebViewFeature.PRECONNECT)) {
profile.preconnect("https://www.example.com/index.html")
// This initiates a connection to the origin https://www.example.com
}
Java
// Must be called on the @UiThread
if (WebViewFeature.isFeatureSupported(WebViewFeature.PRECONNECT)) {
profile.preconnect("https://www.example.com/index.html");
// This initiates a connection to the origin https://www.example.com
}
Общие параметры конфигурации: PrefetchParameters и PrerenderParameters
Как Prefetch, так и Prerender используют PrefetchParameters или PrerenderParameters для настройки запроса. Эти классы позволяют указывать дополнительные заголовки и подсказки для сопоставления URL-адресов, например, параметры No-Vary-Search .
Котлин
// Isolated configuration specifically for Cache-Level Prefetching
val prefetchParams = PrefetchParameters.Builder()
.addAdditionalHeader("X-Custom-Client", "Android-App-V2")
.setExpectedNoVarySearchHeader(
NoVarySearchHeader.varyExcept(true, listOf("session_id", "click_ref"))
)
.build()
Java
PrefetchParameters prefetchParams = new PrefetchParameters.Builder()
.addAdditionalHeader("X-Custom-Header", "value")
/**
* Hint to ignore specific query parameters during cache matching.
* This allows the cache to match even if the tracking_id differs.
*/
.setExpectedNoVarySearchHeader(
NoVarySearchHeader.varyExcept(true, Arrays.asList("tracking_id"))
)
/**
* Determines if Client Hints are sent.
* NOTE: This is ignored for Prerendering API requests, which default to
* the WebView's WebSettings.getJavaScriptEnabled() value.
*/
.setJavaScriptEnabled(true)
.build();
Предварительная загрузка контента
Предварительная загрузка (prefetching) загружает основной HTML-ресурс URL-адреса и сохраняет его в сетевом кэше профиля. В WebView Profile выступает в качестве контейнера для данных браузера, включая файлы cookie, HTTP-кэш и сервис-воркеры. Поскольку предварительная загрузка является операцией уровня профиля, любой WebView связанный с этим профилем, может использовать кэшированный ответ.
Выполнение
Для запуска предварительной загрузки вызовите метод prefetchUrlAsync() для экземпляра Profile . Эта операция поддерживает только схему HTTPS.
Котлин
profile.prefetchUrlAsync(
url,
prefetchParams,
cancellationSignal,
executor,
object : WebViewOutcomeReceiver<PrefetchResult, PrefetchException> {
override fun onResult(result: PrefetchResult) {
if (result.wasDuplicate()) {
// URL and No-Vary-Search permutations already exist in the cache layer
} else {
// The HTML payload has been successfully secured in the HTTP cache
}
}
override fun onError(error: PrefetchException) {
when (error) {
is PrefetchNetworkException -> {
// Isolates network layer or server-side HTTP anomalies
val code = error.httpStatusCode
// Facilitates rapid diagnosis of 4xx or 5xx server responses
}
else -> {
// Catches generalized execution failures and system constraints
}
}
}
}
)
Java
profile.prefetchUrlAsync(
url,
prefetchParams,
cancellationSignal,
executor,
new WebViewOutcomeReceiver<PrefetchResult, PrefetchException>() {
@Override
public void onResult(PrefetchResult result) {
if (result.wasDuplicate()) {
// URL and No-Vary-Search permutations already exist in the cache layer
} else {
// The HTML payload has been successfully secured in the HTTP cache
}
}
@Override
public void onError(PrefetchException error) {
if (error instanceof PrefetchNetworkException) {
// Isolates network layer or server-side HTTP anomalies
int code = ((PrefetchNetworkException) error).httpStatusCode;
// Facilitates rapid diagnosis of 4xx or 5xx server responses
} else {
// Catches generalized execution failures and system constraints
}
}
}
);
Жизненный цикл перехвата
Запрос предварительной загрузки WebView изменяет момент и способ срабатывания коллбэка shouldInterceptRequest() . Поскольку это напрямую влияет на успешность использования предварительно загруженного контента, крайне важно понимать двухэтапный жизненный цикл:

1. Спекулятивная фаза (запрос предварительной выборки)
При вызове prefetchUrlAsync() WebView загружает основной HTML-ресурс в фоновом режиме. shouldInterceptRequest() полностью пропускается для этого фонового запроса. Любая пользовательская логика, токены авторизации или внедрение заголовков, обычно обрабатываемые внутри вашего перехватчика, не применяются к предварительно загруженному HTML-ресурсу.
2. Этап навигации (активация пользователя)
Когда приложение явно переходит по URL-адресу (например, используя loadUrl() ) или пользователь щелкает по соответствующей ссылке, WebView определяет, может ли он использовать предварительно загруженный кэш:
Основная оценка HTML: В этот момент WebView вызовет
shouldInterceptRequest()для основного HTML. Для успешной отправки страницы из предварительного кэша ваш перехватчик должен возвращатьnull. Если вы возвращаете пользовательскийWebResourceResponse, WebView учитывает ваш перехватчик и полностью обходит предварительный кэш.Оценка вложенных ресурсов: После того, как предварительно загруженный HTML-код будет очищен от необходимости его использования,
shouldInterceptRequest()срабатывает в обычном режиме для всех последующих вложенных ресурсов (таких как изображения, скрипты и CSS), необходимых для завершения отрисовки страницы.
Ключевые модели поведения
Следующие эксплуатационные характеристики и проверки соответствия требованиям определяют, как WebView инициирует и управляет запросами предварительной выборки:
- Потокобезопасность: Запросы могут быть инициированы из любого потока.
- Условия соответствия: Перед началом выполнения запроса WebView проверяет его безопасность и контекстную корректность, выполняя следующие проверки:
- Существующие файлы cookie: Для защиты конфиденциальности пользователей и предотвращения побочных эффектов, подобных CSRF-атакам, WebView может пропускать предварительную загрузку, если запрос требует определенных аутентифицированных файлов cookie, которые могут вызвать изменение состояния сервера.
- Наличие сервис-воркера: Если сервис-воркер уже контролирует область действия URL-адреса, WebView может передать управление обработчику выборки сервис-воркера, а не инициировать стандартную предварительную загрузку сети.
- Доступность прокси: WebView проверяет стабильность текущего сетевого пути (включая любые настроенные прокси), чтобы избежать сбоев при выполнении спекулятивных запросов в сложных сетевых конфигурациях.
- Если предварительная загрузка не запускается (даже с допустимыми параметрами), это часто происходит потому, что WebView определил, что фоновый запрос может помешать текущей сессии пользователя или состоянию безопасности.
- Отмена: Используйте
CancellationSignal, чтобы завершить выполняющийся запрос и предотвратить его кэширование.
Предварительная отрисовка страниц
Предварительная отрисовка создает скрытое «веб-содержимое», позволяющее полностью отобразить страницу в фоновом режиме, включая выполнение скриптов и загрузку подресурсов. Предварительная отрисовка основана на той же базовой инфраструктуре, что и предварительная загрузка. Если приложение инициирует предварительную отрисовку, WebView сначала выполняет предварительную загрузку ответа для предоставления навигации в режиме предварительной отрисовки, избегая избыточной сетевой активности.
Выполнение
Предварительная отрисовка — это операция на уровне экземпляра WebView. Вызовите prerenderUrlAsync() с помощью WebViewCompat из потока пользовательского интерфейса.
Котлин
WebViewCompat.prerenderUrlAsync(
webView,
url,
cancellationSignal,
executor,
params,
object : PrerenderOperationCallback {
override fun onPrerenderActivated() {
// Called when the user navigates to the URL and the hidden page is swapped in
}
override fun onError(exception: Throwable) {
// exception is an instance of PrerenderException
// Handle prerender failure (for example, memory pressure or disallowed JavaScript APIs)
}
}
)
Java
WebViewCompat.prerenderUrlAsync(webView, url, cancellationSignal, executor, params, new PrerenderOperationCallback() {
@Override
public void onPrerenderActivated() {
// Called when the user navigates to the URL and the hidden page is swapped in.
}
@Override
public void onError(@NonNull Throwable exception) {
// Handle prerender failure (for example, resource constraints or disallowed APIs).
}
});
И prefetch, и prerender являются полностью асинхронными функциями. prefetchUrlAsync() может быть вызван из любого потока, тогда как prerenderUrlAsync() должен быть инициирован из потока пользовательского интерфейса.
Технические ограничения
Для обеспечения баланса между мгновенной навигацией и работоспособностью системы WebView применяет следующие ограничения во время выполнения:
- Нехватка памяти: WebView отменяет предварительно отрисованные URL-адреса, если на устройстве недостаточно оперативной памяти.
- Запрещенные API: Любые попытки JavaScript получить доступ к определенным API (например, воспроизведение звука, оповещения) в фоновом режиме приведут к немедленному завершению предварительной отрисовки.
- Ограничение на количество экземпляров: существует ограничение на количество активных предварительно отрисованных URL-адресов, разрешенных для каждого WebView.
Сопоставление URL-адресов и поиск без изменяемых значений (NVS)
Для корректной работы WebView необходим надежный алгоритм сопоставления, гарантирующий, что предварительно загруженный ресурс будет отображаться только для соответствующего контекста навигации.
Точное совпадение против совпадения NVS
По умолчанию для предварительной загрузки и предварительной отрисовки требуется точное совпадение URL-адреса. Если URL-адрес, по которому осуществляется переход, идентичен предварительно загруженному URL-адресу, он немедленно загружается из кэша. Если параметры запроса различаются, WebView использует следующие правила No-Vary-Search (NVS):
- Подсказка: разработчики предоставляют подсказку
setExpectedNoVarySearchHeader()во время инициализации. Если URL-адрес, по которому осуществляется переход, совпадает с URL-адресом запроса за вычетом указанных параметров, WebView на короткое время блокируется, ожидая фактических заголовков от сервера. - Заголовок сервера: Заголовок ответа NVS от сервера является определяющим. Если сервер подтверждает, что различия в запросах следует игнорировать, совпадение предоставляется из кэша. В противном случае WebView переходит к загрузке из сети без предварительного запроса.
Функция No-Vary-Search (NVS) предназначена для продвинутых пользователей, и большинству разработчиков она может не понадобиться, поскольку они передают один и тот же URL-адрес как в функцию prefetch, так и в loadUrl() . Данное руководство необходимо только в том случае, если существуют различия в параметрах запроса между URL-адресом prefetch и URL-адресом loadUrl() .
Глобальная конфигурация
Настройте поведение спекулятивной загрузки на уровне профиля, задав ограничения PrefetchCache и максимальное количество предварительных рендеров. Вы также можете сбросить пользовательские ограничения предварительной загрузки до значений по умолчанию:
Котлин
// Configure prefetch cache limits
profile.prefetchCache.setMaxPrefetches(10)
profile.prefetchCache.setPrefetchTtlSeconds(60)
// Reset to system defaults when needed
profile.prefetchCache.clearMaxPrefetches()
// Configure maximum active prerenders
profile.setMaxPrerenders(2)
Java
// Configure prefetch cache limits
PrefetchCache prefetchCache = profile.getPrefetchCache();
prefetchCache.setMaxPrefetches(10);
prefetchCache.setPrefetchTtlSeconds(60);
// Reset to system defaults when needed
prefetchCache.clearMaxPrefetches();
// Configure maximum active prerenders
profile.setMaxPrerenders(2);
Обработка ошибок и исключений
В спекулятивных операциях для сообщения результатов используются объекты OutcomeReceiverCompat или PrerenderOperationCallback .
Основные исключения
При сбое операции спекулятивной загрузки обработчик ошибок сообщает об одном из следующих основных типов исключений, чтобы помочь вам диагностировать конкретные сценарии сбоя:
-
PrefetchException: Базовый класс для всех ошибок асинхронной предварительной выборки. -
PrefetchNetworkException: указывает на сбой на уровне сети или сервера. Может содержать полеhttpStatusCode(например, 404 или 503) для диагностики проблем на стороне сервера. -
PrerenderException: Суперкласс для всех ошибок, связанных с предварительной отрисовкой, таких как сбои из-за нехватки памяти или использования запрещенных API (например, воспроизведения звука) в фоновом режиме.
Стратегии оптимизации
Следуйте этим рекомендациям, чтобы максимально использовать преимущества спекулятивной загрузки и одновременно экономить системные ресурсы:
- Начинайте заблаговременно: запускайте предварительную загрузку данных во время запуска приложения или как только станет вероятным пункт назначения в навигации.
- Интегрированная стратегия: если вы предварительно отображаете URL-адрес, уже находящийся в кэше предварительной загрузки, навигация, отображаемая в предварительном рендеринге, будет обслуживаться из этого кэша, что позволит избежать избыточных сетевых запросов.
- Мониторинг квот: предварительная отрисовка требует больших ресурсов. Предпочтительнее предварительная загрузка нескольких вероятных вариантов, а предварительная отрисовка должна быть зарезервирована для одного наиболее вероятного варианта навигации.
- Поддержка схем: Убедитесь, что все URL-адреса используют обязательную схему HTTPS. Недопустимые схемы или нулевые входные данные вызывают синхронное
IllegalArgumentException.
Дополнительные ресурсы
Чтобы узнать больше об отладке веб-приложений, оптимизации производительности запуска WebView и обработке завершения процесса рендеринга, см. следующие ресурсы: