Мотивация
Кому могут понадобиться Desktop приложения, использующие веб-страницы в качестве UI? Отвечу прямо — всем! Всем кто страдает, пользуясь неудобными и некрасивыми приложениями.
Для меня, как для разработчика, очень важно делать пользовательские интерфейсы удобными, интересными и позволяющими пользователям не отвлекаться от работы. Кроме того, очень хочется, чтобы интерфейсы были привлекательными и их было приятно показывать. Все эти соображения приводят нас к неутешительному выводу: существующие UI фреймворки для настольных приложений ужасны.
Так что же такое есть в веб-приложениях, чего нет в настольных?
- Разнообразие визуального оформления
- Адаптивные и идеально-масштабируемые интерфейсы
- Развитые анимации
- Богатый набор компонентов и библиотек для разработки UI
Похожими возможностями обладают WPF для .NET приложений и JavaFX из мира Java. Но их компоненты не могут угнаться за стремительно развивающимися web-технологиями.
Системный кризис UI фреймворков в Java
Если мы поближе посмотрим на JavaFX, то увидим что многие идеи Web проникли в этот фреймворк. Самая главная технология UI в web-приложениях — CSS, позволяющая великолепно выглядеть вашим приложениям. JavaFX поддерживает её для стилизации компонентов, пополняя набор стандартных CSS атрибутов своими специфичными атрибутами. При этом поддержка стандартных атрибутов CSS в компонентах JavaFX похожа на лоскутное одеяло с большими дырками и заплатками.
Поищите в Java мире что-нибудь похожее по возможностям на JavaFX и вы сильно расстроитесь. Ничего нет! Есть жуткий Swing, есть сторонние технологии, позволяющие использовать Qt для Java, но все эти технологии являются полумерами. Так что если вы хотите написать Desktop приложение на Java, то вам придётся использовать JavaFX.
Когда вы воспользуетесь JavaFX, вы заметите, как мало сторонних компонентов написано, и как мало людей по-настоящему полагаются на JavaFX. Это всего лишь одна из технологий.
Спросите любого Java разработчика, какой интерфейс лучше всего сделать для Java-приложения? Вы получите один ответ, лучший интерфейс для Java-приложения — это веб-интерфейс.
Но есть некоторые области, в которых до сих пор нельзя обойтись без Desktop приложений:
- Приложения CALL-центров для приёма заказов такси
- Приложения, требующие поддержку работы Offline
А ведь как здорово было бы использовать HTML/CSS/JS для реализации UI в этих приложениях?
Как должно выглядеть HTML5 приложение?
Представьте, что вы запустили ваш любимый web-сайт вне браузера в виде Desktop приложения и оно выглядит, как если бы это была отдельная вкладка браузера без лишних меню, декораций и панелей, так как если бы это было приложение, которому доступны нотификации на рабочем столе и интеграция с другими приложениями, автоматическое обновление и оно может работать в отсутствии интернета.
О, это было бы волшебно!
Как реализовать такое приложение на Java?
Последнее время довольно популярным стал подход, продвигаемый фреймворками Electron и node-webkit. Основная идея — скомпилировать вместе Node.JS и браузер Chromium, предоставив среду для разработки Desktop приложений на JavaScript. В такой схеме Electron предоставляет JavaScript API для возможностей, специфичных для Desktop приложений, таких как уведомления, нативные меню и многое другое, а само приложение разрабатывается на JavaScript.
Desktop приложения на базе Electron включают две составляющие:
- Backend в виде Node.JS приложения
- Frontend в виде JavaScript компонентов или Single-Page-Application
Но наше горе, как Java разработчиков, состоит в том, что наши приложения написаны на Java и мы не хотим их переписывать на JavaScript.
Я изучил этот вопрос и выделил пару живых вариантов реализации HTML5 UI в Desktop приложении на Java:
- JavaFX WebView + Jetty
- Java CEF + Jetty
Поскольку само Desktop приложение будет так же написано на Java, то мы сможем использовать все фишки Desktop приложений: создавать дополнительные окна, показывать нативные меню, добавлять иконки в трей и показывать уведомления.
JavaFX WebView
WebView — специальный компонент JavaFX, позволяющий отображать web-контент в приложении. WebView использует специальный движок на базе webkit, интегрированный в Java Runtime.
1 |
<WebView fx:id="webview" prefHeight="200.0" prefWidth="200.0" VBox.vgrow="ALWAYS"/> |
1 2 3 4 |
// Загружаем страницу в Platform.runLater при открытии окна, чтобы не тормозить UI-поток Platform.runLater(() -> webview.getEngine().load("http://localhost:8080/app") ); |
Преимущества WebView:
- Работает на всех платформах: Linux / MacOS / Windows
- Отображает страницы довольно быстро
- Имеет проработанный Java API
- Удобные интерфейсы вызова JavaScript кода из Java
- Возможность предоставлять JavaScript коду API Java приложения
Недостатки:
- Рендеринг отличается от Chrome в худшую сторону, особенно заметно отличие в рендеринге шрифтов
- Производительность отрисовки и CSS анимаций в среднем в 3-4 раза хуже чем в Chrome
- Реализация webkit обновляется крайне редко
Я попробовал использовать WebView для встраивания приложений на базе фреймворка Vaadin и заметил эти недостатки, которые определённо не понравятся пользователям. Несмотря на замеченные проблемы, стоит использовать WebView при создании простых приложений или если UI приложения вы будете сами реализовывать на HTML/CSS и при этом полностью контролируете процесс.
Пример использования WebView в HTML5 Desktop приложении вы найдёте на Github: https://github.com/jreznot/fxc
Chromium Embedded Framework
CEF — фреймворк для встраивания Chromium в нативные приложения. Активно развивается и применяется в приложениях для отображения web-контента. Основная цель разработки фреймворка — предоставить удобный API к возможностям Chromium. Распространяется под лицензией BSD. Имеет биндинги для многих языков программирования.
Сайт CEF: https://bitbucket.org/chromiumembedded
Для Java существует биндинг JCEF: https://bitbucket.org/chromiumembedded/java-cef Он позволяет отображать в приложениях на базе Swing встроенное окно CEF.
Вот так выглядит код окна Swing, в которое встроен фрейм CEF:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
public class CefcWindow extends JFrame { public void init() { CefApp.addAppHandler(new CefAppHandlerAdapter(null) { @Override public void stateHasChanged(org.cef.CefApp.CefAppState state) { // Shutdown the app if the native CEF part is terminated if (state == CefApp.CefAppState.TERMINATED) { System.exit(0); } } }); CefSettings settings = new CefSettings(); settings.windowless_rendering_enabled = false; CefApp cefApp = CefApp.getInstance(settings); CefClient client = cefApp.createClient(); CefBrowser browser = client.createBrowser("http://localhost:8080", false, false); Component cefBrowserPane = browser.getUIComponent(); getContentPane().add(cefBrowserPane, BorderLayout.CENTER); pack(); setSize(1280, 800); setVisible(true); Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); setLocation(screenDimension.width / 2 - this.getSize().width / 2, screenDimension.height / 2 - this.getSize().height / 2); addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { CefApp.getInstance().dispose(); } }); } } |
Преимущества CEF:
- Идеальный рендеринг, максимально близкий к Chrome
- Регулярные обновления движка Blink
- Отличная производительность отрисовки и анимаций
Недостатки:
- Бинарные сборки, которые нужно поставлять вместе с приложением
- Отсутствие готовых сборок JCEF, требуется вручную собирать версии JCEF под все поддерживаемые платформы, поскольку JCEF использует JNI для вызова библиотек CEF
- API, который отталкивается от возможностей CEF
- Не всегда стабильные сборки JCEF
Если вы хотите решить проблему тотально, то советую использовать CEF. Пример реализации HTML5 Desktop приложения на CEF вы найдёте на Github: https://github.com/jreznot/cefc
Jetty
Jetty — универсальный сервер для Java приложений, который удобно встраивать в другие приложения. Основная особенность Jetty — API для программной настройки сервера. Так мы можем запустить сервлет в настольном приложении при помощи Jetty:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
Server embeddedServer = new Server(8080); ServletContextHandler contextHandler = new ServletContextHandler(null, "/", true, false); embeddedServer.setHandler(contextHandler); HashSessionManager manager = new HashSessionManager(); SessionHandler sessions = new SessionHandler(manager); contextHandler.setSessionHandler(sessions); ServletHolder servletHolder = new ServletHolder(CefcServlet.class); contextHandler.addServlet(servletHolder, "/*"); try { embeddedServer.start(); } catch (Exception e) { System.out.print("Server stopped\n" + e.getMessage()); e.printStackTrace(System.out); } |
Пример использования Jetty вместе с CEF вы найдёте здесь: https://github.com/jreznot/cefc
Перспективы
Надеюсь, что в скором времени разработка Desktop приложений на web-стеке придёт и в мир Java и мы сможем поставить крест на Swing!