SlideShare uma empresa Scribd logo
1 de 107
Baixar para ler offline
Know them, or you will get angry.
JavaFX Pitfalls
CON4789
Warning
This presentation contains
a heavy load of code
Speaker
@sialcasa
Alexander Casall
Software Architect & Scrum Product Owner
Custom Software Development Company in Germany
Dresden, Munich, Berlin, Hamburg, Leipzig…
225 employees
3 Scrum Teams in JavaFX Projects
±7 consultants supporting JavaFX projects on customer side
alexander.casall@saxsys.de
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
FXML / Caching
Memory
Threading and Services
Pixel Operations
Examples
https://github.com/sialcasa/javafx-pitfalls
Before we start:
If you are looking for common performance tipps regarding JavaFX
https://wiki.openjdk.java.net/display/OpenJFX/Performance+Tips+and+Tricks
FXML / Caching
FXML-Loading Performance
FXML / Caching
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.chart.*?>
<?import javafx.scene.shape.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.40">
<children>
<BorderPane prefHeight="200.0" prefWidth="200.0">
<center>
<SplitPane dividerPositions="0.29797979797979796" prefHeight="160.0" prefWidth="200.0" Bord
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0" />
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
<children>
<TabPane layoutX="-32.0" layoutY="-31.0" prefHeight="200.0" prefWidth="200.0" t
<tabs>
<Tab text="Untitled Tab 1">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth
<children>
<TextFlow layoutX="-86.0" layoutY="-30.0" prefHeight="200.0"
<children>
<TitledPane animated="false" text="untitled">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeig
<children>
DEMO
Conclusion
wildcard imports are bad - check your FXML files
IntelliJ IDEA has import optimisation
We’ll fix this issue on Scene Builder
Why you don’t use caching?
FXML / Caching
What caching does
extends	
  Node
myMagicalCircle.setCache(true);
Image
Why should you use it?
magical circles are expensive to render
GPU
The movement of the element causes rendering effort
GPU
GPU
Other nodes can also cause rendering
Caching avoids unnecessary rendering of nodes
Why should you use it?
GPU
myMagicalCircle.setCache(true)
no more rendering caused e.g. by movement or overlapping nodes
Demo
Conclusion
simple objects which change often
complex objects which don’t change
(Don’t) cache…
Memory
Listener can cause memory leaks
Memory
public class TemporaryView extends StackPane {
public TemporaryView(Car dataModel) {
dataModel.name.addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable,
String oldValue, String newValue) {
//Do Something
}
});
}
}
public class Car {
StringProperty name = new SimpleStringProperty("Audi");
}
Instances of anonymous inner
classes hold on to a reference
to their enclosing instances
even if these references are
never actually used.
references
@Override
public void start(Stage primaryStage) throws Exception {
this.longlivingData = new Car();
this.root = new VBox(new TemporaryView(longlivingData));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
} VBox in Stage
Temporary
View
long living
Data Model
root.getChildren().clear();
Application Lifetime
VBox in Stage
Temporary
View
long living
Data Model
Temporary
View
Application Lifetime
VBox in Stage
Temporary
View
long living
Data Model
Temporary
View
retains in memory
omg, it’s a leak
How to avoid it?
Use WeakListener (dangerous)
Do manual Housekeeping
Demo
Conclusion
Listeners can leak
WeakListeners are dangerous
manage your Listeners manually
avoid anonymous implementations
Be aware of lost listeners!
Memory
DoubleProperty one=…;
DoubleProperty two=…;
this.one.add(this.two).
addListener((x, y, z) -> {
if (z != null && z.intValue() == 10) {
Alert alert = new Alert(…);
alert.showAndWait();
}
});
The listener get’s garbage collected
Same as:
DoubleProperty one=…;
DoubleProperty two=…;
NumberBinding add = Bindings.add(one, two);
add.addListener((x, y, z) -> {
if (z != null && z.intValue() == 10) {
Alert alert = new Alert(…);
alert.showAndWait();
}
});
The listener get’s garbage collected
DEMO
Conclusion - reference the binding
DoubleProperty one=…;
DoubleProperty two=…;
this.add = Bindings.add(one, two);
this.add.addListener((x, y, z) -> {
if (z != null && z.intValue() == 10) {
Alert alert = new Alert(…);
alert.showAndWait();
}
});
Threading and Services
It’s hard to test JavaFX Services!
Threading and Services
public class TestService extends Service<String> {
@Override
protected Task<String> createTask() {
return new Task<String>() {
@Override
protected String call() throws Exception {
Thread.sleep(5000);
return "I'm an expensive result.";
}
};
}
}
How to test this?
Not FX specific:
How to test async behaviour?
@Test
public void testLongLastingOperation() throws … {
TestService service = new TestService();
CompletableFuture<String> future = new CompletableFuture<>();
service.valueProperty().addListener((b, o, n) -> {
if (n != null) {
future.complete(n);
}
});
service.restart();
assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS));
}
Green or Red?
java.lang.IllegalStateException:	
  Toolkit	
  not	
  initialized	
  
	
   at	
  com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:
	
   at	
  com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:
	
   at	
  javafx.application.Platform.runLater(Platform.java:83)	
  
	
   at	
  javafx.concurrent.Service.runLater(Service.java:913)	
  
	
   at	
  javafx.concurrent.Service.start(Service.java:676)	
  
…	
  
@Test
public void testLongLastingOperation() throws … {
TestService service = new TestService();
CompletableFuture<String> future = new CompletableFuture<>();
service.valueProperty().addListener((b, o, n) -> {
if (n != null) {
future.complete(n);
}
});
service.restart();
assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS));
}
Green or Red?
Needs started FX-Toolkit
Ok… let’s fix this
@RunWith(JfxRunner.class)
public class ServiceNotTestableTest {
@Test
public void testLongLastingOperation() throws … {
TestService service = new TestService();
CompletableFuture<String> future = new CompletableFuture<>();
service.valueProperty().addListener((b, o, n) -> {
if (n != null) {
future.complete(n);
}
});
service.restart();
assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS));
}
<dependency>
<groupId>de.saxsys</groupId>
<artifactId>jfx-testrunner</artifactId>
<version>1.2-SNAPSHOT</version>
</dependency>
But…
The Service has the intention
to be reused multiple times.
Let’s test this!
Let’s do a small refactoring
@RunWith(JfxRunner.class)
public class TestableServiceTest {
@Test
public void testLongLastingOperation() throws … {
TestService service = new TestService();
CompletableFuture<String> future = new CompletableFuture<>();
service.valueProperty().addListener((b, o, n) -> {
if (n != null) {
future.complete(n);
}
});
service.restart();
assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS));
}
@RunWith(JfxRunner.class)
public class TestableServiceTest {
@Test
public void testLongLastingOperation() throws … {
TestService service = new TestService();
//refactored the Completable future stuff with the listeners in a method
CompletableFuture<String> future = createFutureWithListener(service);
service.restart();
assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS));
}
}
@RunWith(JfxRunner.class)
public class TestableServiceTest {
@Test
public void testLongLastingOperation() throws … {
TestService service = new TestService();
//refactored the Completable future stuff with the listeners in a method
CompletableFuture<String> future = createFutureWithListener(service);
service.restart();
assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS));
CompletableFuture<String> future = createFutureWithListener(service);
service.restart();
assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS));
}
}
Green or Red?
java.lang.IllegalStateException:	
  Service	
  must	
  
only	
  be	
  used	
  from	
  the	
  FX	
  Application	
  Thread	
  
	
   at	
  javafx.concurrent.Service.checkThread(Service.java:906)	
  
	
   at	
  javafx.concurrent.Service.valueProperty(Service.java:205)	
  
	
   at	
  
de.saxsys.pitfalls.services.ClassToTestTest.testLongLastingOperation(ClassToTestTest.java:
40)	
  
	
   at	
  sun.reflect.NativeMethodAccessorImpl.invoke0(Native	
  Method)	
  
	
   at	
  sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)	
  
	
   at	
  sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:	
  
@RunWith(JfxRunner.class)
public class TestableServiceTest {
@Test
public void testLongLastingOperation() throws … {
TestService service = new TestService();
//refactored the Completable future stuff with the listeners in a method
CompletableFuture<String> future = createFutureWithListener(service);
service.restart();
assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS));
CompletableFuture<String> future = createFutureWithListener(service);
service.restart();
assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS));
}
}
Exception goes here
Let’s have a look on the Service Implementation
@Override public final V getValue() { checkThread(); return value.get(); }
@Override public final ReadOnlyObjectProperty<V> valueProperty() { checkThread(); return value;}
/**
* Cancels any currently running Task, if any, and restarts this Service. The state
* will be reset to READY prior to execution. This method should only be called on
* the FX application thread.
*/
public void restart() {
checkThread();
…
}
void checkThread() {
if (startedOnce && !isFxApplicationThread()) {
throw new IllegalStateException("Service must only be
used from the FX Application Thread");
}
}
first restart()
second restart()
But I wan’t to call my service multiple times
in my test - because this is it’s use-case.
Let’s test this in the FX-
Thread
@RunWith(JfxRunner.class)
public class TestableServiceTest {
@Test
public void testLongLastingOperation() throws … {
TestService service = new TestService();
//refactored the Completable future stuff with the listeners in a method
CompletableFuture<String> future = createFutureWithListener(service);
service.restart();
assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS));
CompletableFuture<String> future = createFutureWithListener(service);
service.restart();
assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS));
}
}
@RunWith(JfxRunner.class)
public class TestableServiceTest {
@Test
@TestInJfxThread
public void testLongLastingOperation() throws … {
TestService service = new TestService();
//refactored the Completable future stuff with the listeners in a method
CompletableFuture<String> future = createFutureWithListener(service);
service.restart();
assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS));
CompletableFuture<String> future = createFutureWithListener(service);
service.restart();
assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS));
}
}
jfx-testrunner annotation
@RunWith(JfxRunner.class)
public class TestableServiceTest {
@Test
@TestInJfxThread
public void testLongLastingOperation() throws … {
TestService service = new TestService();
//refactored the Completable future stuff with the listeners in a method
CompletableFuture<String> future = createFutureWithListener(service);
service.restart();
assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS));
CompletableFuture<String> future = createFutureWithListener(service);
service.restart();
assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS));
}
}
Green or Red?
java.util.concurrent.TimeoutException
at java.util.concurrent.CompletableFuture.timedGet(CompletableFuture.java:1763)
at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1907)
at de.saxsys.pitfalls.services.nottestable.ServiceNotTestableTest.testLongLastingOperationInFXThread(
TestServiceTest.java:85)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at de.saxsys.javafx.test.JfxRunner.access$0(JfxRunner.java:1)
at de.saxsys.javafx.test.JfxRunner.lambda$0(JfxRunner.java:47)
at de.saxsys.javafx.test.JfxRunner$$Lambda$56/1865127310.run(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$null$170(PlatformImpl.java:295)
at com.sun.javafx.application.PlatformImpl$$Lambda$52/1724593206.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$171(PlatformImpl.java:294)
at com.sun.javafx.application.PlatformImpl$$Lambda$51/580016451.run(Unknown Source)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
@RunWith(JfxRunner.class)
public class TestableServiceTest {
@Test
@TestInJfxThread
public void testLongLastingOperation() throws … {
TestService service = new TestService();
//refactored the Completable future stuff with the listeners in a method
CompletableFuture<String> future = createFutureWithListener(service);
service.restart();
assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS));
CompletableFuture<String> future = createFutureWithListener(service);
service.restart();
assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS));
}
}
Line 85: .get()
blocks the UI Thread
Ok, correct solution:
start with Test Thread and move all Service accesses to FX Thread
FX Thread
@Test
public void testLongLastingOperationSimple() throws … {
TestService service = new TestService();
CompletableFuture<String> future = new CompletableFuture<>();
Platform.runLater(() -> {
service.valueProperty().addListener((b, o, n) -> {
if (n != null) {
future.complete(n);
}
});
service.restart();
});
assertEquals("I'm an expensive result. 1", future.get(5, TimeUnit.SECONDS));
}
Ok, let’s run and test the Service
multiple times
@Test
public void testLongLastingOperationSimple() throws … {
TestService service = new TestService();
CompletableFuture<String> future = new CompletableFuture<>();
Platform.runLater(() -> {
service.valueProperty().addListener((b, o, n) -> {
if (n != null) {
future.complete(n);
}
});
service.restart();
});
assertEquals("I'm an expensive result. 1", future.get(5, TimeUnit.SECONDS));
CompletableFuture<String> future = new CompletableFuture<>();
Platform.runLater(() -> {
service.valueProperty().addListener((b, o, n) -> {
if (n != null) {
future.complete(n);
}
});
service.restart();
});
assertEquals("I'm an expensive result. 1", future.get(5, TimeUnit.SECONDS));
}
For each execution of the Service you have to double the
amount of the (boilerplate)-code.
This is too much boilerplate code for testing Services
@RunWith(JfxRunner.class)
public class ServiceTestableTest {
@Test
public void startServiceMultipleTimesTest() throws …{
TestService service = new TestService();
ServiceWrapper wrapper = new ServiceWrapper(service);
wrapper.startAndWait(6000);
assertEquals("I'm an expensive result. 1", wrapper.getValue());
wrapper.restartAndWait(6000);
assertEquals("I'm an expensive result. 2", wrapper.getValue());
}
}
no more
checkThread()
problems for asserts
reduces
platform issues
get help with jfx-­‐testrunner
https://github.com/sialcasa/jfx-testrunner
blocks test thread
Conclusion
there are problems testing services
you need a lot of boilerplate code to test them
use libraries
Don’t create a complex node graph in the UI Thread!
Threading and Services
but you should create Nodes in background threads!
Attach Nodes to the scene graph on the UI Thread only,
Load Data
FX-Thread
ArrayList<String> data = getBigData();
Pane displayPane = new Pane();
for (int i = 0; i < data.size(); i++) {
Label label = new Label(data.get(i));
displayPane.getChildren().add(label);
}
root.getChildren().add(displayPane);
UI-Freezes until the
Graph has been build
Display Loaded Data
Load Data
FX-Thread
ArrayList<String> data = new ArrayList<>();
Pane displayPane = new Pane();
for (int i = 0; i < data.size(); i++) {
Label label = new Label(data.get(i));
displayPane.getChildren().add(label);
}
Platform.runLater(() -> root.getChildren().add(displayPane));
Build Graph
Pixel Performance
Preprocess Metadata of Images
Pixel Performance
SimpleImageInfo	
  info	
  =	
  new	
  SimpleImageInfo(file);	
  
double	
  width	
  	
  =	
  info.getWidth();	
  
double	
  height	
  =	
  info.getHeight();
Uses meta-data in header [1]
Loading the whole image to get the Metadata is bad.
Image	
  image	
  =	
  new	
  Image("/50mp.jpg",	
  true);	
  
double	
  width	
  =	
  image.getWidth();	
  
double	
  height	
  =	
  image.getHeight();
How to preprocess the Metadata of Images?
Read- / Write Pixels
Pixel Performance
Read / Write
javafx.scene.image.PixelWriter	
  
retrieving the pixel data from an Image
writing the pixel data of a WritableImage
javafx.scene.image.PixelReader
Read Pixels Process Pixels Write Pixels
Many methods, same result…
PixelReader getArgb(…) getColor(…) getPixels(…)
setArgb(…) setColor(…) setPixels(…)PixelWriter
e.g. cropping
DEMO
Choose the right pixel format!
Pixel Performance
What is the right pixel format for Quantum Toolkit?
BYTE_BGRA_PRE
myPixelReader.setPixels(…,pixelformat	
  ,…);
myPixelReader.getPixels(…,pixelformat	
  ,…);
	
  	
  	
  	
  @Override	
  
	
  	
  	
  	
  public	
  PlatformImage	
  createPlatformImage(int	
  w,	
  int	
  h)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  ByteBuffer	
  bytebuf	
  =	
  ByteBuffer.allocate(w	
  *	
  h	
  *	
  4);	
  
	
  	
  	
  	
  	
  	
  	
  	
  return	
  com.sun.prism.Image.fromByteBgraPreData(bytebuf,	
  w,	
  h);	
  
	
  	
  	
  	
  }
calls
Demo - Videoplayer using VLC & setPixels()
1. FX-Mediaplayer does not supports all codecs
2. there is no javafx.scene.media.* on ARM
VLC solves 2 additional pitfalls
Many complex nodes? Use Canvas!
Pixel Performance
Rule: use fewer nodes (>20.000 Nodes )!
picking, sync, compute dirty regions, apply CSS,….
Use Canvas / Image when you have a lot to show!
Canvas: + higher fps
+ lower heap
+ (~128MB - 250 pic.)
+ Node count 2
Node: - lower fps
- higher heap
-(~200MB - 250 pic.)
- Node count >250
Demo
Pixel Performance Conclusion
be aware of complex/many nodes
Node or Images? —> depends
choose correct PixelFormat & methods
Questions?
Hint: JavaFX Real-World Apps [CON3907]
Alexander Casall, Dirk Lemmermann
Wednesday, Oct 28, 1:00 p.m.
Links
1. http://max-wielsch.blogspot.de/2015/10/testing-asynchronous-
javafx-logic.html
2. https://jaimonmathew.wordpress.com/2011/01/29/
simpleimageinfo/
3. http://tomasmikula.github.io/blog/2015/02/10/the-trouble-
with-weak-listeners.html
4. https://blog.codecentric.de/en/2015/04/tweaking-the-menu-
bar-of-javafx-8-applications-on-os-x/
BACKUP
• JavaFX bindings uses WeakListeners to observe
dependencies
• dispose root binding should be enough
leaking Bindings
• 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 !
• 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();
}
chain your service execution
FX-Thread
Executor-Thread
• 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 !!
• 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!
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
Test	
  service	
  with	
  fluent	
  API
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
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
OS	
  X	
  Menu	
  Bar
OS	
  X	
  Menu	
  Bar
• JavaFX is platform independent
• no direct access to OS X application menu
OS	
  X	
  Menu	
  Bar
• Solution: Eclipse SWT -> Cocoa native binding
JavaFX wrapper: NSMenuFX [3]
better

Mais conteúdo relacionado

Mais procurados

月間10億pvを支えるmongo db
月間10億pvを支えるmongo db月間10億pvを支えるmongo db
月間10億pvを支えるmongo db
Yuji Isobe
 
Easy data-with-spring-data-jpa
Easy data-with-spring-data-jpaEasy data-with-spring-data-jpa
Easy data-with-spring-data-jpa
Staples
 
History & Practices for UniRx UniRxの歴史、或いは開発(中)タイトルの用例と落とし穴の回避法
History & Practices for UniRx UniRxの歴史、或いは開発(中)タイトルの用例と落とし穴の回避法History & Practices for UniRx UniRxの歴史、或いは開発(中)タイトルの用例と落とし穴の回避法
History & Practices for UniRx UniRxの歴史、或いは開発(中)タイトルの用例と落とし穴の回避法
Yoshifumi Kawai
 

Mais procurados (20)

Introduction to react_js
Introduction to react_jsIntroduction to react_js
Introduction to react_js
 
Advanced JavaScript
Advanced JavaScriptAdvanced JavaScript
Advanced JavaScript
 
odoo json rpc.docx
odoo json rpc.docxodoo json rpc.docx
odoo json rpc.docx
 
ReactJS presentation
ReactJS presentationReactJS presentation
ReactJS presentation
 
Understanding react hooks
Understanding react hooksUnderstanding react hooks
Understanding react hooks
 
Introduction to VueJS & Vuex
Introduction to VueJS & VuexIntroduction to VueJS & Vuex
Introduction to VueJS & Vuex
 
月間10億pvを支えるmongo db
月間10億pvを支えるmongo db月間10億pvを支えるmongo db
月間10億pvを支えるmongo db
 
Node mailer example how to send email using nodemailer with gmail &amp; mailtrap
Node mailer example how to send email using nodemailer with gmail &amp; mailtrapNode mailer example how to send email using nodemailer with gmail &amp; mailtrap
Node mailer example how to send email using nodemailer with gmail &amp; mailtrap
 
Vue js for beginner
Vue js for beginner Vue js for beginner
Vue js for beginner
 
Spring Bootでチャットツールを作りながらWebの仕組みを理解しよう!
Spring Bootでチャットツールを作りながらWebの仕組みを理解しよう!Spring Bootでチャットツールを作りながらWebの仕組みを理解しよう!
Spring Bootでチャットツールを作りながらWebの仕組みを理解しよう!
 
File system node js
File system node jsFile system node js
File system node js
 
Java開発の強力な相棒として今すぐ使えるGroovy
Java開発の強力な相棒として今すぐ使えるGroovyJava開発の強力な相棒として今すぐ使えるGroovy
Java開発の強力な相棒として今すぐ使えるGroovy
 
Introduction to TypeScript
Introduction to TypeScriptIntroduction to TypeScript
Introduction to TypeScript
 
스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해
 
20180518 QNAP Seminar - Introduction to React Native
20180518 QNAP Seminar - Introduction to React Native20180518 QNAP Seminar - Introduction to React Native
20180518 QNAP Seminar - Introduction to React Native
 
Java ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsugJava ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsug
 
Easy data-with-spring-data-jpa
Easy data-with-spring-data-jpaEasy data-with-spring-data-jpa
Easy data-with-spring-data-jpa
 
Hibernate Basic Concepts - Presentation
Hibernate Basic Concepts - PresentationHibernate Basic Concepts - Presentation
Hibernate Basic Concepts - Presentation
 
Express JS
Express JSExpress JS
Express JS
 
History & Practices for UniRx UniRxの歴史、或いは開発(中)タイトルの用例と落とし穴の回避法
History & Practices for UniRx UniRxの歴史、或いは開発(中)タイトルの用例と落とし穴の回避法History & Practices for UniRx UniRxの歴史、或いは開発(中)タイトルの用例と落とし穴の回避法
History & Practices for UniRx UniRxの歴史、或いは開発(中)タイトルの用例と落とし穴の回避法
 

Destaque

Destaque (20)

The JavaFX Ecosystem
The JavaFX EcosystemThe JavaFX Ecosystem
The JavaFX Ecosystem
 
JavaFX in Action Part I
JavaFX in Action Part IJavaFX in Action Part I
JavaFX in Action Part I
 
Welches Webframework passt zu mir? (WJAX)
Welches Webframework passt zu mir? (WJAX)Welches Webframework passt zu mir? (WJAX)
Welches Webframework passt zu mir? (WJAX)
 
DataFX 8 (JavaOne 2014)
DataFX 8 (JavaOne 2014)DataFX 8 (JavaOne 2014)
DataFX 8 (JavaOne 2014)
 
JavaFX Layout Secrets with Amy Fowler
JavaFX Layout Secrets with Amy FowlerJavaFX Layout Secrets with Amy Fowler
JavaFX Layout Secrets with Amy Fowler
 
Java fx ap is
Java fx ap isJava fx ap is
Java fx ap is
 
From Swing to JavaFX
From Swing to JavaFXFrom Swing to JavaFX
From Swing to JavaFX
 
Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)
Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)
Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)
 
JavaFX Your Way: Building JavaFX Applications with Alternative Languages
JavaFX Your Way: Building JavaFX Applications with Alternative LanguagesJavaFX Your Way: Building JavaFX Applications with Alternative Languages
JavaFX Your Way: Building JavaFX Applications with Alternative Languages
 
A Tour of PostgREST
A Tour of PostgRESTA Tour of PostgREST
A Tour of PostgREST
 
JavaFX in Action (devoxx'16)
JavaFX in Action (devoxx'16)JavaFX in Action (devoxx'16)
JavaFX in Action (devoxx'16)
 
JavaFX Overview
JavaFX OverviewJavaFX Overview
JavaFX Overview
 
JavaFX Real-World Apps
JavaFX Real-World AppsJavaFX Real-World Apps
JavaFX Real-World Apps
 
JavaFX GUI architecture with Clojure core.async
JavaFX GUI architecture with Clojure core.asyncJavaFX GUI architecture with Clojure core.async
JavaFX GUI architecture with Clojure core.async
 
FlatGUI: Reactive GUI Toolkit Implemented in Clojure
FlatGUI: Reactive GUI Toolkit Implemented in ClojureFlatGUI: Reactive GUI Toolkit Implemented in Clojure
FlatGUI: Reactive GUI Toolkit Implemented in Clojure
 
8 True Stories about JavaFX
8 True Stories about JavaFX8 True Stories about JavaFX
8 True Stories about JavaFX
 
JavaFX 8 - GUI by Illusion
JavaFX 8 - GUI by IllusionJavaFX 8 - GUI by Illusion
JavaFX 8 - GUI by Illusion
 
JavaFX Presentation
JavaFX PresentationJavaFX Presentation
JavaFX Presentation
 
What's New in Java 8
What's New in Java 8What's New in Java 8
What's New in Java 8
 
Clojure #1
Clojure #1Clojure #1
Clojure #1
 

Semelhante a JavaFX Pitfalls

09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
Igor Bronovskyy
 
JavaScript (without DOM)
JavaScript (without DOM)JavaScript (without DOM)
JavaScript (without DOM)
Piyush Katariya
 
WSO2 SOA with C and C++
WSO2 SOA with C and C++WSO2 SOA with C and C++
WSO2 SOA with C and C++
WSO2
 
Droidcon2013 android experience lahoda
Droidcon2013 android experience lahodaDroidcon2013 android experience lahoda
Droidcon2013 android experience lahoda
Droidcon Berlin
 

Semelhante a JavaFX Pitfalls (20)

Programming Sideways: Asynchronous Techniques for Android
Programming Sideways: Asynchronous Techniques for AndroidProgramming Sideways: Asynchronous Techniques for Android
Programming Sideways: Asynchronous Techniques for Android
 
Maxim Salnikov - Service Worker: taking the best from the past experience for...
Maxim Salnikov - Service Worker: taking the best from the past experience for...Maxim Salnikov - Service Worker: taking the best from the past experience for...
Maxim Salnikov - Service Worker: taking the best from the past experience for...
 
Divide and Conquer – Microservices with Node.js
Divide and Conquer – Microservices with Node.jsDivide and Conquer – Microservices with Node.js
Divide and Conquer – Microservices with Node.js
 
Is writing performant code too expensive?
Is writing performant code too expensive? Is writing performant code too expensive?
Is writing performant code too expensive?
 
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
 
Das kannste schon so machen
Das kannste schon so machenDas kannste schon so machen
Das kannste schon so machen
 
JavaScript (without DOM)
JavaScript (without DOM)JavaScript (without DOM)
JavaScript (without DOM)
 
"Applied Enterprise Metaprogramming in JavaScript", Vladyslav Dukhin
"Applied Enterprise Metaprogramming in JavaScript", Vladyslav Dukhin"Applied Enterprise Metaprogramming in JavaScript", Vladyslav Dukhin
"Applied Enterprise Metaprogramming in JavaScript", Vladyslav Dukhin
 
Why Windows 8 drivers are buggy
Why Windows 8 drivers are buggyWhy Windows 8 drivers are buggy
Why Windows 8 drivers are buggy
 
ZCM update VAI Brainforce
ZCM update VAI BrainforceZCM update VAI Brainforce
ZCM update VAI Brainforce
 
Lagom in Practice
Lagom in PracticeLagom in Practice
Lagom in Practice
 
WSO2 SOA with C and C++
WSO2 SOA with C and C++WSO2 SOA with C and C++
WSO2 SOA with C and C++
 
Jdk Tools For Performance Diagnostics
Jdk Tools For Performance DiagnosticsJdk Tools For Performance Diagnostics
Jdk Tools For Performance Diagnostics
 
From Web App Model Design to Production with Wakanda
From Web App Model Design to Production with WakandaFrom Web App Model Design to Production with Wakanda
From Web App Model Design to Production with Wakanda
 
NestJS
NestJSNestJS
NestJS
 
Javascript Memory leaks and Performance & Angular
Javascript Memory leaks and Performance & AngularJavascript Memory leaks and Performance & Angular
Javascript Memory leaks and Performance & Angular
 
soft-shake.ch - Hands on Node.js
soft-shake.ch - Hands on Node.jssoft-shake.ch - Hands on Node.js
soft-shake.ch - Hands on Node.js
 
Virtualizing Java in Java (jug.ru)
Virtualizing Java in Java (jug.ru)Virtualizing Java in Java (jug.ru)
Virtualizing Java in Java (jug.ru)
 
Improving android experience for both users and developers
Improving android experience for both users and developersImproving android experience for both users and developers
Improving android experience for both users and developers
 
Droidcon2013 android experience lahoda
Droidcon2013 android experience lahodaDroidcon2013 android experience lahoda
Droidcon2013 android experience lahoda
 

Último

%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
masabamasaba
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
masabamasaba
 
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
masabamasaba
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
masabamasaba
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Medical / Health Care (+971588192166) Mifepristone and Misoprostol tablets 200mg
 

Último (20)

WSO2CON 2024 - How to Run a Security Program
WSO2CON 2024 - How to Run a Security ProgramWSO2CON 2024 - How to Run a Security Program
WSO2CON 2024 - How to Run a Security Program
 
What Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the SituationWhat Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the Situation
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
 
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
 
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
 
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open SourceWSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
 
Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK Software
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
 
WSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go Platformless
 
WSO2Con204 - Hard Rock Presentation - Keynote
WSO2Con204 - Hard Rock Presentation - KeynoteWSO2Con204 - Hard Rock Presentation - Keynote
WSO2Con204 - Hard Rock Presentation - Keynote
 
WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?
 
WSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaSWSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaS
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
 
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
 

JavaFX Pitfalls

  • 1. Know them, or you will get angry. JavaFX Pitfalls CON4789
  • 3. Speaker @sialcasa Alexander Casall Software Architect & Scrum Product Owner Custom Software Development Company in Germany Dresden, Munich, Berlin, Hamburg, Leipzig… 225 employees 3 Scrum Teams in JavaFX Projects ±7 consultants supporting JavaFX projects on customer side alexander.casall@saxsys.de
  • 4. 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
  • 5. FXML / Caching Memory Threading and Services Pixel Operations Examples https://github.com/sialcasa/javafx-pitfalls
  • 6. Before we start: If you are looking for common performance tipps regarding JavaFX https://wiki.openjdk.java.net/display/OpenJFX/Performance+Tips+and+Tricks
  • 9.
  • 10. <?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.chart.*?> <?import javafx.scene.shape.*?> <?import javafx.scene.text.*?> <?import javafx.scene.control.*?> <?import java.lang.*?> <?import javafx.scene.layout.*?> <?import javafx.scene.layout.AnchorPane?> <AnchorPane xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.40"> <children> <BorderPane prefHeight="200.0" prefWidth="200.0"> <center> <SplitPane dividerPositions="0.29797979797979796" prefHeight="160.0" prefWidth="200.0" Bord <items> <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0" /> <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0"> <children> <TabPane layoutX="-32.0" layoutY="-31.0" prefHeight="200.0" prefWidth="200.0" t <tabs> <Tab text="Untitled Tab 1"> <content> <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth <children> <TextFlow layoutX="-86.0" layoutY="-30.0" prefHeight="200.0" <children> <TitledPane animated="false" text="untitled"> <content> <AnchorPane minHeight="0.0" minWidth="0.0" prefHeig <children>
  • 11. DEMO
  • 12. Conclusion wildcard imports are bad - check your FXML files IntelliJ IDEA has import optimisation We’ll fix this issue on Scene Builder
  • 13. Why you don’t use caching? FXML / Caching
  • 14. What caching does extends  Node myMagicalCircle.setCache(true); Image
  • 15. Why should you use it?
  • 16. magical circles are expensive to render GPU
  • 17. The movement of the element causes rendering effort GPU
  • 18. GPU Other nodes can also cause rendering
  • 19. Caching avoids unnecessary rendering of nodes Why should you use it?
  • 20. GPU myMagicalCircle.setCache(true) no more rendering caused e.g. by movement or overlapping nodes
  • 21. Demo
  • 22. Conclusion simple objects which change often complex objects which don’t change (Don’t) cache…
  • 24. Listener can cause memory leaks Memory
  • 25. public class TemporaryView extends StackPane { public TemporaryView(Car dataModel) { dataModel.name.addListener(new ChangeListener<String>() { @Override public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) { //Do Something } }); } } public class Car { StringProperty name = new SimpleStringProperty("Audi"); } Instances of anonymous inner classes hold on to a reference to their enclosing instances even if these references are never actually used. references
  • 26. @Override public void start(Stage primaryStage) throws Exception { this.longlivingData = new Car(); this.root = new VBox(new TemporaryView(longlivingData)); Scene scene = new Scene(root); primaryStage.setScene(scene); primaryStage.show(); } VBox in Stage Temporary View long living Data Model
  • 27. root.getChildren().clear(); Application Lifetime VBox in Stage Temporary View long living Data Model Temporary View
  • 28. Application Lifetime VBox in Stage Temporary View long living Data Model Temporary View retains in memory omg, it’s a leak
  • 29. How to avoid it? Use WeakListener (dangerous) Do manual Housekeeping
  • 30. Demo
  • 31. Conclusion Listeners can leak WeakListeners are dangerous manage your Listeners manually avoid anonymous implementations
  • 32. Be aware of lost listeners! Memory
  • 33. DoubleProperty one=…; DoubleProperty two=…; this.one.add(this.two). addListener((x, y, z) -> { if (z != null && z.intValue() == 10) { Alert alert = new Alert(…); alert.showAndWait(); } }); The listener get’s garbage collected Same as:
  • 34. DoubleProperty one=…; DoubleProperty two=…; NumberBinding add = Bindings.add(one, two); add.addListener((x, y, z) -> { if (z != null && z.intValue() == 10) { Alert alert = new Alert(…); alert.showAndWait(); } }); The listener get’s garbage collected
  • 35. DEMO
  • 36. Conclusion - reference the binding DoubleProperty one=…; DoubleProperty two=…; this.add = Bindings.add(one, two); this.add.addListener((x, y, z) -> { if (z != null && z.intValue() == 10) { Alert alert = new Alert(…); alert.showAndWait(); } });
  • 38. It’s hard to test JavaFX Services! Threading and Services
  • 39. public class TestService extends Service<String> { @Override protected Task<String> createTask() { return new Task<String>() { @Override protected String call() throws Exception { Thread.sleep(5000); return "I'm an expensive result."; } }; } } How to test this?
  • 40. Not FX specific: How to test async behaviour?
  • 41. @Test public void testLongLastingOperation() throws … { TestService service = new TestService(); CompletableFuture<String> future = new CompletableFuture<>(); service.valueProperty().addListener((b, o, n) -> { if (n != null) { future.complete(n); } }); service.restart(); assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS)); } Green or Red?
  • 42.
  • 43. java.lang.IllegalStateException:  Toolkit  not  initialized     at  com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:   at  com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:   at  javafx.application.Platform.runLater(Platform.java:83)     at  javafx.concurrent.Service.runLater(Service.java:913)     at  javafx.concurrent.Service.start(Service.java:676)   …  
  • 44. @Test public void testLongLastingOperation() throws … { TestService service = new TestService(); CompletableFuture<String> future = new CompletableFuture<>(); service.valueProperty().addListener((b, o, n) -> { if (n != null) { future.complete(n); } }); service.restart(); assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS)); } Green or Red? Needs started FX-Toolkit
  • 46. @RunWith(JfxRunner.class) public class ServiceNotTestableTest { @Test public void testLongLastingOperation() throws … { TestService service = new TestService(); CompletableFuture<String> future = new CompletableFuture<>(); service.valueProperty().addListener((b, o, n) -> { if (n != null) { future.complete(n); } }); service.restart(); assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS)); } <dependency> <groupId>de.saxsys</groupId> <artifactId>jfx-testrunner</artifactId> <version>1.2-SNAPSHOT</version> </dependency>
  • 47.
  • 48. But… The Service has the intention to be reused multiple times. Let’s test this!
  • 49. Let’s do a small refactoring @RunWith(JfxRunner.class) public class TestableServiceTest { @Test public void testLongLastingOperation() throws … { TestService service = new TestService(); CompletableFuture<String> future = new CompletableFuture<>(); service.valueProperty().addListener((b, o, n) -> { if (n != null) { future.complete(n); } }); service.restart(); assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS)); }
  • 50. @RunWith(JfxRunner.class) public class TestableServiceTest { @Test public void testLongLastingOperation() throws … { TestService service = new TestService(); //refactored the Completable future stuff with the listeners in a method CompletableFuture<String> future = createFutureWithListener(service); service.restart(); assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS)); } }
  • 51. @RunWith(JfxRunner.class) public class TestableServiceTest { @Test public void testLongLastingOperation() throws … { TestService service = new TestService(); //refactored the Completable future stuff with the listeners in a method CompletableFuture<String> future = createFutureWithListener(service); service.restart(); assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS)); CompletableFuture<String> future = createFutureWithListener(service); service.restart(); assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS)); } } Green or Red?
  • 52.
  • 53. java.lang.IllegalStateException:  Service  must   only  be  used  from  the  FX  Application  Thread     at  javafx.concurrent.Service.checkThread(Service.java:906)     at  javafx.concurrent.Service.valueProperty(Service.java:205)     at   de.saxsys.pitfalls.services.ClassToTestTest.testLongLastingOperation(ClassToTestTest.java: 40)     at  sun.reflect.NativeMethodAccessorImpl.invoke0(Native  Method)     at  sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)     at  sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:  
  • 54. @RunWith(JfxRunner.class) public class TestableServiceTest { @Test public void testLongLastingOperation() throws … { TestService service = new TestService(); //refactored the Completable future stuff with the listeners in a method CompletableFuture<String> future = createFutureWithListener(service); service.restart(); assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS)); CompletableFuture<String> future = createFutureWithListener(service); service.restart(); assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS)); } } Exception goes here
  • 55. Let’s have a look on the Service Implementation @Override public final V getValue() { checkThread(); return value.get(); } @Override public final ReadOnlyObjectProperty<V> valueProperty() { checkThread(); return value;} /** * Cancels any currently running Task, if any, and restarts this Service. The state * will be reset to READY prior to execution. This method should only be called on * the FX application thread. */ public void restart() { checkThread(); … } void checkThread() { if (startedOnce && !isFxApplicationThread()) { throw new IllegalStateException("Service must only be used from the FX Application Thread"); } }
  • 57. But I wan’t to call my service multiple times in my test - because this is it’s use-case.
  • 58.
  • 59. Let’s test this in the FX- Thread
  • 60. @RunWith(JfxRunner.class) public class TestableServiceTest { @Test public void testLongLastingOperation() throws … { TestService service = new TestService(); //refactored the Completable future stuff with the listeners in a method CompletableFuture<String> future = createFutureWithListener(service); service.restart(); assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS)); CompletableFuture<String> future = createFutureWithListener(service); service.restart(); assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS)); } }
  • 61. @RunWith(JfxRunner.class) public class TestableServiceTest { @Test @TestInJfxThread public void testLongLastingOperation() throws … { TestService service = new TestService(); //refactored the Completable future stuff with the listeners in a method CompletableFuture<String> future = createFutureWithListener(service); service.restart(); assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS)); CompletableFuture<String> future = createFutureWithListener(service); service.restart(); assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS)); } } jfx-testrunner annotation
  • 62. @RunWith(JfxRunner.class) public class TestableServiceTest { @Test @TestInJfxThread public void testLongLastingOperation() throws … { TestService service = new TestService(); //refactored the Completable future stuff with the listeners in a method CompletableFuture<String> future = createFutureWithListener(service); service.restart(); assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS)); CompletableFuture<String> future = createFutureWithListener(service); service.restart(); assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS)); } } Green or Red?
  • 63.
  • 64. java.util.concurrent.TimeoutException at java.util.concurrent.CompletableFuture.timedGet(CompletableFuture.java:1763) at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1907) at de.saxsys.pitfalls.services.nottestable.ServiceNotTestableTest.testLongLastingOperationInFXThread( TestServiceTest.java:85) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at de.saxsys.javafx.test.JfxRunner.access$0(JfxRunner.java:1) at de.saxsys.javafx.test.JfxRunner.lambda$0(JfxRunner.java:47) at de.saxsys.javafx.test.JfxRunner$$Lambda$56/1865127310.run(Unknown Source) at com.sun.javafx.application.PlatformImpl.lambda$null$170(PlatformImpl.java:295) at com.sun.javafx.application.PlatformImpl$$Lambda$52/1724593206.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at com.sun.javafx.application.PlatformImpl.lambda$runLater$171(PlatformImpl.java:294) at com.sun.javafx.application.PlatformImpl$$Lambda$51/580016451.run(Unknown Source) at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
  • 65. @RunWith(JfxRunner.class) public class TestableServiceTest { @Test @TestInJfxThread public void testLongLastingOperation() throws … { TestService service = new TestService(); //refactored the Completable future stuff with the listeners in a method CompletableFuture<String> future = createFutureWithListener(service); service.restart(); assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS)); CompletableFuture<String> future = createFutureWithListener(service); service.restart(); assertEquals("I'm an expensive result.", future.get(5, TimeUnit.SECONDS)); } } Line 85: .get() blocks the UI Thread
  • 66. Ok, correct solution: start with Test Thread and move all Service accesses to FX Thread
  • 67. FX Thread @Test public void testLongLastingOperationSimple() throws … { TestService service = new TestService(); CompletableFuture<String> future = new CompletableFuture<>(); Platform.runLater(() -> { service.valueProperty().addListener((b, o, n) -> { if (n != null) { future.complete(n); } }); service.restart(); }); assertEquals("I'm an expensive result. 1", future.get(5, TimeUnit.SECONDS)); }
  • 68.
  • 69. Ok, let’s run and test the Service multiple times
  • 70. @Test public void testLongLastingOperationSimple() throws … { TestService service = new TestService(); CompletableFuture<String> future = new CompletableFuture<>(); Platform.runLater(() -> { service.valueProperty().addListener((b, o, n) -> { if (n != null) { future.complete(n); } }); service.restart(); }); assertEquals("I'm an expensive result. 1", future.get(5, TimeUnit.SECONDS)); CompletableFuture<String> future = new CompletableFuture<>(); Platform.runLater(() -> { service.valueProperty().addListener((b, o, n) -> { if (n != null) { future.complete(n); } }); service.restart(); }); assertEquals("I'm an expensive result. 1", future.get(5, TimeUnit.SECONDS)); } For each execution of the Service you have to double the amount of the (boilerplate)-code.
  • 71. This is too much boilerplate code for testing Services
  • 72. @RunWith(JfxRunner.class) public class ServiceTestableTest { @Test public void startServiceMultipleTimesTest() throws …{ TestService service = new TestService(); ServiceWrapper wrapper = new ServiceWrapper(service); wrapper.startAndWait(6000); assertEquals("I'm an expensive result. 1", wrapper.getValue()); wrapper.restartAndWait(6000); assertEquals("I'm an expensive result. 2", wrapper.getValue()); } } no more checkThread() problems for asserts reduces platform issues get help with jfx-­‐testrunner https://github.com/sialcasa/jfx-testrunner blocks test thread
  • 73. Conclusion there are problems testing services you need a lot of boilerplate code to test them use libraries
  • 74. Don’t create a complex node graph in the UI Thread! Threading and Services
  • 75. but you should create Nodes in background threads! Attach Nodes to the scene graph on the UI Thread only,
  • 76. Load Data FX-Thread ArrayList<String> data = getBigData(); Pane displayPane = new Pane(); for (int i = 0; i < data.size(); i++) { Label label = new Label(data.get(i)); displayPane.getChildren().add(label); } root.getChildren().add(displayPane); UI-Freezes until the Graph has been build Display Loaded Data
  • 77. Load Data FX-Thread ArrayList<String> data = new ArrayList<>(); Pane displayPane = new Pane(); for (int i = 0; i < data.size(); i++) { Label label = new Label(data.get(i)); displayPane.getChildren().add(label); } Platform.runLater(() -> root.getChildren().add(displayPane)); Build Graph
  • 79. Preprocess Metadata of Images Pixel Performance
  • 80. SimpleImageInfo  info  =  new  SimpleImageInfo(file);   double  width    =  info.getWidth();   double  height  =  info.getHeight(); Uses meta-data in header [1] Loading the whole image to get the Metadata is bad. Image  image  =  new  Image("/50mp.jpg",  true);   double  width  =  image.getWidth();   double  height  =  image.getHeight(); How to preprocess the Metadata of Images?
  • 81. Read- / Write Pixels Pixel Performance
  • 82. Read / Write javafx.scene.image.PixelWriter   retrieving the pixel data from an Image writing the pixel data of a WritableImage javafx.scene.image.PixelReader Read Pixels Process Pixels Write Pixels
  • 83. Many methods, same result… PixelReader getArgb(…) getColor(…) getPixels(…) setArgb(…) setColor(…) setPixels(…)PixelWriter e.g. cropping
  • 84. DEMO
  • 85. Choose the right pixel format! Pixel Performance
  • 86. What is the right pixel format for Quantum Toolkit? BYTE_BGRA_PRE myPixelReader.setPixels(…,pixelformat  ,…); myPixelReader.getPixels(…,pixelformat  ,…);        @Override          public  PlatformImage  createPlatformImage(int  w,  int  h)  {                  ByteBuffer  bytebuf  =  ByteBuffer.allocate(w  *  h  *  4);                  return  com.sun.prism.Image.fromByteBgraPreData(bytebuf,  w,  h);          } calls
  • 87. Demo - Videoplayer using VLC & setPixels() 1. FX-Mediaplayer does not supports all codecs 2. there is no javafx.scene.media.* on ARM VLC solves 2 additional pitfalls
  • 88. Many complex nodes? Use Canvas! Pixel Performance
  • 89. Rule: use fewer nodes (>20.000 Nodes )! picking, sync, compute dirty regions, apply CSS,…. Use Canvas / Image when you have a lot to show!
  • 90. Canvas: + higher fps + lower heap + (~128MB - 250 pic.) + Node count 2 Node: - lower fps - higher heap -(~200MB - 250 pic.) - Node count >250 Demo
  • 91. Pixel Performance Conclusion be aware of complex/many nodes Node or Images? —> depends choose correct PixelFormat & methods
  • 92. Questions? Hint: JavaFX Real-World Apps [CON3907] Alexander Casall, Dirk Lemmermann Wednesday, Oct 28, 1:00 p.m.
  • 93. Links 1. http://max-wielsch.blogspot.de/2015/10/testing-asynchronous- javafx-logic.html 2. https://jaimonmathew.wordpress.com/2011/01/29/ simpleimageinfo/ 3. http://tomasmikula.github.io/blog/2015/02/10/the-trouble- with-weak-listeners.html 4. https://blog.codecentric.de/en/2015/04/tweaking-the-menu- bar-of-javafx-8-applications-on-os-x/
  • 95. • JavaFX bindings uses WeakListeners to observe dependencies • dispose root binding should be enough leaking Bindings
  • 96. • 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 !
  • 97. • 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(); }
  • 98. chain your service execution FX-Thread Executor-Thread
  • 99. • 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 !!
  • 100. • 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!
  • 101. 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
  • 102. Test  service  with  fluent  API
  • 103. 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
  • 104. 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
  • 105. OS  X  Menu  Bar
  • 106. OS  X  Menu  Bar • JavaFX is platform independent • no direct access to OS X application menu
  • 107. OS  X  Menu  Bar • Solution: Eclipse SWT -> Cocoa native binding JavaFX wrapper: NSMenuFX [3] better