JavaFX Pitfalls What no documentation tells you Speaker Andy - - PowerPoint PPT Presentation

javafx pitfalls
SMART_READER_LITE
LIVE PREVIEW

JavaFX Pitfalls What no documentation tells you Speaker Andy - - PowerPoint PPT Presentation

JavaFX Pitfalls What no documentation tells you Speaker Andy Moncsek Senior Consultant Application Development @andyahcp Trivadis AG Switzerland - Zrich 600 employees in Switzerland, Germany and Austria consulting, development, BI,


slide-1
SLIDE 1

What no documentation tells you…

JavaFX Pitfalls

slide-2
SLIDE 2

Speaker

Andy Moncsek

Senior Consultant Application Development

Trivadis AG Switzerland - Zürich 600 employees in Switzerland, Germany and Austria consulting, development, BI, infrastructure, operation andy.moncsek@trivadis.com

@andyahcp

slide-3
SLIDE 3

Pitfall - lost Listeners

Overall Performance

slide-4
SLIDE 4
  • Accidental Garbage Collection

Bindings. add(one, two). addListener((x, y, z) -> { if (z != null && z.intValue() == 10) { Alert alert = new Alert(INFORMATION); alert.showAndWait(); } });

lost Listeners

Same as:

slide-5
SLIDE 5
  • Accidental Garbage Collection

lost Listeners

DoubleBinding b = Bindings.add(one, two); b.addListener((x, y, z) -> { if (z != null && z.intValue() == 10) { Alert alert = new Alert(INFORMATION); alert.showAndWait(); } });

slide-6
SLIDE 6

this.b = Bindings.add(one, two); this.b.addListener((x, y, z) -> { if (z != null && z.intValue() == 10) { Alert alert = new Alert(INFORMATION); alert.showAndWait(); } });

keep a reference of your binding

lost Listeners

better:

slide-7
SLIDE 7

Solution?

Use a custom implementation for async things, if you can’t life with the pitfalls.

slide-8
SLIDE 8

@RunWith(JfxRunner.class) public class TestServiceTest {

main thread

@Test public void testLongLastingOperation() { final TestService service = new TestService(); CompletableFuture<String> f = new CompletableFuture<>();

Platform.runLater(() -> {

service.valueProperty().addListener((b, o, n) -> { if (n != null) { f.complete(n); } }); service.restart(); }); assertEquals(„..“, future.get(5, TimeUnit.SECONDS));

} }

slide-9
SLIDE 9

@RunWith(JfxRunner.class) public class TestServiceTest {

main thread FX thread

@Test public void testLongLastingOperation() { final TestService service = new TestService(); CompletableFuture<String> f = new CompletableFuture<>();

Platform.runLater(() -> {

service.valueProperty().addListener((b, o, n) -> { if (n != null) { f.complete(n); } }); service.restart(); }); assertEquals(„..“, future.get(5, TimeUnit.SECONDS));

} }

slide-10
SLIDE 10

@RunWith(JfxRunner.class) public class TestServiceTest {

@Test public void testLongLastingOperation() { final TestService service = new TestService(); CompletableFuture<String> f = new CompletableFuture<>();

Platform.runLater(() -> {

service.valueProperty().addListener((b, o, n) -> { if (n != null) { f.complete(n); } }); service.restart(); }); assertEquals(„..“, f.get(5, TimeUnit.SECONDS));

} }

main thread FX thread

slide-11
SLIDE 11

Conclusion

there are big problems testing services you can ignore it use own implementations

slide-12
SLIDE 12

Don’t create a complex node graph in the UI Thread

Async Pitfalls

slide-13
SLIDE 13
  • not in FX - Thread? -> do not touch Nodes!

Threads, Tasks and Services

  • take care of callbacks from other frameworks

(WebSocket, ...)

  • create Nodes -> when not in SceneGraph OK
slide-14
SLIDE 14

Threads, Tasks and Services

public class MyApp extends Application { @Override public void init() throws Exception { // Do some heavy lifting } @Override public void start(Stage primaryStage) throws Exception { ... primaryStage.show(); } public static void main(String[] args) { LauncherImpl.launchApplication(MyApp.class,Preloader.class, args); } }

slide-15
SLIDE 15

Threads, Tasks and Services

public class MyApp extends Application { @Override public void init() throws Exception { // Do some heavy lifting } @Override public void start(Stage primaryStage) throws Exception { ... primaryStage.show(); } public static void main(String[] args) { LauncherImpl.launchApplication(MyApp.class,Preloader.class, args); } }

FX launcher thread FX thread

slide-16
SLIDE 16

Threads, Tasks and Services

public class MyApp extends Application { private InitialUIService service = new InitialUIService(); @Override public void init() throws Exception { // Do some heavy lifting service.start(); } @Override public void start(Stage primaryStage) throws Exception { ... primaryStage.show(); } public static void main(String[] args) { LauncherImpl.launchApplication(MyApp.class,Preloader.class, args); } }

FX launcher thread FX thread Worker thread

slide-17
SLIDE 17

Pixel Performance

slide-18
SLIDE 18
  • know the image size before loading

SimpleImageInfo info = new SimpleImageInfo(file); double width = info.getWidth(); double height = info.getHeight();

Uses meta-data in header [1] Do not load the whole image !

Image image = new Image("/50mp.jpg", true); double width = image.getWidth(); double height = image.getHeight();

Pushing - Pixels image size

slide-19
SLIDE 19

Pushing - Pixels read/write

  • PixelWriter
  • Get pixel -> manipulate -> write pixel
  • retrieving the pixel data from an Image
  • writing the pixel data of a WritableImage
  • PixelReader
slide-20
SLIDE 20

Pushing Pixels read/write

PixelReader PixelWriter getArgb(x,y) setArgb(x,y,value) getColor(x,y) setColor(x,y,value) getPixels(…) setPixels(…)

VS.

slide-21
SLIDE 21

Example: Crop an image Demo

slide-22
SLIDE 22

Pushing Pixels PixelFormat

  • Choose the right PixelFormat -> setPixels(…)
  • INT_ARGB_PRE, INT_ARGB, BYTE_BGRA

new WriteableImage() -> QuantumToolkit

public PlatformImage createPlatformImage(int w, int h) { ByteBuffer bytebuf = ...; return com.sun.prism.Image.fromByteBgraPreData(bytebuf, w, h); }

slide-23
SLIDE 23

Demo

Pushing Pixels PixelFormat

  • Play video in JavaFX using VLC & setPixels(…)
  • video fortmat!
  • no MediaPlayer on ARM
slide-24
SLIDE 24

Pushing - Pixels

  • Rule 1: use viewer nodes (~20.000 Nodes )!
  • Custom Controls —> x Nodes? complex CSS?
  • picking, sync, compute dirty regions, apply CSS,….
  • Use Canvas / Image when you have a lot to show!
slide-25
SLIDE 25

Canvas: + higher fps

+ lower heap + (~128MB - 250 pic.) + Node count 2

Node: - lower fps

  • higher heap
  • (~200MB - 250 pic.)
  • Node count >250

Pushing - Pixels

slide-26
SLIDE 26

Conclusion

be aware of complex/many nodes Node or Images? —> depends choose correct PixelFormat & methods

slide-27
SLIDE 27

Questions?

slide-28
SLIDE 28

Links

  • 1. https://jaimonmathew.wordpress.com/2011/01/29/

simpleimageinfo/

  • 2. http://tomasmikula.github.io/blog/2015/02/10/

the-trouble-with-weak-listeners.html

  • 3. https://blog.codecentric.de/en/2015/04/

tweaking-the-menu-bar-of-javafx-8-applications-

  • n-os-x/
slide-29
SLIDE 29

BACKUP

slide-30
SLIDE 30
  • JavaFX bindings uses WeakListeners to observe

dependencies

  • dispose root binding should be enough

leaking Bindings

slide-31
SLIDE 31
  • Memory Leaks in Bindings

SimpleDoubleProperty prop = new SimpleDoubleProperty(0.0);

for (int i = 0; i < 1000000; ++i) { prop.add(5).multiply(10).dispose(); } prop.unbind(); // will it work?

leaking Bindings

NO! count WeakListener = 1000000 ! count WeakReference = 1000000 !

slide-32
SLIDE 32
  • Memory Leaks in Bindings

SimpleDoubleProperty prop = new SimpleDoubleProperty(0.0);

leaking Bindings

prop.add(10); // invalidates all ref. !! for (int i = 0; i < 1000000; ++i) { prop.add(5).multiply(10).dispose(); }

slide-33
SLIDE 33

chain your service execution

FX-Thread Executor-Thread

slide-34
SLIDE 34
  • Solution 1 - Task & Platform.runLater

new Task<String>() { protected String call() throws Exception { Result r = service.get(); Platform.runLater(()-> .....); Result r2 = service2.get(); Platform.runLater(()-> .....); return "finished - step 2"; } };

chain your Service execution

FX-Thread Executor-Thread

  • Platform.runLater - keeps order
  • bad error handling & more !!
slide-35
SLIDE 35
  • Solution 2 - event handler

A.addEventHandler(SUCCEEDED, (s) -> { updateUI(); // start next service B.start(); }); A.addEventHandler(FAILED, (e) -> { doThat(); updateUI(); });

B.setOnSucceeded((state) -> { updateUI(); }); B.setOnFailed((event) -> { doThat(); updateUI(); });

chain your Service execution

FX-Thread Executor-Thread

  • better error handling
  • lots of code!
slide-36
SLIDE 36

chain your Service execution

  • Solution 3 - fluent API

handler .supplyAsync(()->longRunningTask1()) .onError(this::updateErrorMessage) .consumeOnFXThread(stringVal -> vbox.getChildren(). add(new Button(stringVal)) ) .supplyAsync(()->longRunningTask2()) .onError(this::updateErrorMessage)

.execute(this::updateUI); //non-blocking!

FX-Thread Executor-Thread

  • good error handling
  • easy to read
slide-37
SLIDE 37

Test service with fluent API

slide-38
SLIDE 38

final TestService service = new TestService();

IntStream.range(0, 5).forEach(v -> { handler.supplyOnFXThread(() -> { registerListenerAndStart(service, waitLatch); return service; }).functionOnExecutorThread((service) -> { // wait outside FX application thread !!! waitForService(waitLatch); return service; }).execute(serv -> { assertEquals("I'm an expensive result.", serv.getValue()); stop.countDown(); }); awaitServiceDone(stop); });

main thread FX thread

slide-39
SLIDE 39

final TestService service = new TestService();

IntStream.range(0, 5).forEach(v -> { handler.supplyOnFXThread(() -> { registerListenerAndStart(service, waitLatch); return service; }).functionOnExecutorThread((service) -> { // wait outside FX application thread !!! awaitService(waitLatch); return service; }).execute(serv -> { assertEquals("I'm an expensive result.", serv.getValue()); stop.countDown(); }); // Non-blocking !!! awaitServiceResult(stop); });

main thread FX thread worker thread

slide-40
SLIDE 40

OS X Menu Bar

slide-41
SLIDE 41

OS X Menu Bar

  • JavaFX is platform independent
  • no direct access to OS X application menu
slide-42
SLIDE 42

OS X Menu Bar

  • Solution:

Eclipse SWT -> Cocoa native binding JavaFX wrapper: NSMenuFX [3] better