Software Development

Mocne i słabe strony JavaFX

Kwiecień 6, 2016 4
Podziel się:

Ponad rok temu stanąłem przed problemem wyboru odpowiedniej technologii dla zbudowania aplikacji desktopowej. Musiałem szybko zbudować sporo ekranów, które byłbym gotów zaprezentować naszemu klientowi, jako prototyp działającego rozwiązania. Ponieważ do tamtej pory zajmowałem się tylko i wyłącznie tworzeniem systemów dostępnych przez przeglądarkę internetową w JAVA-ie, zacząłem szukać porad w Internecie krążąc wokół tego języka programowania. Byłem wręcz przekonany, że w 2015 roku istnieją już biblioteki, narzędzia i przykłady – w jaki sposób szybko zbudować aplikację standalone w tej właśnie technologii. Wiedziałem od dawna o tym, że istnieje Swing (ponieważ Swing istnieje od dawna :)), ale sposób tworzenia aplikacji w Swingu wydawał się dość toporny. Szybko natrafiłem na JavaFX, który został dołączony do JDK już od wersji 7. Po bliższym przyjrzeniu się temu frameworkowi doszedłem do wniosku, że jest to właśnie to, czego szukałem. JavaFX umożliwia budowanie widoków w XML-u, obsługuje style pisane w CSS-ach, a na dodatek istnieje JavaFX Scene Builder z pomocą, którego w miarę szybko przygotuję to, czego potrzebuję. Z doświadczenia wiedziałem, że każda świeża technologia niesie ze sobą także i problemy, ale chęć poznania czegoś nowego przyćmiewała migającą czerwoną lampkę w mojej głowie. Zabrałem się do pracy…

Krótko o tym, czym jest JavaFX i jak działa

JavaFX została opracowana, jako następca Swinga i jest obecnie rekomendowana przez dostawcę tego oprogramowania – firmę Oracle do tworzenia GUI, między innymi w samodzielnych aplikacjach. Interfejs użytkownika od początku do końca możemy tworzyć w „czystej” JAVA-ie, ale istnieje także możliwość wykorzystania w tym celu plików FXML.

Pierwszy sposób jest bardzo podobny do pisania aplikacji w Swingu, gdzie krok po kroku, linijka po linijce kładziemy kontrolki na zdefiniowanych wcześniej panelach. Dużo ciekawszym podejściem jest drugi sposób, pozwalający na wyraźniejsze oddzielenie warstwy widoku od kontrolera.

Pliki FXML to nic innego jak XML z odpowiednimi znacznikami wewnątrz. Inicjalizacją obiektów naszego widoku, ustawieniem ich właściwości oraz stworzeniem zależności między nimi zajmuje się wówczas za nas klasa FXMLoader.

Pomimo, że użycie i zaprojektowanie widoku w plikach FXML jest znacznie szybsze i wygodniejsze, nie da się uniknąć tworzenia kontrolek w klasach obsługujących widoki (Controlers). Wszędzie tam gdzie będziecie chcieli dynamicznie zarządzać położeniem i ilością kontrolek na ekranie, trzeba będzie w mniejszym lub większym stopniu obsługę takich przypadków zaimplementować w klasach. Warto przemyśleć na początku projektowania aplikacji gdzie i ile będzie takich miejsc, ponieważ mogą kosztować trochę więcej nakładu pracy.

Lepiej używać JavaFX od JDK 8

Choć JavaFX pojawiła się już od JDK 7 to należy zwrócić uwagę na fakt, że w wersji 8 została znacząco poprawiona i ulepszona. Oprócz poprawy kilku istotnych błędów, odświeżony został całkowicie podstawowy wygląd.

Dla mnie istotny wpływ miały właściwie trzy rzeczy, jeśli chodzi o różnicę pomiędzy JDK 7 i 8:

  • w starszej wersji JAVA-y nie mogłem użyć biblioteki zewnętrznej, którą chciałem wykorzystać do walidacji, a mianowicie ControlsFX,
  • błąd skalowania czcionek na różnych systemach operacyjnych w konsekwencji wymusił określanie explicite rozmiaru czcionki przy każdym użyciu chociażby obiektu javafx.scene.control.Label,
  • błędy związane z odświeżaniem styli CSS przypisanych do danej kontrolki spowodowały konieczność kasowania określonej klasy stylu i ustawiania go ponownie.

Przed wdrożeniem aplikacji na stacjach roboczych (np. w środowisku klienta) proponuję jednak najpierw upewnić się, jaka wersja JRE będzie zainstalowana na komputerach. W wielu korporacjach istnieją standardy zainstalowanego oprogramowania i może okazać się, że nie będziemy mogli w ogóle uruchomić aplikacji.

Wielojęzyczność UI

W JavaFX nie znajdziemy gotowych mechanizmów wspierających przygotowanie UI dla wielu języków. Aplikacje, które udało mi się zbudować nie miały takiego wymagania, ale należy to wziąć pod uwagę przy tworzeniu nowego programu. Wyświetlane teksty można definiować zarówno w plikach FXML jak i klasach je obsługujących. O ile zastosowanie mechanizmu properties (podobnego do tego w Spring MVC) w klasach łatwo można sobie wyobrazić i zrealizować, to zamiana tekstów w plikach FXML, będzie wymagała najprawdopodobniej rozszerzenia standardowych kontrolek.

Przygotowanie modelu danych

Kolejnym istotnym elementem w projektowaniu naszej aplikacji jest zamodelowanie klas będących nośnikiem danych i związanie ich z kontrolkami używanymi na ekranie. Przez związanie mam na myśli użycie np. interfejsu javafx.beans.value.ChangeListener, służącego do aktualizowania wartości w modelu. Takie podejście będzie miało zastosowanie wszędzie tam gdzie warstwa bakcend-u czy serwisowa będzie przetwarzać te dane w bardziej złożonych algorytmach.

Na prostych ekranach z prostą logiką możemy spokojnie wykorzystywać klasę javafx.scene.Scene i jej przydatną metodę lookup do wyszukiwania obiektów na aktywnej formatce. Należy pamiętać jednak o tym, że metoda getScene dostępna przy każdej kontrolce lub panelu będzie zwracała wartość null w przypadku, w którym używana kontrolka nie będzie znajdowała się na aktualnie wyświetlanym/aktywnym ekranie. Z taką sytuacją będziemy mieli do czynienia przy przechowywaniu całych ekranów wraz z wypełnionymi wartościami w pamięci programu. Dostęp do danych zawartych na takich elementach będzie wówczas utrudniony.

JavaFX Scene Builder

Bardzo przydatnym narzędziem służącym do projektowania interfejsu użytkownika jest aplikacja JavaFX Scene Builder. Dzięki niej przygotujemy szybko odpowiedni plik FXML. Jeśli chodzi o „rapid development” średnio złożonych ekranów to właśnie to oprogramowanie pozwala na szybkie przygotowanie kompletnych makiet. Można sobie wyobrazić, że takim samym narzędziem będzie posługiwał się analityk pracujący z klientem nad uszczegółowieniem wymagań odnośnie aplikacji. Opracowywać może gotowe kawałki UI używane potem przez programistów do spięcia wszystkiego w całość.

JavaFX Scene Builder

Rysunek 1 Ekran główny programu JavaFX Scene Builder

W JavaFX standardowe kontrolki możemy rozszerzać o dodatkowe właściwości wykorzystywane w aplikacji. Możemy to zrobić poprzez dziedziczenie po ich klasie bazowej. Przykład rozszerzenia elementu javafx.scene.control.TextField pokazuje poniższy fragment kodu.

2

Zdefiniowane przez nas nowe pole będzie widoczne w JavaFX Scene Builder na liście Properties po prawej stronie ekranu w sekcji Custom.

3

Rysunek 2 Dodanie nowego pola do standardowej kontrolki TextField

Obok wielu zalet oprogramowanie to posiada także wady. Pierwsze z nich to takie, że nie potrafi dokładnie odwzorowywać styli CSS zdefiniowanych w pliku zewnętrznym. Miałem problem z podłączeniem styli w taki sposób, aby wygląd projektowanego interfejsu odpowiadał temu w skompilowanej i uruchomionej aplikacji.

W przypadku, w którym plik FXML jest większy lub gdy chcemy podzielić ekrany na stałe kawałki, możemy zastosować klauzulę include jak na poniższym przykładzie.

4

Problem w tym, że wówczas stracimy możliwość pełnej edycji takiego pliku w programie JavaFX Scene Builder. Plik FXML, który składa się z plików podrzędnych zostanie wyświetlony w narzędziu bez możliwości edycji zagnieżdżonych elementów. Jeśli chcemy edytować zagnieżdżony kawałek powinniśmy plik otworzyć w osobnym oknie programu. Jest to bardzo duża wada, która mocno utrudnia budowanie bardziej złożonych ekranów i aplikacji.

Wydajność oraz zużycie pamięci

Jeśli chodzi o kwestie wydajnościowe to pamiętać należy o jednym istotnym elemencie, który należy brać pod uwagę przy budowaniu skomplikowanych ekranów. Wczytanie pliku FXML oraz powołanie do życia wszystkich obiektów i komponentów z nim związanych potrafi zająć zauważalną chwilę (w opracowanej przeze mnie produkcyjnej aplikacji nawet do ok. 1 sek.). Trzeba o tym pamiętać przy projektowaniu przepływu ekranów. Np. jeżeli chcemy tworzenie ekranu tzn. wczytanie pliku FXML podłączyć pod zaznaczany komponent CheckBox, opóźnienia we wczytywaniu mogą być irytujące dla użytkownika.

JavaFX ma spore zapotrzebowanie na pamięć i niewielkie aplikacje mogą zajmować już sporo naszej pamięci operacyjnej. Nawet takie funkcje jak przełączanie ekranów (przy założeniu, że zbudowane ekrany trzymamy w pamięci i jedynie zmieniamy „dzieci” jednego z paneli głównych) potrafi znacząco podbijać zużycie pamięci. Osobom zainteresowanym polecam dogłębne zapoznanie się z wieloma wpisami w sieci na temat Memory leaks in JavaFX. Dystrybucja aplikacji

Na pewno prędzej czy później staniecie przed zagadnieniem dystrybucji Waszej aplikacji w organizacji klienta. Można to zrobić na dwa sposoby, które były brane przeze mnie pod uwagę. Żaden z nich nie jest niestety pozbawiony wad.

Pierwszy sposób to dystrybucja naszej aplikacji, jako biblioteki uruchamialnej JAR. Tego typu aplikacje są rzadko spotykane, ponieważ nasz kod może zostać w bardzo łatwy sposób rozpakowany, zdekompilowany i użyty przez kogoś innego. Oczywiście można użyć programów do zaciemniania kodu aplikacji, ale i tak pozostają pliki FXML, czy inne zasoby, które muszą pozostać w niezmienionej formie.

Drugi sposób to dystrybucja naszej aplikacji w pliku EXE. Determinuje to od razu rodzinę systemów operacyjnych, na których aplikację da się uruchomić w tym przypadku to Windows. W moim wdrożeniu sprawdziło się właśnie takie podejście. Wygodnym narzędziem do zamiany JAR na EXE jest oprogramowanie Launch4J. Jest prosty w obsłudze i posiada wszystkie niezbędne funkcjonalności, z których bardzo przydatną jest możliwość zdefiniowania komunikatów pokazywanych w zależności od wystąpienia problemów tj. błędy uruchomienia, brak wymaganej wersji JRE, błędy instalacji itp.

Podsumowanie

Reasumując, JavaFX to technologia, w której szybko można stworzyć średnio skomplikowaną aplikację, ale zalecam jej używanie dopiero od wersji JDK 8. Na dzień dzisiejszy jest młodą technologią, która ma jeszcze kilka istotnych ograniczeń. Mam nadzieję, że w kolejny wersjach JAVA-y zostanie mocno rozwinięta i poprawiona. Na pewno chciałbym jeszcze zbadać w niej realizację obsługi grafiki 3D. Jeśli uda mi się zdobyć doświadczenie w tym obszarze, poczytacie o tym w kolejnych moich wpisach.

5 / 5
Tomasz Czerwonka
Autor: Tomasz Czerwonka
Pracuję na stanowisku Architekta rozwiązań w SII. Jestem odpowiedzialny za rozwój i utrzymanie systemów budowanych w oparciu o język JAVA. Prywatnie szczęśliwy mąż i ojciec oraz początkujący gitarzysta :)

Imię i nazwisko (wymagane)

Adres email (wymagane)

Temat

Treść wiadomości

komentarze(4)

avatar'
Michał Małecki
9 kwietnia 2016 Odpowiedz

Czy launch4j należy traktować jako sposób ukrycia naszego kody i fxmli? Czy rozwiązania typu http://www.nirsoft.net/utils/resources_extract.html nie pozwalają na dostęp do jaru w szybki sposób?
Myślałem że launch4j to raczej uproszczenia aplikacji, w przypadku braku w podstawowych zależnościach, jak np java

    Tomasz Czerwonka
    13 kwietnia 2016 Odpowiedz

    W moim przypadku właśnie chodziło o uproszczenie aplikacji na tyle, żeby dało się ją szybko wdrożyć na standardowych stacjach roboczych w środowisku klienta. Dostęp do jar i wgląd w kod nie miał aż takiego znaczenia, ze względu na to, że aplikacja jest używana przez użytkowników wewnętrznych w organizacji. W artykule celowo nie wchodziłem w temat ukrywania kodu, ale rzeczywiście jest to istotny element w przypadku, w którym udostępniamy aplikację na zewnątrz.

avatar'
Maciej Dobrowolski
11 kwietnia 2016 Odpowiedz

Panie Tomku,
wsparcie dla i18n w plikach FXML jest i sprawdza się całkiem w porządku (zarówno z poziomu kodu jak i SceneBuilder'a) - problemy są, ale szczątkowe - nie ma na przykład prostego sposobu na przetłumaczenie menu kontekstowego kontrolki pola tekstowego. Poza tym - jest okej.

Jak wspomniał Michał w poprzednim komentarzu - Java to Java i kod można co najwyżej mniej lub bardziej skutecznie zaciemnić (np. proguard)/szyfrować (przy okazji - HASP jest tutaj arcyciekawym rozwiązaniem).

Jeśli zaś chodzi o dystrybucje, to warto także wspomnieć o czymś takim jak fxlauncher i podobne, które bardzo skutecznie dostarczają mechanizmy auto-update. Mamy także Java Web Start - z tego Oracle się niestety wycofuje, ale nadal jest wspierane.

No i najważniejsze - najbezpieczniejszą dla aplikacji dystrybucją jest jej dystrybucja razem z JRE - znajdzie się to w prawie każdym komercyjnym projekcie (oprócz tego nawet JavaFX Packager w domyślnej konfiguracji także pakuje JRE do paczki z aplikacją).

JavaFX ma już całkiem nieźle rozwinięte comminity oraz bazę kodu open-source - warto poczytać o Griffon, afterburner i wielu innych, które znakomicie upraszczają tworzenie dużych, komercyjnych aplikacji - tutaj seria przykładów https://dlemmermann.wordpress.com/2015/10/22/javafx-real-world-apps-emirates-airlines-network-capacity-optimization/.

    Tomasz Czerwonka
    13 kwietnia 2016 Odpowiedz

    Panie Macieju,
    Dziękuję za uzupełnienie mojego artykułu swoim merytorycznym komentarzem - jest to cenna wiedza. Ma Pan rację jeśli chodzi o wsparcie dla i18n w plikach FXML. Akapit "Wielojęzyczność UI" w tym kontekście jest mylący. Mam nadzieję, że ktoś kto będzie czytał ten artykuł dojdzie także i do komentarzy :)

Zostaw komentarz