Основы webview
В двух словах об основах WebView. Рассмотрим четыре строки кода:
Что такое android system webview
Рассматриваемый компонент представляет собой оболочку для предпросмотра ссылок и работы с веб-приложениями, которая обычно активна по умолчанию. Все особенности этого софта уже рассмотрел один из наших авторов, а мы далее сосредоточимся на вариантах его запуска, если по каким-то причинам он отключён.
Что делать, если android system webview не включается
Иногда приведённые выше инструкции не работают, и рассматриваемый компонент просто отказывается запускаться. Это возникает по нескольким причинам, определить которые и устранить можно, выполнив следующие шаги:
1. Первым делом перезапустите смартфон или планшет – возможно, вы столкнулись с банальным багом.
2. Попробуйте обновить софт: для этого вызовите Google Play Маркет, нажмите на три полоски вверху слева и выберите «Мои приложения и игры».
Проверьте, нет ли пункта Вебвью на вкладке «Обновления». Если таковой отсутствует, перейдите к разделу «Установленные», найдите там соответствующую запись, и нажмите «Обновить».
3. Если обновление недоступно или не принесло ожидаемого эффекта, проведите очистку кэша программы: выполните шаги 1-3 первого способа, но затем тапните по пункту «Хранилище и кеш», после чего воспользуйтесь кнопкой «Очистить кеш».
4. Последним вариантом решения проблемы будет сброс устройства к заводским настройкам: возможно, составляющие WebView оказались повреждены. Разумеется, при этом сотрутся все пользовательские данные, поэтому озаботьтесь резервным копированием самых важных файлов.
Подробнее: Как сбросить Android до заводских настроек
Почему это приложение не включается
Второй часто задаваемый вопрос об Android System Webview — почему оно отключено и не включается (как его включить).
Ответ простой: начиная с Android 7 Nougat оно перестало использоваться на некоторых устройствах и в этом случае по умолчанию отключено. Теперь те же самые задачи выполняются через механизмы Google Chrome или встроенные средства самих приложений, т.е. необходимости во включении нет.
Если же у вас есть острая необходимость включить именно System Webview в Android 7 и 8, для этого существуют следующие два пути.
Первый — более простой:
- В приложениях отключите Google Chrome.
- Установите/обновите Android System Webview из Play Маркет.
- Откройте что-то, что использует Android System Webview, например, зайдите в настройки — Об устройстве — Юридическая информация — Правовая информация Google, затем откройте одну из ссылок.
- После этого вернитесь к приложению, и вы сможете увидеть, что оно включено.

Учтите, что после включения Google Chrome оно снова отключится — они не работают вместе.
Второй — несколько сложнее и не всегда работает (иногда возможность переключения отсутсвует).
- Включите режим разработчика на вашем Android-устройстве.
- Зайдите в раздел «Для разработчиков» и нажмите по пункту «Сервис WebView».
- Возможно, вы увидите там возможность выбрать между Chrome Stable и Android System WebView (или Google WebView, что одно и то же).
При изменении сервиса WebView с Chrome на Android (Google) вы включите рассматриваемое в статье приложение.
Javascript alerts
По умолчанию диалог Alert в WebView не работает. Если загрузить туда страницу HTML с JavaScript и выполнить alert(‘Hello’), то ничего не произойдет. Чтобы заставить его работать, нужно определить свой инстанс WebChromeClient, переопределить метод WebChromeClient.onJSAlert() и в нем вызвать у него super.onJSAlert(). Этого достаточно, чтобы Alerts заработали.
WebView webView = (WebView) findViewById(R.id.web_view);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebChromeClient(new WebChromeClient() { @Override public boolean onJsAlert(....) { return super.onJsAlert(view, url, message, result); }
}Вопросы и ответы
Вопрос:
Есть проект
— это сторонняя реализация WebView, позволяющая на старых устройствах использовать свежий Chrome. У вас есть какой-то опыт, вы пробовали его встраивать?
Ответ:
Я не пробовал. На текущий момент мы поддерживаем Android начиная с 14-й версии и уже не ориентируемся на старые устройства.
Вопрос: Как вы боретесь с артефактами, которые остаются при прорисовке WebView?Ответ: Мы с ними не боремся, пробовали — не получилось. Это происходит не на всех устройствах. Решили, что это не настолько вопиющая проблема, чтобы тратить на нее больше ресурсов.
Вопрос: Иногда требуется WebView вложить в ScrollView. Это некрасиво, но иногда требуется по заданию. Это не поощряется, даже где-то запрещается, и после этого возникают недостатки в работе. Но все равно иногда это приходится делать. Например, если вы сверху рисуете WebView, а под ним рисуете какой-то нативный компонент (который должен быть нативным согласно требованию), и все это должно быть выполнено в виде единого ScrollView.
То есть сначала пользователь посмотрел бы всю страничку, а потом, если бы захотел, то долистал бы до этих нативных компонентов.Ответ: К сожалению, не могу вам ответить, потому что я не сталкивался с такой ситуацией. Она довольно специфическая, и представить себе вариант, когда нужно WebView положить в ScrollView, мне сложно.
Вопрос: Есть почтовое приложение. Там сверху шапка с получателями и со всем остальным. Даже в этом случае не все будет гладко. У WebView возникают большие проблемы, когда он пытается определить свой размер внутри ScrollView.Ответ: Можно попробовать отрисовать означенную часть UI внутри WebView.
Вопрос: То есть полностью перенести всю логику из нативной части в WebView и оставить эти контейнеры?Ответ: Даже, может быть, логику переносить не надо, имеется в виду инжектирование Java-классов. Логику можно оставить и вызывать через инжектированный класс. В WebView можно перенести только UI.
Вопрос: Вы упоминали про игры в мессенджере. Они представляют собой web-приложения?Ответ: Да, это web-страницы с JavaScript внутри WebView.
Вопрос: Вы все это делаете, чтобы просто не переписывать игры нативно?Ответ: И для этого тоже. Но основная идея в том, чтобы дать сторонним разработчикам возможность создавать приложения, которые могут встраиваться в ICQ, и с помощью этого ICQ Web API взаимодействовать с мессенджером.
Вопрос: То есть в эти игры можно играть также через web-браузер на лэптопе?Ответ: Да. Она может быть открыта в web-браузере, и мы иногда их прямо в нем и отлаживаем.
Вопрос: А если Intent, допустим, в Chrome прокинуть эту игрушку, какие проблемы тогда будут? Если не свою WebView писать, а воспользоваться услугами?Ответ: Проблема в том, что в своем WebView мы можем предоставить API через инжектирование Java-класса, и с помощью этого API приложение сможет напрямую взаимодействовать с ICQ, отправлять ему различные команды.
Допустим, команду на получение имени пользователя, на получение чатов, которые у него открыты, отправлять сообщения в чат непосредственно из ICQ. То есть из Chrome отправлять сообщения непосредственно в ICQ не получится. В нашем случае все это возможно.
Вопрос: Вы упомянули, что режете данные на куски по одному мегабайту. Как вы их потом собираете?Ответ: Мы сейчас этого не делаем, потому что у нас нет такой потребности.
Вопрос: Хватает одного мегабайта?Ответ: Да. Если картинки больше, то пытаемся их ужимать. Я сказал о том, что если такая потребность существует, то это может быть решением — разрезать и собирать потом в Java.
Вопрос: Как вы обеспечиваете безопасность работы приложений в песочнице? Правильно ли я понял, что из JavaScript приложения нужно вызывать инжектированные Java-классы?Ответ: Да.
Вопрос: Как будет обеспечиваться в этом случае безопасность, запрещен ли доступ к каким-то системным функциям?Ответ: Прямо сейчас, так как система еще довольно молодая, у нас в основном используются наши собственные web-приложения, и мы им полностью доверяем.
В дальнейшем все приложения, которые будут поступать к нам, будут администрироваться, код будет просматриваться, для этого выделена специальная Security Team. Дополнительно будет создана специальная система разрешений, без которых приложения не смогут получить доступ к какой-то критической для пользователя информации.
Инжектирование кода java в javascript
Пример кода Java:
WebView webView = (WebView) findViewById(R.id.web_view);
webView.getSettings().setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(new MyJavaInterface(), "test");
private class MyJavaInterface { @android.webkit.JavascriptInterface public String getGreeting() { return "Hello JavaScript!"; }
}Пример кода JavaScript:
Здесь показан пример инжектирования кода Java в JavaScript. Создается коротенький Java-класс MyJavaInterface, и у него есть один единственный метод getGreeting(). Обратите внимание, что этот метод помечен маркирующим интерфейсом @JavaScriptInterface — это важно.
Вызывая метод WebView.addJavascriptInterface(), мы пробрасываем данный класс в WebView. Ниже мы видим, как к нему можно обращаться из JavaScript, вызвав test.getGreeting(). Важным моментом здесь является имя test, которое впоследствии в JavaScript будет использовано как объект, через который можно делать вызовы к нашему Java-коду.
Если мы поставим breakpoint на строку return «Hello JavaStript!» и посмотрим имя потока, в котором получен вызов, какой это будет поток? Это не UI-поток, а специальный поток Java Bridge. Следовательно, если при вызове каких-то методов Java мы хотим манипулировать с UI, то нам нужно позаботиться о том, чтобы эти операции передавались в UI-поток — использовать хэндлеры или любой другой способ.
Второй момент: Java Bridge поток нельзя блокировать, иначе JavaScript в WebView просто перестанет работать, и никакие действия пользователя не будут иметь отклика. Поэтому если нужно делать много работы, задачи нужно также отправлять в другие потоки или сервисы.
Контроль загрузки страницы в webview
После того, как мы отдали WebView команду на загрузку страницы, следующим шагом нужно узнать результат выполнения: загрузилась ли страница. С точки зрения официальной Android-документации, все просто. У нас есть метод WebViewClient.onPageStarted(), который вызывается, когда страница начинает загружаться.
ОЖИДАНИЕ:
- onPageStarted→ shouldOverrideUrlLoading (если редирект) → onPageFinished / onReceivedError
РЕАЛЬНОСТЬ:
- onPageStarted → onPageStarted → onPageFinished
- onPageStarted → onPageFinished → onPageFinished
- onPageFinished → onPageStarted
- onReceivedError → onPageStarted → onPageFinished
- onReceivedError → onPageFinished (no onPageStarted)
- onPageFinished (no onPageStarted)
- shouldOverrideUrlLoading → shouldOverrideUrlLoading
На самом деле, все всегда по-разному и зависит от конкретного устройства: onPageStarted(), onPageFinished() и другие методы могут вызываться два раза, все методы могут вызываться в разном порядке, а некоторые могут не вызываться совсем. Особенно часто подобные проблемы возникают на Samsung и Google Nexus.
Проблему эту приходится решать при помощи добавления дополнительных проверок в наш инстанс класса WebViewClient. Когда он начинает работать, мы сохраняем URL и затем проверяем, что загрузка происходит именно по этому URL. Если она завершилась, то проверяем на наличие ошибок.
Несоответствие типов java в javascript
Когда мы вызываем некоторые методы, написанные на Java и инжектированные в JavaScript, как показано выше, возникает проблема несоответствия типов Java и JavaScript. В этой таблице приведены основные правила мапинга между системами типов:
| Java -> JavaScript | JavaScript -> Java | ||
|---|---|---|---|
| byte, short, char, int, long, float, double | Number | Number | Byte, short, int, long, float, double ( не Integer, Byte, Short, Long, Float, Double и не char) |
| boolean | Boolean | Boolean | boolean (не Boolean) |
| Boolean, Integer, Long, Character, Object | Object | Array, Object, Function | null |
| String | String (Object) | String | String (не char[]) |
| char[], int[], Integer[], Object[] | undefined | undefined | null |
| null | undefined | null | null |
Самое основное, что стоит здесь заметить, — то, что объектные обертки не передаются. А из всех Java-объектов в JavaScript мапится только String. Массивы и null в Java преобразуются в undefined в JavaScript.
С передачей в обратную сторону, из JavaScript в Java, тоже есть нюансы. Если вызывать какой-то метод, имеющий параметрами элементарные типы, то можно передать туда number. А если среди параметров метода есть не элементарные типы, а скажем, объектные обертки, такие как Integer, то такой метод не будет вызван. Поэтому нужно пользоваться только элементарными типами Java.
Обработка изменения ориентации устройства
Ещё одна серьезная проблема связана с портретной и альбомной ориентацией. Если поменять ориентацию устройства, то по умолчанию Activity будет пересоздана. При этом все View, которые к ней прикреплены, тоже будут пересозданы. Представьте ситуацию: есть WebView, в который загружена некая игра.
Пользователь доходит до 99 уровня, поворачивает устройство, и инстанс WebView с игрой пересоздается, страница загружается заново, и он снова на первом уровне. Чтобы этого избежать, мы используем мануальную обработку смены конфигурации устройства. В принципе, это вещь известная и описана в
. Для этого достаточно прописать в AndroidManifest.xml в разделе активити параметр configChanges.
Это будет означать, что мы сами обрабатываем смену ориентации в activity. Если ориентация изменится, мы получаем вызов Activity.onConfigurationChange() и можем поменять какие-то ресурсы программно. Но обычно activity с WebView имеют только сам WebView, растянутый на весь экран, и там ничего делать не приходится.
Определение состояния сети в javascript
В JavaScript есть полезный объект
. У него есть поле onLine, показывающее статус сетевого подключения. Если у нас есть подключение к сети, в браузере это поле имеет значение true, в противном случае — false. Чтобы оно работало корректно внутри WebView, необходимо использовать метод WebView.
setNetworkAvailable(). С его помощью мы передаем актуальное сетевое состояние, которое можно получить при помощи сетевого broadcast receiver или любым другим способом, которым вы трекаете сетевое состояние в Android. Делать это нужно постоянно. Если сетевое состояние изменилось, то нужно заново вызвать WebView.
Полноэкранный медиаплеер
Если в web-страницу встроен медиаплеер, то часто возникает потребность обеспечить возможность его работы в полноэкранном режиме. Например, медиаплеер
Проблемы при работе с webview
После старта загрузки обычно бывает нужно проконтролировать этот процесс: узнать, успешно ли прошла загрузка, были ли редиректы, отследить время загрузки и другие вещи. Также будет сказано о потоках, в которых работает JavaScript и вызовы в Java, о несоответствии типов Java и JavaScript, поведении Alerts в JavaScript и размерах передаваемых данных. Решение для этих проблем будет также описано дальше.
Размеры данных, передаваемых между java и javascript
Еще одна существенная проблема связана с объемом передаваемых данных между Java и JavaScript. Если передается достаточно большой объем данных (например, картинки) из JavaScript в Java, то при возникновении ошибки OutОfMemory, поймать ее не получится.
Способ 1: включение в настройках (android 5.0-7.0 и android 10 )
В «зелёном роботе» версий с пятой по седьмую, а также в новейших релизах, соответствующих номерам 10 , рассматриваемое ПО присутствует как выделенный компонент, который пользователи могут включать или выключать самостоятельно.
1. Откройте «Настройки» и воспользуйтесь пунктами «Приложения и уведомления» – «Показать все приложения».
2. Далее нажмите на три точки вверху справа и тапните «Показать системные процессы».
3. Прокрутите перечень до позиции «Android System WebView» и тапните по ней.
4. Нажмите на кнопку «Включить».
Готово – теперь компонент будет активен.
Способ 2: отключение chrome (android 7.1-9.0)
В седьмой, восьмой и девятой версиях Андроида функции Вебвью на себя взял Гугл Хром – веб-приложения в этих вариантах ОС отображаются с помощью его движка. Активировать первый можно отключением последнего, в 9.0 это делается следующим образом:
1. Откройте «Настройки», где выберите пункты «Приложения и уведомления» – «Показать все приложения».
2. В перечне установленного софта найдите позицию «Google Chrome» и тапните по ней.
3. На странице программы нажмите «Отключить» и подтвердите своё желание.
Теперь ПО, которое раньше пользовалось движком Хром, будет автоматически переключено на активированный таким образом WebView.
