Reputation: 3294
I have a model class with SimpleXXXXProperty
properties. Javafx GUI elements are updated using either bindings or change listeners, e.g.
textField.textProperty().bind(myModel.myModelStatus());
or
myModel.myModelStatus().addListener((obj,oldv.newv) -> { update here });
When the instance of the model class changes, I rebind the controls and add the listeners again. However, I can see by the memory use that the old model still persists in memory.
What do I have to do to remove all references to the model so it can be cleaned up?
Is there are more automatic way of updating bindings and listeners on nested properties when the parent property changes?
Upvotes: 1
Views: 1696
Reputation: 24454
Points to consider when you want to undo bindings (including listeners) to your model:
p1.bind(p2)
) are automatically unbound when binding the same property again (e.g. p1.bind(p3)
), but it does not hurt to do it explicitely (p1.unbind()
).p1.bindBidirectional(p2)
or Bindings.bindBidirectional(p1, p2)
) have to be unbound explicitely (p1.unbindBidirectional(p2)
or Bindings.unbindBidirectional(p1, p2)
).prop.removeListener(l)
).The third is the tricky part, as listeners are often implemented as lambda expressions or method references. Unfortunately, lambda expressions as well as method references(!) are not constant:
// lambdas are not constant
InvalidationListener l1 = obs -> {};
InvalidationListener l2 = obs -> {};
assert l1 != l2; // they are NOT identical!
Well, this might be obvious for lambdas, but the same is also true for method references, which is really annoying:
// method references are not constant
Runnable runnable1 = this::anyMethod;
Runnable runnable2 = this::anyMethod;
assert runnable1 != runnable2; // they are NOT identical!
That means, you cannot register a lambda expression or a simple method reference as listener if you want to be able to unregister it:
// if you register listeners on a property like that...
label.textProperty().addListener(obs -> System.out.println(obs));
label.textProperty().addListener(this::handleLabelInvalid);
// ...these calls WON'T remove them due to the inequality shown above!
label.textProperty().removeListener(obs -> System.out.println(obs));
label.textProperty().removeListener(this::handleLabelInvalid);
Solution
You have to store a reference to the lambda expression or method referency by yourself. I use to use final fields for that:
public class MyClass {
// store references for adding/removal
private final InvalidationListener l1 = this::handleLabelInvalid;
private final InvalidationListener l2 = obs -> System.out.println(obs);
...
public void bind() {
label.textProperty().addListener(l1);
label.textProperty().addListener(l2);
}
public void unbind() {
label.textProperty().removeListener(l1);
label.textProperty().removeListener(l2);
}
private void handleLabelInvalid(Observable observable) { ... }
}
Upvotes: 5