Компания
28,47
рейтинг
24 октября 2013 в 14:15

Разработка → JavaFX WebView (HTML/JS) — используем web практики для разработки desktop приложений

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

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

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

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


WebView webView = new WebView();
WebEngine engine = webView.getEngine();
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-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.myMethod();
     }
});

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

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

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

В Java добавили обработчик «onAlert» для webview.engine и теперь мы точно знаем что 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 буквально на пустом месте. Такая проблема решается достаточно легко — оборачиваем вызов JavaScript из Java c помощью Platform.runLater().

Третья проблема. Дебаг и логгинг.
На первом этапе работы, когда идет прототипирование и вы работаете только с 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>


И конечно же, как только наше приложение «задышало» и заработал livecoding для HTML/JS мы стали использовать COLT для разработки COLT. На данный момент функционал нашего же приложения покрывает большинство кейсов — FireBug Lite нам уже не нужен. Лог, живое обновление, удаленное тестирование, ливкодинг. Ливкодинг на наш взгляд может заменить дебаг, но пользователи просят, и «настоящий дебаг с брейпойнтами» мы тоже прикрутим.

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

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

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

image

Страница будет открыта в браузере. Копируем пусть к странице и вставляем в наш JS код. Окно браузера закрываем. Теперь запускаем 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 мы не потеряем. Они просто будут по другому запускаться.

Сайт проекта codeorchestra.com
Автор: @codeorchestra
CodeOrchestra
рейтинг 28,47
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Комментарии (20)

  • 0
    Server not found

    Firefox can't find the server at www.codeoerchestra.com.
    • 0
      • 0
        У меня нормально. S3 глючит, видимо.
        • 0
          а, дошло, спасибо :)
  • 0
    Спасибо, очень интересно. Уже некоторое время интересуюсь JavaFX, в планах статья про использование JavaFX из Scala, а вот WebView не удалось пока пощупать.

    Я заметил у вас на скринах, что вы уже используете Java 8. Как ощущения от новой версии языка? Не страшно его использовать? Релиз ещё через почти через полгода. Хотя сейчас, наверное, там API уже не должен сильно меняться.
    • 0
      Особых проблем с Java 8 не обнаружили. Лямбда выражения очень нравятся. IDEA, которую мы используем отлично поддерживает синтаксис Java 8. Плюс в IDEA есть инструменты миграции. Но как упоминалось в статье, мы в основном пишем на Groovy и потестировали новую Java мы «по диагонали». Java 8 пользуемся так как JavaFX свежая требует только ее.
      Из минусов — пришлось включить в поставку JRE, чтобы не озадачивать пользователей. Плюсы — новый GPU рендеринг графики, новая javafx и нет проблем с permgen.
      • 0
        Ясно. Очень здорово, везёт вам) у нас же матёрый энтерпрайз — кое-где ещё 1.5 используется(
        А почему вы выбрали именно Groovy, а не, например, Scala или Clojure? Из-за схожести с Java синтаксисом?
        • 0
          > Из-за схожести с Java синтаксисом?
          именно так. Мы используем Java+Groovy. Где нужен больший контроль и прозводительность — Java.
          Нам было бы трудно прыгать туда-сюда. Плюс так сложилось исторически. Смотрели на другие языки JVM, но решили не дергаться.
          Ну еще фактор, что я эксперт по Груви, и настоял :)
          • 0
            Понятно, спасибо)
  • 0
    Еще есть схожий подход — пускать локальный вебсервер, логику реализовывать на php/perl/python/java, GUI на привычных js+html в окне браузера. Получаем кросс-платформенный продукт работающий на windows, mac, linux.

    Чтобы не тревожить файрволы на винде, есть специальные браузеры (на основе firefox) и веб-серверы которые общаются друг с другом не через TCP а через named pipes, например этот stunnix.com/prod/aws/overview.shtml
    • –1
      Именно так мы и поступили. Сначала долго писали на свинге. Потом тыркнулись в JavaFX 1.3 для написания игр. Это было ошибкой — Oracle оставила эту ветку без поддержки и выпилила JFX Script, не дав даже элементарного конвертера. JFX 2.x позиционировался как Swing 2.0 и новое поколение UI для java. Прикрутили новый графический композитный toolkit (prism), который долго пиарили: типа акселерация и все дела. Реально же скроллы ворочаются также медленно как и раньше. Плюс совершенно уродский лукандфил у контролов.
      Вобщем, мы радикально поменяли стратегию и попробовали Embedded Jetty + GWT + SmartGWT + IE ActiveX. Результаты превзошли ожидания! Сейчас пробуем Jetty + Vaadin + Chromium Embedded.

      P.S. я реально не вижу преимуществ у JavaFX перед уже существующими веб-технологиями. Кроме того, вполне возможно, что при низкой востребованности Oracle примет очередное решение, выпилив его обратно из Java, как это произошло с 1.3.
      • 0
        Если вам элегантный вид контролов JavaFX кажется уродским, то боюсь, это скорее особенность вашего персонального эстетического вкуса. Уж не говоря про то, как легко JavaFX контролы стилизуются под ваши нужды средствами CSS/FXML. Да и сторонние LaF уже появляются. Вот, к примеру, modena. Modena будет вторым стандартным стилем который будет в JavaFX 8. Помимо уже известного caspian.

        В любом случае, костыльный подход, основанный на локальном веб-сервере (да ещё и c непереносимыми никуда Active-X) и программирование десктоп приложения опираясь на несколько веб слоёв, которые ещё надо интегрировать с бизнес-логикой, имеет смысл только если у вас куча HTML/Javascript программистов и они тотально не способны обучиться писать на Java.
      • 0
        На заметку: юзал SmartGWT и ExtGWT (GXT). Второй (IMO)- на порядок структурирование и логичнее (правда платный).
  • 0
    Скажите а какой на сегодня статус у JavaFX? Я год назад им заинтересовался но вроде бы тогда народ поговаривал что не все в нем так стабильно как хотелось. Как с этим дела обстоят сейчас?
    • +1
      Могу ошибаться, но такой же как у Java 8. Статус бета.
      Релиз через полгода. По слухам начало 2014.
      Что не по мешало нам использовать JavaFX в релизе :)
      1. Есть глюки с рендерингом GPU на Win — когда компьютер уходит в hibernate то будет черное окно. Воспроизводится не везде — на отдельных видиокартах.
      2. Кое где вылезали глюки с лайаутом Menu. 3. Как писал вызовы из JS нужно было оборачивать runLater() хотя до этого все работало без таких оберток.
      Но это мелкие баги. В основном все работает очень стабильно.
      • 0
        JavaFX давным давно не бета. Уже не первый год. Были версии JavaFX 2.0, JavaFX 2.1, JavaFX 2.2.
        Просто сейчас она ещё не часть рантайма и ставится отдельно. Начиная с Java 8 она будет называться JavaFX 8 и станет частью стандартного Java рантайма (JRE) как swing.
        • 0
          Часть рантайма уже сейчас. По крайней мере свежие версии Oracle JDK 7 поставляются вместе с JavaFX 2.2.
          • 0
            М-м-м… Вроде она уже по умолчанию есть часть Oracle Java SE platform. Но чисто в голый JRE пока не входит.
            • 0
              Ну в JRE от оракла входит вроде как. А вот в OpenJDK, например, не входит, к сожалению. Про другие VM вообще без понятия.
              • 0
                Попытался найти когда включили в JRE. Действительно, вроде как начиная с Java 7u6 она стала уже полноценно включаться в JRE.

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

Самое читаемое Разработка