Реальный опыт использования WebView (JavaScript/HTML) при разработке JavaFx приложения

от автора

image
Базовым UI фреймворком для нашего приложения был выбрана JavaFX. JavaFX прекрасно показала себя, в этой же статье мы хотели сконцентрироваться на одном компоненте JavaFX — WebView.

Мы при разработке нашего приложения — интерфейса COLT использовали набирающий среди девелоперов подход, когда часть компонентов реализуется на JavaScript/HTML.

Компонент на базе HTML/JS — это обычный Java класс, обычный JavaFx компонент с лайаутом — HBox или просто Box, который содержит в себе экземляр компонента Webview.

Как создать экземпляр webkit в JavaFx и подгрузить HTML

WebView webView = new WebView(); WebEngine engine = webView.engine engine.load(this.getClass().getResource("html/webview.html").toExternalForm())

Что мы получили использовав web технологии в нашем приложении.

Тонны готовых решений

jQuery, D3 покрывает почти все наши задачи. Нужно готовое решение — находим через гугл за 5 минут. Здорово что аналогами элементов интерфейса заполнен под завязку весь интернет. Причем все в открытом доступе. Можно подглядеть идею и взять ее реализацию.

Дешево

На Java такое же, что мы реализовали на JS/HTML написать было бы непросто.

Например, этот компонент для добавления путей. За основу мы взяли движок для tag-ов и хорошенечко его «допилили». Получилась очень умная и приятная штука. Умеет редактировать в режиме «тэгов», а по двойному клику еще позволяет просто работать с текстом. Работа с клипбоардом.

image

До этого, в предыдущей версии интерфейса, у нас использовался уродливый list-view. Наш новый fileset прост, компактен, современен. Есть идеи как еще можно расширять его фунциональность.

Второй пример — log-view.

image

Сначала мы хотели сделать компонент на основе ListView. Особых проблем с со скинованием мы не получили, но все же компонент получился малопригодным для реального использования пользователями. Например, нельзя было выделить логи как текст, сразу несколько блоков, реализация компонента грозила вылиться в немалый объем кода. Компонент на HTML получился легким и легко расширяемым. Нужно отметить что JavaFX использует GPU для рендеринга DOM, поэтому компонент получился достаточно производительным. Компонент мы планируем расширить поиском по логам, фильтрами и т.д. Все это мы планируем реализовать средствами JavaScript/CSS.

Возможно мы просто не настолько хороши в Java-UI, но нам действительно было сложно писать на Java компоненты со сложной интерактивностью. Там где нужен готовый компонент, пример который можно «нагуглить» — да все легко и просто. Связать через байндинг данные и «вьюхи» — опять очень просто и привычно (наш предыдущий опыт — Adobe Flex). Но когда дело касается чего-то выходящего за рамки стандартных, описанных в документации кейсов — мы теряли слишком много времени на ресерч.

Мы выработали для себя следующую схему — пытаемся сделать через JavaFx-компоненты, и если «упираемся в стену», или излишне усложняется реализация, то пишем на HTML. Такой подход значительно ускорил нашу разработку.

Кроссплатфоменность

Движок Webview в JavaFX — это webkit. На любой платформе, которую поддерживает Java. Мы практически не тратили время на кросс платфоменность.

Java-Javascript Bridge

Связка java-javascript, мост между ними позволяет делать вызовы методов Java-классов из HTML, а так же вызывать код JavaScript из Java. Простой пример код на Java:

private void clear() {     JSObject windowObject = (JSObject)webView.getEngine().executeScript("window"); 		windowObject.call("test", new ArrayList(1, 2, 3)); }

А этот код JavaScript который принимает данные и вызывает методы ArrayList java —

function test(list){ 	console.log("list.size() - " + list.size();//3 	console.log("list.get(0) - " + list;//1 }

Пример конечно же не совсем реальный, синтетика, но видно, что методы вызываются, и данные принимаются.

Вы можете при загрузке html, добавить ссылку на приложение прямо в объект window.

JSObject windowObject = (JSObject) webView.engine.executeScript("window");  					windowObject.setMember("app", this);

И вызывать методы Java из кода JavaScript —

app.addData($("my-input").val());

Более правильным (более безопасным) подходом является создание в Java окружении специального объекта, который ограничивает доступ, и позволяет из JavaScript вызывать в приложении только «разрешенные» методы.

windowObject.setMember("app", new JSBridge(){      public myMethod(){         MyApp.this.myMethid();      } });

Еще один пример связки через JS «alert». Нам нужно было отловить «load» html — полную инициализацию приложения. Все другие методы оказались не так надежны. Что мы сделали.

Как обычно в JavaScript через jquery:

$(function(){ 	alert("command:ready"); }); 

В Java добавили обработчик «onAlert» для webview.enging и теперь мы точно знаем что HTML загружен и инициализирован.

engine.onAlert = new EventHandler<WebEvent<String>>() {     @Override     void handle(WebEvent<String> event) {         if("command:ready".equals(event.getData())){ 		//TODO: initialize         }     } }

Переиспользование кода

Код который мы написали для нашего приложения для компьютеров мы планируем переиспользовать для мобильной разработки. На мобильном приложении (objective-c/java) мы так же создадим компонент на на основе webview большая часть фунциональности будет будет уже готовой.

C какими сложностями мы столкнулись при работе с HTML/JS в javafx?

Первое. Жизненный цикл загрузки документа. Вызывать методы в JS можно только после полной загрузки страницы, в документации предлагается путь — отслеживать в JAVA событие смены состояния страницы. Такой подход оказался достаточно глючным. Как упоминал ранее связка события «load» на стороне html и перехват «alert» на стороне Java, решил эту проблему.

Вторая проблема, которая попила у нас кровь — это клюки с вызовами одновременно нескольких javascript функций в разных компонентах с webview. Причем такой баг вылез достаточно недавно и очень похоже на оптимизацию, которую добавили в движок webkit в javafx. В любом случае, можно получить ошибку о бесконечной рекурсии в javascript буквально на пустом месте. Такая проблема решается достаточно легко оборачиванием вызова Platform.runLater() обращений к Javascript из Java..

Третья проблема. Дебаг и логгинг.
На первом этапе работы, когда идет прототипирование и вы работаете только с HTML/JS можно тестировать просто в браузере. На этапе интерграции с Java ваш код просто перестает работать без правильного окружения. И вы все чаще и чаще начинаете собирать Java приложение для того чтобы потестировать и подправить фукнционал на JavaScript. А билд Java приложения и его запуск это долгие секунды ожидания. Какие приемы в работе мы использовали, чтобы такую долгую загрузки избежать и не терять время?

Простой и очевидный пусть — добавить «релоад» страницы по комбинации клавиш. Подправил HTML/CSS/JS — нажал F5 — содержимое WebView перегрузилось:

$(document).keydown(function (e) {     if(e.keyCode == 116 /*F5*/){         location.reload();     } });

Но кроме быстрого рефреша нам понадобится просмотр логов.
Достаточно простым решением было добавить FireBug Lite —

<script type='text/javascript'             src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>

Но настоящим прорывом, для нас и решением большинства кейсов стало использование нашего же инструмента COLT. Да и логично, не так ли?

Как подключить? Просто добавляем в html документ следующий код

var url = "http://<address>:<port>/webview.html"; if (location.href != url) {     location.href = url }

Где url — адрес по которому COLT запускает трансформированную страницу. Узнать его просто. Создаем проект COLT, указываем ссылку на нашу страницу — «Main Document» и запускаем сессию жмем «зеленую молнию».

image

Страница будет открыта в браузере. Копируем пусть к странице и вставляем в наш код. Окно браузера закрываем. Теперь запускаем JavaFx приложение. Пробуем изменить содержимое страницы, javascript и прямо в JavaFx приложении будет работать livecoding.

В новой версии, в настройках, в блоке «Advanced», мы добавили специальное тектовое поле с данным снипетом. Без старта проекта вы сможете скопировать данный код прямо из настроек COLT.

image

Логи будут появляться в окне COLT. Чтобы в следующий раз кольт открывал не браузер, а запускал ваше приложение, можно выбрать запуск не браузера, а запуск с консоли. В нашем проекте, мы просто скопировали текст из output нашей run-configuration IDEA и добавили этот вызов в COLT console luncher.

image

image

Теперь при нажатии на «зеленую бровь» будет запущено окно Java приложения.

В целом подход с редиректом внутри HTML на live-страницу универсален. Он, например, прекрасно работает и на PhoneGap. Приложение запускается на мобильное устройство (главное чтобы устройство было в той же локальной сети) и обновления доставляются без необходимости рестарта. Нужно запомонить порядок действий — и можно применять везде, где есть WebView.

Хорошей практикой стало добавлять функцию-обработчик события изменнения кода.

//@code-update function live(){     console.log("live update"); }

Аннотацию @code-update перехватывают наши AST трансформации и добавляют листенер на событие обновления кода.

Типичный кейс работы с событием обновления кода, это вызывать функции в коде, то есть то что мы обычно пишем в консоль FireBug — поменять значение или вызвать функцию, когда приложение запущено. Добавил код, сохранил — код выполнился.
Плюс удобно трейсить, выводить данные, значение которых нужно узнать в рантайм, без остановки кода, дебага, вы можете просто вывести их на консоль.

Groovy

Так же несколько выходящим за рамки темы статьи, вывод, который мы сделали при реализации нашего проекта и которым бы хотели поделиться, что писать UI лучше на языке более гибким чем Java. Мы воспользовались для этого Groovy и очень довольны. Количество кода сократилось раз в десять, а работа с XML и файловой системой упростилась так же на порядок. AST трансформации для создания Bindable свойств, наши трансформации позволили подмешивать сервисы в контроллеры, генерировать Bindable обертки для простых данных. И так далее. Что называется must have. Про использвоание Groovy для JavaFx мы пишем отдельную статью.

Планы

Подход, который мы использовали в разработке нашего настольного приложения, мы начали применять и для мобильной разработки. Мы используем PhoneGap+COLT и кейс практически ничем не отличается от JavaFX+HTML+COLT.

Выбрали PhoneGap, а не другую платформу так как уже было накоплено много готового в desktop проекте и переписывать заново на другую платфому нам показалось нецелесообразным. Если наше приложение потребует отказаться от PhoneGap, мы мы перепишем часть приложения на натив, но весь фунционал, который написан на HTML/JS мы не потеряем. Они просто будут под другому запускаться.

Сайт проекта codeoerchestra.com

ссылка на оригинал статьи http://habrahabr.ru/company/codeorchestra/blog/198776/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *