javafx pitfalls: javaone 2015

42
What no documentation tells you… JavaFX Pitfalls

Upload: vokhuong

Post on 05-Jan-2017

221 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: JavaFX Pitfalls: JavaOne 2015

What no documentation tells you…

JavaFX Pitfalls

Page 2: JavaFX Pitfalls: JavaOne 2015

Speaker

Andy Moncsek Senior Consultant Application Development

Trivadis AG Switzerland - Zürich 600 employees in Switzerland, Germany and Austria consulting, development, BI, infrastructure, operation [email protected]

@andyahcp

Page 3: JavaFX Pitfalls: JavaOne 2015

Pitfall - lost Listeners

Overall Performance

Page 4: JavaFX Pitfalls: JavaOne 2015

• 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:

Page 5: JavaFX Pitfalls: JavaOne 2015

• 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(); } });

Page 6: JavaFX Pitfalls: JavaOne 2015

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:

Page 7: JavaFX Pitfalls: JavaOne 2015

Solution? Use a custom implementation for async things,

if you can’t life with the pitfalls.

Page 8: JavaFX Pitfalls: JavaOne 2015

@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));

} }

Page 9: JavaFX Pitfalls: JavaOne 2015

@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));

} }

Page 10: JavaFX Pitfalls: JavaOne 2015

@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

Page 11: JavaFX Pitfalls: JavaOne 2015

Conclusion

there are big problems testing services

you can ignore it

use own implementations

Page 12: JavaFX Pitfalls: JavaOne 2015

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

Async Pitfalls

Page 13: JavaFX Pitfalls: JavaOne 2015

• 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

Page 14: JavaFX Pitfalls: JavaOne 2015

Threads, Tasks and Servicespublic 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); }

}

Page 15: JavaFX Pitfalls: JavaOne 2015

Threads, Tasks and Servicespublic 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

Page 16: JavaFX Pitfalls: JavaOne 2015

Threads, Tasks and Servicespublic 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

Page 17: JavaFX Pitfalls: JavaOne 2015

Pixel Performance

Page 18: JavaFX Pitfalls: JavaOne 2015

• 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

Page 19: JavaFX Pitfalls: JavaOne 2015

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

Page 20: JavaFX Pitfalls: JavaOne 2015

Pushing Pixels read/write

PixelReader PixelWriter

getArgb(x,y) setArgb(x,y,value)

getColor(x,y) setColor(x,y,value)

getPixels(…) setPixels(…)

VS.

Page 21: JavaFX Pitfalls: JavaOne 2015

Example: Crop an image

Demo

Page 22: JavaFX Pitfalls: JavaOne 2015

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);

}

Page 23: JavaFX Pitfalls: JavaOne 2015

Demo

Pushing Pixels PixelFormat

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

Page 24: JavaFX Pitfalls: JavaOne 2015

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!

Page 25: JavaFX Pitfalls: JavaOne 2015

Canvas: + higher fps + lower heap + (~128MB - 250 pic.) + Node count 2

Node: - lower fps - higher heap -(~200MB - 250 pic.) - Node count >250

Pushing - Pixels

Page 26: JavaFX Pitfalls: JavaOne 2015

Conclusion

be aware of complex/many nodes

Node or Images? —> depends

choose correct PixelFormat & methods

Page 27: JavaFX Pitfalls: JavaOne 2015

Questions?

Page 29: JavaFX Pitfalls: JavaOne 2015

BACKUP

Page 30: JavaFX Pitfalls: JavaOne 2015

• JavaFX bindings uses WeakListeners to observe dependencies

• dispose root binding should be enough

leaking Bindings

Page 31: JavaFX Pitfalls: JavaOne 2015

• 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 !

Page 32: JavaFX Pitfalls: JavaOne 2015

• 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(); }

Page 33: JavaFX Pitfalls: JavaOne 2015

chain your service execution

FX-ThreadExecutor-Thread

Page 34: JavaFX Pitfalls: JavaOne 2015

• 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-ThreadExecutor-Thread

• Platform.runLater - keeps order

• bad error handling & more !!

Page 35: JavaFX Pitfalls: JavaOne 2015

• 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-ThreadExecutor-Thread

• better error handling

• lots of code!

Page 36: JavaFX Pitfalls: JavaOne 2015

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-ThreadExecutor-Thread

• good error handling

• easy to read

Page 37: JavaFX Pitfalls: JavaOne 2015

TestservicewithfluentAPI

Page 38: JavaFX Pitfalls: JavaOne 2015

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

Page 39: JavaFX Pitfalls: JavaOne 2015

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

Page 40: JavaFX Pitfalls: JavaOne 2015

OSXMenuBar

Page 41: JavaFX Pitfalls: JavaOne 2015

OSXMenuBar

• JavaFX is platform independent

• no direct access to OS X application menu

Page 42: JavaFX Pitfalls: JavaOne 2015

OSXMenuBar

• Solution: Eclipse SWT -> Cocoa native binding

JavaFX wrapper: NSMenuFX [3]

better