На прошедшей встрече AndroidDevs Meetup выступили несколько разработчиков из команды мессенджера ICQ. Мой доклад был посвящен Android WebView. Для всех, кто не смог приехать на встречу, публикую здесь статью по мотивам выступления. Пойду по верхам, крупными штрихами. Глубоких технических деталей и много кода давать не буду. Если вас заинтересуют подробности, по ссылке в конце поста можно скачать приложение, специально написанное в качестве иллюстрации, и все увидеть на примерах.WebView — это компонент платформы Android, который позволяет встраивать web-страницы в Android-приложения. По сути, это встраиваемый браузер. При помощи WebView примерно год назад мы решили создать ICQ Web API для интеграции web-приложений в наш мессенджер. Что представляет собой web-приложение? По сути, это HTML-страница, которая содержит JavaScript и работает внутри ICQ. Используя ICQ Web API, web-страницы через JavaScript могут отдавать ICQ разные команды, допустим, на отправку сообщений, открытие чата и т.п. Вот как это выглядит в ICQ. Из пункта Applications можно перейти в список приложений. Это пока еще не WebView, чтобы попасть в него, нужно выбрать одно из приложений. Тогда мы переходим непосредственно в WebView, куда web-приложение загружается из сети. Как это устроено технически? У WebView есть возможность определенным образом инжектировать Java код в JavaScript. JavaScript может вызывать код, который мы написали и предоставили ему. Это возможность, на которой и основан весь ICQ Web API. Здесь показано, что внутри ICQ работает WebView, между ними есть инжектированный Java-класс, а в WebView загружаются приложения из сети. Итак, JavaScript из WebView делает вызовы к Java-коду ICQ. Существует большое число различных вызовов, и в процессе разработки встретилось множество проблем, связанных с работой этого механизма, о которых я и расскажу далее. После старта загрузки обычно бывает нужно проконтролировать этот процесс: узнать, успешно ли прошла загрузка, были ли редиректы, отследить время загрузки и другие вещи. Также будет сказано о потоках, в которых работает JavaScript и вызовы в Java, о несоответствии типов Java и JavaScript, поведении Alerts в JavaScript и размерах передаваемых данных. Решение для этих проблем будет также описано дальше. В двух словах об основах WebView. Рассмотрим четыре строки кода:
WebView webView = (WebView) findViewById(R.id.web_view); webView.loadUrl("http://www.example.com"); webView.setWebViewClient(new WebViewClient() {…} ); webView.setWebChromeClient(new WebChromeClient() {…} );
Тут видно, что мы получаем WebView и загружаем в него сайт example.com, вызвав WebView.loadURL(). В Android есть два важных класса: WebViewClient и WebChromeClient, которые взаимодействуют с WebView. Зачем они нужны? WebViewClient требуется для того, чтобы контролировать процесс загрузки страницы, а WebChromeClient — чтобы взаимодействовать с этой страницей после того, как она успешно загружена. До завершения загрузки страницы работает WebViewClient, а после — WebChromeClient. Как видно в коде, для того чтобы взаимодействовать с WebView, нам нужно создать собственные инстансы этих классов и передать их в WebView. Далее WebView при определенных условиях вызывает разные методы, которые мы переопределили в наших инстансах, и так мы узнаем о событиях в системе. Наиболее важные методы, которые WebView вызывает у созданных нами инстансов WebViewClient и WebChromeClient:
WebViewClient | WebChromeClient |
---|---|
onPageStarted() | openFileChooser(), onShowFileChooser() |
shouldOverrideUrlLoading() | onShowCustomView(), onHideCustomView() |
onPageFinished(), onReceivedError() | onJsAlert() |
О назначении всех этих методов буду рассказывать немного позднее, хотя из самих названий уже многое понятно. После того, как мы отдали WebView команду на загрузку страницы, следующим шагом нужно узнать результат выполнения: загрузилась ли страница. С точки зрения официальной Android-документации, все просто. У нас есть метод WebViewClient.onPageStarted(), который вызывается, когда страница начинает загружаться. В случае редиректа вызывается WebViewClient.shouldOverrideUrlLoading(), если страница загрузилась — WebViewClient.onPageFinished(), если не загрузилась — WebViewClient.onReceivedError(). Все кажется логичным. Как это происходит на самом деле? ОЖИДАНИЕ:
- 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:
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 просто перестанет работать, и никакие действия пользователя не будут иметь отклика. Поэтому если нужно делать много работы, задачи нужно также отправлять в другие потоки или сервисы. Когда мы вызываем некоторые методы, написанные на 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. Еще одна существенная проблема связана с объемом передаваемых данных между Java и JavaScript. Если передается достаточно большой объем данных (например, картинки) из JavaScript в Java, то при возникновении ошибки OutОfMemory, поймать ее не получится. Приложение просто падает. Вот пример того, что можно увидеть в logcat в этом случае:
Process com.estrongs.android.pop (pid 6941) has died Process com.google.android.youtube (pid 24613) has died Process kik.android (pid 3022) has died Process com.doeasyapps.optimize (pid 30743) has died Process com.sandisk.mz (pid 31340) has died WIN DEATH: Window{3fc4769b u0 mc.test/mc.test.MainActivity} WIN DEATH: Window{17a5c850 u0 mc.test/mc.test.DataSizeTestActivity} Process mc.test (pid 16794) has died Force removing ActivityRecord{215171e4 u0 mc.test/.DataSizeTestActivity t406}: app died, no saved state
Как видите, если в приложении происходит OutOfMemory, то начинают вылетать различные другие приложения, запущенные на устройстве. В итоге, закрыв все что можно, Android доходит до нашего приложения, и, так как оно находится в foreground, закрывает его последним. Еще раз хочу напомнить, что никакого исключения мы не получим, приложение просто упадет. Чтобы этого не происходило, необходимо ограничивать размер передаваемых данных. Многое зависит от устройства. На некоторых гаджетах получается передавать 6 мегабайт, на некоторых 2-3. Для себя мы выбрали ограничение в 1 мегабайт, и этого достаточно для большинства устройств. Если нужно передать больше, то данные придется резать на чанки и передавать частями. По умолчанию диалог 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); } }
Ещё одна серьезная проблема связана с портретной и альбомной ориентацией. Если поменять ориентацию устройства, то по умолчанию Activity будет пересоздана. При этом все View, которые к ней прикреплены, тоже будут пересозданы. Представьте ситуацию: есть WebView, в который загружена некая игра. Пользователь доходит до 99 уровня, поворачивает устройство, и инстанс WebView с игрой пересоздается, страница загружается заново, и он снова на первом уровне. Чтобы этого избежать, мы используем мануальную обработку смены конфигурации устройства. В принципе, это вещь известная и описана в официальной документации. Для этого достаточно прописать в AndroidManifest.xml в разделе активити параметр configChanges.
Это будет означать, что мы сами обрабатываем смену ориентации в activity. Если ориентация изменится, мы получаем вызов Activity.onConfigurationChange() и можем поменять какие-то ресурсы программно. Но обычно activity с WebView имеют только сам WebView, растянутый на весь экран, и там ничего делать не приходится. Он просто перерисовывается и все продолжает нормально работать. Таким образом, установка configChanges позволяет не пересоздавать Activity, и все View, которые в нем присутствуют, сохранят свое состояние. Если в web-страницу встроен медиаплеер, то часто возникает потребность обеспечить возможность его работы в полноэкранном режиме. Например, медиаплеер youtube может работать внутри web-страницы в html-теге iframe, и у него есть кнопка переключения в полноэкранный режим. К сожалению, в WebView по умолчанию это не работает. Чтобы заставить это работать, нужно сделать несколько манипуляций. В xml layout, в котором расположен WebView, разместим дополнительно FrameLayout. Это контейнер, который растянут на весь экран и в котором будет находится View с плеером:
<WebVie
А затем в своем инстансе WebChromeClient переопределим несколько методов:
public void onShowCustomView(View v, CustomViewCallback c) { mWebView.setVisibility(View.GONE); mFullScreenContainer.setVisibility(View.VISIBLE); mFullScreenContainer.addView(view); mFullScreenView = v; mFullscreenViewCallback = c; } public void onHideCustomView() { mFullScreenContainer.removeView(mFullScreenView); mFullscreenViewCallback.onCustomViewHidden(); mFullScreenView = null; mWebView.setVisibility(View.VISIBLE); mFullScreenContainer.setVisibility(View.GONE); }
Система вызывает WebChromeClient.onShowCustomView(), когда юзер нажимает на кнопку перехода в полноэкранный режим в плеере. оnShowCustomView() принимает View, которое и репрезентует сам плеер. Этот View вставляется в FullScreenContainer и делается видимым, а WebView скрывается. Когда пользователь хочет вернуться из полноэкранного режима, вызывается метод WebChromeClient.onHideCustimView() и проделывается обратная операция: отображаем WebView и скрываем FullScreenContainer. Web-разработчики знают, что этот контейнер используется на web-страницах для того, чтобы пользователь мог выбрать какой-то файл и загрузить его на сервер, либо показать на экране. Для работы этого контейнера в WebView нам нужно переопределить метод WebChromeClient.openFileChooser(). В этом методе есть некий callback, в который нужно передать файл, выбранный пользователем. Никакого дополнительного функционала сам по себе />
не имеет. Диалог выбора файла нам нужно обеспечить. То есть мы можем открыть любой стандартный Android picker, в котором пользователь выберет нужный файл, получить его, например, через onActivityResult(), и передать в callback метода openFileChooser().Пример кода JavaScript:
Пример кода Java:
WebChromeClient myClient = new WebChromeClient() { @SuppressWarnings("unused") public void openFileChooser(ValueCallback callback, String t, String capture) { callback.onReceiveValue(Uri.parse("file://" + getFileFromSomeProvider())); } }; WebView webView = (WebView) findViewById(R.id.web_view); webView.setWebChromeClient(myClient);
В JavaScript есть полезный объект Navigator. У него есть поле onLine, показывающее статус сетевого подключения. Если у нас есть подключение к сети, в браузере это поле имеет значение true, в противном случае — false. Чтобы оно работало корректно внутри WebView, необходимо использовать метод WebView.setNetworkAvailable(). С его помощью мы передаем актуальное сетевое состояние, которое можно получить при помощи сетевого broadcast receiver или любым другим способом, которым вы трекаете сетевое состояние в Android. Делать это нужно постоянно. Если сетевое состояние изменилось, то нужно заново вызвать WebView.setNetworkAvailable() и передать актуальные данные. В JavaScript мы будем получать актуальное значение этого свойства через Navigator.onLine.github.com/mc-android-developer/mc.presentation.webviewВопрос: Есть проект CrossWalk — это сторонняя реализация 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. Дополнительно будет создана специальная система разрешений, без которых приложения не смогут получить доступ к какой-то критической для пользователя информации. Автор: raz0r2018-12-09Web В» Мобильные приложения Данный материал предоставлен сайтом ProWebber.ru исключительно в ознакомительных целях. Администрация не несет ответственности за его содержимое. Скачать бесплатно Universal Android WebView App v2.5.0. WebView App — это нативное приложение для Android, которое использует компонент веб-просмотра для отображения контента. С помощью этих исходных кодов вы можете превратить ваш адаптивный веб-сайт в универсальное мобильное приложение. Это быстро, легко и доступно. Приложение WebView имеет много полезных встроенных функций и сервисов. Это самое популярное приложение для просмотра веб-страниц. Universal Android WebView App предоставляет вам простой способ создать собственное приложение. Не требует навыков программирования. Код легко настраивается. Существует только один файл конфигурации для настройки всего. Проект хорошо документирован. Создайте свое собственное приложение менее чем за 15 минут без каких-либо специальных знаний! Это проще, чем вы думаете.Демо:https://codecanyon.net/item/universal-andro…iew-app/8431507Приложение WebView использует мощный и быстрый движок Chromium. Он совместим с WordPress или любым другим веб-фреймворком. Он поддерживает HTML5, CSS3, javascript, jQuery, Bootstrap и другие веб-технологии. Смотрите полный список функций ниже.Zwebviewapp-2.5.0.zipРазмер: 1.89 Mb, скачали 1047 раз В Благодарностей: 6 slackye, Pain187, Softmaster, Eh3an, sergeysrnd, ManiRana Просмотры: 3152 :: Комментарии (4) :: :: Нужна помощь? Задайте вопрос на форуме :: Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь. Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем. Информация Посетители, находящиеся в группе Гости, не могут оставлять комментарии в данной новости.
WebView представляет собой компонент, который отображает внутри приложения веб-страницу. С его помощью можно загрузить из Интернета любую веб-страницу или HTML-документ, или даже превратить приложение в полноценный браузер.
Теперь, чтобы использовать компонет, нужно в коде активности инициализировать экземпляр класса WebView.
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); WebView webView = findViewById(R.id.webView); }
Если нужно загрузить страницу из Интернета, вам понадобится добавить специальное разрешение для приложения в файл AndroidManifest.xml. Для этого нужно перед элементом добавить следующую строчку:
После этого приложение сможет выполнять операции, связанные с сетью.
Теперь в коде активности после инициализации добавим метод loadUrl(), в параметры которого нужно передать адрес страницы, чтобы загрузить нужную страницу.
webView.loadUrl("https://google.com");
Если запустить приложение сейчас, то перед загрузкой страницы система вместо того, чтобы загрузить ссылку в приложении, предложит воспользоваться браузером.
Это связано с тем, как WebView обрабатывает события навигации. Ниже показана последовательность событий:
- WebView пытается загрузить URL и получает перенаправление на новый URL.
- WebView проверяет, может ли система обрабатывать намерение для URL. Если может, то система обрабатывает навигацию по URL, в противном случае WebView будет производить навигацию внутри (иными словами, у пользователя на устройстве не установлен браузер).
- Система выбирает предпочтительное приложение для обработки URL, то есть браузер. Если на устройстве установлено более одного браузера, то будет показан диалог, как на скриншоте выше.
Чтобы открывать ссылки внутри приложения, нужно добавить обработку навигации для WebView. Для этого необходимо создать экземпляр класса WebViewClient, который служит для управления различными событиями WebView. Внутри него следует переопределить метод shouldOverrideUrlLoading(). Этот метод сообщает Activity Manager, хотим ли мы, чтобы система обрабатывала URL.
Примечание: начиная с Android Nougat (API 24) была добавлена новая реализация этого метода, а старая на данный момент является устаревшей. Поэтому если вы хотите, чтобы WebView работал как на старых, так и на новых версиях Android, вам нужно будет переопределить обе реализации, используя для старого метода аннотацию @SuppressWarnings(«deprecation») для отключения предупреждений компилятора и для нового метода аннотацию @TargetApi(), которая указывает, что метод будет работать, начиная с заданного API.
В результате код получившегося WebViewClient можно увидеть ниже:
WebViewClient webViewClient = new WebViewClient() { @SuppressWarnings("deprecation") @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } @TargetApi(Build.VERSION_CODES.N) @Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { view.loadUrl(request.getUrl().toString()); return true; } };
Возвращая true, мы сообщаем системе, что берём на себя управление навигацией, поэтому в реализации нужно также вызывать метод loadUrl(), в противном случае страница не будет загружена. Если же вернуть false, то необходимость использовать loadUrl() в реализации отпадёт, WebView автоматически загрузит любую страницу без какой-либо обработки.
WebViewClient также может обрабатывать самые разные события, однако зачастую нам важно знать, что страница загружена. Чтобы отслеживать, когда страница начала загружаться и когда была загружена, нужно переопределить методы onPageStarted() и onPageFinished() соответственно.
WebViewClient webViewClient = new WebViewClient() { @SuppressWarnings("deprecation") @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } @TargetApi(Build.VERSION_CODES.N) @Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { view.loadUrl(request.getUrl().toString()); return true; } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); Toast.makeText(getApplicationContext(), "Страница загружена!", Toast.LENGTH_SHORT).show(); } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); Toast.makeText(getApplicationContext(), "Начата загрузка страницы", Toast.LENGTH_SHORT) .show(); } };
Теперь созданный WebViewClient нужно передать в WebView, для этого нужно воспользоваться методом setWebViewClient().
webView.setWebViewClient(webViewClient);
После того, как мы передали WebViewClient, можно снова загружаем сайт с помощью метода loadUrl() и смотрим результат.
webView.loadUrl("https://google.com");
Большим преимуществом использования WebView в приложении является то, что разработчик может хранить в приложении ассеты. Это позволяет приложению работать в оффлайне и увеличивает время загрузки, поскольку WebView может извлекать ассеты непосредственно из файловой системы и загружать их.
Чтобы хранить файлы, такие как HTML, CSS,JavaScript, нужно поместить их в папку assets. Это директория, которую Android использует для необработанных файлов, которые могут понадобиться приложению.
Чтобы создать в проекте папку assets, нажмите правой кнопкой мыши на список файлов во вкладке Project, затем в контекстном меню выберите New — Folder — Assets Folder.
После этого в появившемся окне выберите папку, в которую нужно добавить, и нажмите Finish.
Теперь во вкладке Project можно обнаружить, что появилась новая папка assets, внутри неё и будут размещаться все файлы, необходимые для WebView.
Создадим простой HTML-файл с текстом внутри.
Привет, мир!
Как правило, хорошей практикой является сохранение веб-файлов в подкаталоге. Поэтому в папке assets добавим ещё одну и назовём её www, внутри неё разместим созданный HTML-файл.
Примечание: абсолютные ссылки не работают в WebView при обращении к другим файлам, таким как CSS и JavaScript. Поэтому убедитесь, что все ссылки относительные (например, вместо /pages/link.html используйте ./pages/link.html).
Теперь остаётся только загрузить страницу в WebView, для этого воспользуемся также методом loadUrl(), в который передадим специальную ссылку на файл.
webView.loadUrl("file:///android_asset/www/test.html");
В результате при запуске приложения загрузится нужная страница.
По умолчанию, WebView не разрешает выполнять скрипты JavaScript на загруженных страницах. Чтобы разрешить использовать JavaScript, нужно получить от WebView объект WebSettings с помощью метода getSettings(). С помощью класса WebSettings можно выполнять самую разную настройку, например изменить размер шрифта, семейство шрифтов, добавить кнопки зума. В данном случае мы хотим включить JavaScript, поэтому воспользуемся методом setJavaScriptEnabled() и передадим в него значение true.
webView.getSettings().setJavaScriptEnabled(true);
После этого скрипты на загруженных страницах начнут работать.
You can build your Android app with the Android Platform APIs and the following libraries.
Developing with Kotlin? Check out the Kotlin reference for the Android Platform and AndroidX library.
Note: Many Kotlin reference topics are derived from Java-based source code. This means that some Kotlin reference topics might contain Java code snippets.
Android Support Library
Provides a variety of Android feature and utility APIs that are compatible with a wide range of platform versions.
AndroidX
Refactored versions of the Android APIs that are not bundled with the operating system.
AndroidX Test
Includes APIs for testing your Android app, including Espresso, JUnit Runner, JUnit4 rules, and UI Automator.
AndroidX Constraint Layout
Includes ConstraintLayout
and related APIs for building constraint-based layouts.
Architecture Components
Includes APIs for a variety of core app components, such as APIs that manage your UI component lifecycle, data persistence, view model, and more.
Jetpack Compose
Jetpack Compose is a modern toolkit for building native Android UI. Jetpack Compose simplifies and accelerates UI development on Android with less code, powerful tools, and intuitive Kotlin APIs.
Android Automotive Library
Provides APIs for building Android Automotive apps.
Databinding Library
Includes APIs to help you write declarative layouts and minimize the glue code necessary to bind your application logic and layouts.
Material Components
Material Components for Android (MDC-Android) help developers execute Material Design to build beautiful and functional Android apps.
Constraint Layout Library
Legacy Support Library ConstraintLayout
and related APIs for building constraint-based layouts.
Android Test Support Library
Includes APIs for testing your Android app, including Espresso, JUnit Runner, JUnit4 rules, and UI Automator.
Android Wearable Library
Provides APIs to build apps for wearable devices running Wear OS by Google.
Android Things Library
Provides APIs to build connected devices running the Android Things platform.
Play In-app Billing Library
Provides APIs to help you implement Google Play’s in-app billing and subscription features.
Play Core Library
Provides APIs to help you request, monitor, and manage on demand downloads for Google Play Instant and Dynamic Delivery.
Play Install Referrer library
Provides APIs to securely retrieve referral content from Google Play.
If you want to deliver a web application (or just a web page) as a part of a client application, you can do it using WebView
. The WebView
class is an extension of Android's View
class that allows you to display web pages as a part of your activity layout. It does not include any features of a fully developed web browser, such as navigation controls or an address bar. All that WebView
does, by default, is show a web page.
A common scenario in which using WebView
is helpful is when you want to provide information in your app that you might need to update, such as an end-user agreement or a user guide. Within your Android app, you can create an Activity
that contains a WebView
, then use that to display your document that's hosted online.
Another scenario in which WebView
can help is if your app provides data to the user that always requires an Internet connection to retrieve data, such as email. In this case, you might find that it's easier to build a WebView
in your Android app that shows a web page with all the user data, rather than performing a network request, then parsing the data and rendering it in an Android layout. Instead, you can design a web page that's tailored for Android devices and then implement a WebView
in your Android app that loads the web page.
This document shows you how to get started with WebView
and how to do some additional things, such as handle page navigation and bind JavaScript from your web page to client-side code in your Android app.
Содержание
Adding a WebView to your app
To add a WebView
to your app, you can either include the element in your activity layout, or set the entire Activity window as a
WebView
in onCreate()
.
Adding a WebView in the activity layout
To add a WebView
to your app in the layout, add the following code to your activity's layout XML file:
To load a web page in the WebView
, use loadUrl()
. For example:
Adding a WebView in onCreate()
To add a WebView
to your app in an activity’s onCreate()
method instead, use logic similar to the following:
Then load the page with:
Or load the URL from an HTML string:
Note: There are restrictions on what this HTML can do. See loadData()
and loadDataWithBaseURL()
for more info about encoding options.
Before this works, however, your app must have access to the Internet. To get internet access, request the INTERNET
permission in your manifest file. For example:
That's all you need for a basic WebView
that displays a web page. Additionally, you can customize your WebView
by modifying the following:
- Enabling fullscreen support with
WebChromeClient
. This class is also called when aWebView
needs permission to alter the host app's UI, such as creating or closing windows and sending JavaScript dialogs to the user. To learn more about debugging in this context, read Debugging Web Apps. - Handling events that impact content rendering, such as errors on form submissions or navigation with
WebViewClient
. You can also use this subclass to intercept URL loading. - Enabling JavaScript by modifying
WebSettings
. - Using JavaScript to access Android framework objects that you have injected into a
WebView
.
Using JavaScript in WebView
If the web page you plan to load in your WebView
uses JavaScript, you must enable JavaScript for your WebView
. Once JavaScript is enabled, you can also create interfaces between your app code and your JavaScript code.
Enabling JavaScript
JavaScript is disabled in a WebView
by default. You can enable it through the WebSettings
attached to your WebView
.You can retrieve WebSettings
with getSettings()
, then enable JavaScript with setJavaScriptEnabled()
.
For example:
WebSettings
provides access to a variety of other settings that you might find useful. For example, if you're developing a web application that's designed specifically for the WebView
in your Android app, then you can define a custom user agent string with setUserAgentString()
, then query the custom user agent in your web page to verify that the client requesting your web page is actually your Android app.
Binding JavaScript code to Android code
When developing a web application that's designed specifically for the WebView
in your Android app, you can create interfaces between your JavaScript code and client-side Android code. For example, your JavaScript code can call a method in your Android code to display a Dialog
, instead of using JavaScript's alert()
function.
To bind a new interface between your JavaScript and Android code, call addJavascriptInterface()
, passing it a class instance to bind to your JavaScript and an interface name that your JavaScript can call to access the class.
For example, you can include the following class in your Android app:
Caution: If you’ve set your targetSdkVersion
to 17 or higher, you must add the @JavascriptInterface
annotation to any method that you want available to your JavaScript, and the method must be public. If you do not provide the annotation, the method is not accessible by your web page when running on Android 4.2 or higher.
In this example, the WebAppInterface
class allows the web page to create a Toast
message, using the showToast()
method.
You can bind this class to the JavaScript that runs in your WebView
with addJavascriptInterface()
and name the interface Android
. For example:
This creates an interface called Android
for JavaScript running in the WebView
. At this point, your web application has access to the WebAppInterface
class. For example, here's some HTML and JavaScript that creates a toast message using the new interface when the user clicks a button:
There's no need to initialize the Android
interface from JavaScript. The WebView
automatically makes it available to your web page. So, when a user clicks the button, the showAndroidToast()
function uses the Android
interface to call the WebAppInterface.showToast()
method.
Note: The object that is bound to your JavaScript runs in another thread and not in the thread in which it was constructed.
Caution: Using addJavascriptInterface()
allows JavaScript to control your Android app. This can be a very useful feature or a dangerous security issue. When the HTML in the WebView
is untrustworthy (for example, part or all of the HTML is provided by an unknown person or process), then an attacker can include HTML that executes your client-side code and possibly any code of the attacker’s choosing. As such, you should not use addJavascriptInterface()
unless you wrote all of the HTML and JavaScript that appears in your WebView
. You should also not allow the user to navigate to other web pages that are not your own, within your WebView
. Instead, allow the user’s default browser application to open foreign links—by default, the user’s web browser opens all URL links, so be careful only if you handle page navigation as described in the following section).
When the user clicks a link from a web page in your WebView
, the default behavior is for Android to launch an app that handles URLs. Usually, the default web browser opens and loads the destination URL. However, you can override this behavior for your WebView
, so links open within your WebView
. You can then allow the user to navigate backward and forward through their web page history that's maintained by your WebView
.
Note: For security reasons, the system’s browser app doesn’t share its application data with your app.
To open links clicked by the user, provide a WebViewClient
for your WebView
, using setWebViewClient()
. For example:
All links the user clicks load in your WebView
.
If you want more control over where a clicked link loads, create your own WebViewClient
that overrides the shouldOverrideUrlLoading()
method. For example:
Then create an instance of this new WebViewClient
for the WebView
:
Now when the user clicks a link, the system calls shouldOverrideUrlLoading()
, which checks whether the URL host matches a specific domain (as defined above). If it does match, then the method returns false in order to not override the URL loading (it allows the WebView
to load the URL as usual). If the URL host does not match, then an Intent
is created to launch the default Activity for handling URLs (which resolves to the user's default web browser).
Navigating web page history
When your WebView
overrides URL loading, it automatically accumulates a history of visited web pages. You can navigate backward and forward through the history with goBack()
and goForward()
.
For example, the following shows how your Activity
can use the device Back button to navigate backward:
The canGoBack()
method returns true if there is actually web page history for the user to visit. Likewise, you can use canGoForward()
to check whether there is a forward history. If you don't perform this check, then once the user reaches the end of the history, goBack()
or goForward()
does nothing.
Handling device configuration changes
During runtime, activity state changes occur when a device’s configuration changes, such as when users rotate the device or dismiss an input method editor (IME). These changes cause a WebView
object's activity to be destroyed and a new activity to be created, which also creates a new WebView
object that loads the destroyed object's URL. To modify your activity’s default behavior, you can change how it handles orientation
changes in your manifest. To learn more about handling configuration changes during runtime, read Handling configuration changes.
Managing windows
By default, requests to open new windows are ignored. This is true whether they are opened by JavaScript or by the target attribute in a link. You can customize your WebChromeClient
to provide your own behavior for opening multiple windows.
Caution: To keep your app more secure, it’s best to prevent popups and new windows from opening. The safest way to implement this behavior is to pass "true"
into setSupportMultipleWindows()
but not override the onCreateWindow()
method, which setSupportMultipleWindows()
depends on. Note that this logic prevents any page that uses target="_blank"
in its links from loading.
- https://habr.com/post/262167/
- https://prowebber.ru/internet/i-mobile-apps/23070-universal-android-webview-app-v250.html
- https://android-tools.ru/coding/webview-v-android/
- https://developer.android.com/reference/android/webkit/webview
- https://developer.android.com/guide/webapps/webview