Cleaning up bindings and change listeners on nested properties when parent properties change in javafx - javafx

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?
Unbind before binding again
Remove the listeners
Both
Is there are more automatic way of updating bindings and listeners on nested properties when the parent property changes?

Points to consider when you want to undo bindings (including listeners) to your model:
Undirectional bindings (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()).
Bidirectional bindings (p1.bindBidirectional(p2) or Bindings.bindBidirectional(p1, p2)) have to be unbound explicitely (p1.unbindBidirectional(p2) or Bindings.unbindBidirectional(p1, p2)).
Listeners must be unregistered (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) { ... }
}

Related

How to check and also set variable synchronously in Kotlin?

I have a mutable nullable property. I'd like to check in a method if its value is null, if so, set it to a newly created object. Then I'd call a method of the property (old or new, but existing object).
And this should be thread-safe. How can I do that in Kotlin?
With .let, I cannot reassign the property. When using synchronized(...) or .withLock, between the assignment and invocation, the thread can be interrupted by another. Or at least the IDE says so, smart cast is impossible. I don't understand, why is synchronized there if it doesn't synchronize the block?
var starterTask: AsyncTask<MyData, Void, Void>? = null
fun start() {
make it thread safe {
if (starterTask == null) {
starterTask = a child of AsyncTask()
}
starterTask.execute(this) // <- no !! operator
}
}
In Java, if I locked an object with synchronized, I could check, assign, invoke an object, everyone was happy. Can I do the same in Kotlin?
You can use a delegated property for that:
val starterTask = lazy { a child of AsyncTask() }
fun start() {
starterTask.execute(this)
The default thread safety mode is SYNCHRONIZED, so it matches your needs.

Access Kotlin Delegate Type without an Instance

I have read Access property delegate in Kotlin which is about accessing a delegate from an instance. One can use KProperty::getDelegate since Kotlin 1.1, however this will return the instance of the delegate and therefore needs an instance of the class first.
Now I want to get the type of the delegate without having an instance of the class. Consider a library with a custom delegate type CustomDelegate that want's to get all properties of a class that are delegated to an instance of CustomDelegate:
class Example
{
var nonDelegatedProperty = "I don't care about this property"
var delegatedProperty1 by lazy { "I don't care about this too" }
var delegatedProperty2 by CustomDelegate("I care about this one")
}
How can I, given I have KClass<Example>, but not an instance of Example, get all properties delegated to CustomDelegate?
How can I, given I have KClass<Example>, but not an instance of
Example, get all properties delegated to CustomDelegate?
You can do it in two ways depending on your needs.
First of all, you have to include the kotlin-reflect dependency in your build.gradle file:
compile "org.jetbrains.kotlin:kotlin-reflect:1.1.51"
In my opinion, you should use the first solution if you can, because it's the most clear and optimized one. The second solution instead, can handle one case that the first solution can't.
First
You can loop an the declared properties and check if the type of the property or the type of the delegate is CustomDelegate.
// Loop on the properties of this class.
Example::class.declaredMemberProperties.filter { property ->
// If the type of field is CustomDelegate or the delegate is an instance of CustomDelegate,
// it will return true.
CustomDelegate::class.java == property.javaField?.type
}
There's only one problem with this solution, you will get also the fields with type CustomDelegate, so, given this example:
class Example {
var nonDelegatedProperty = "I don't care about this property"
val delegatedProperty1 by lazy { "I don't care about this too" }
val delegatedProperty2 by CustomDelegate("I care about this one")
val customDelegate = CustomDelegate("jdo")
}
You will get delegatedProperty2 and customDelegate. If you want to get only delegatedProperty2, I found an horrible solution that you can use if you need to manage this case.
Second
If you check the source code of KPropertyImpl, you can see how a delegation is implemented. So, you can do something like this:
// Loop on the properties of this class.
Example::class.declaredMemberProperties.filter { property ->
// You must check in all superclasses till you find the right method.
property::class.allSuperclasses.find {
val computeField = try {
// Find the protected method "computeDelegateField".
it.declaredFunctions.find { it.name == "computeDelegateField" } ?: return#find false
} catch (t: Throwable) {
// Catch KotlinReflectionInternalError.
return#find false
}
// Get the delegate or null if the delegate is not present.
val delegateField = computeField.call(property) as? Field
// If the delegate was null or the type is different from CustomDelegate, it will return false.
CustomDelegate::class.java == delegateField?.type
} != null
}
In this case, you will get only delegatedProperty2 as result.

Why change in ArrayCollection's length doesn't invoke Setter on component using it as data source?

I have a component where I expose the property 'questions' with the following code:
private var _questions:ArrayCollection;
private var questionsChanged:Boolean;
[Bindable("questionsChanged")]
public function get questions():ArrayCollection {
return _questions;
}
public function set questions(value:ArrayCollection):void {
if (_questions != value) {
_questions = value;
questionsChanged = true;
invalidateProperties();
dispatchEvent(new Event("questionsChanged"));
}
}
In this component, I use commitProperties() to implement my logic.
I use Cairngorm and the 'questions' is in the model and hence it's defined as a source for data binding.
When the 'questions' ArrayCollection's size changes elsewhere in the application, it is not invoking the setter method in the component that is destination for the data binding.
Could someone help me understand why this is the case?
You'll have to show the code where you are changing the array collection. But, this will fire the setter:
questions = somethingArrayCollection();
This will not:
questions.addItem(newQestion)
The questions variable is, basically, a pointer. Changing the thing that the variable points to does not need the set event.
I suggest you look at the CollectionChangeEvent, which the ArrayCollection fires when items are added to and from that. Listen to the event and perform your 'change' actions in the event handler. ( or tie into the lifecycle and invalidate some flag and perform your changes in commitProperties() )

Is there any way to add a valueCommit lifecycle to non-mxml components in Actionscript?

The invalidate/commitProperties model used by mxml components is very useful, in my experience, and I'd like to be able to make use of it in domain model objects in my actionscript applications. How can I go about adding lifecycle events like that to my objects? Is there a global object lifecycle manager?
As noted by Robert Bak, you're essentially on your own to implement such a mechanism for non-UI components.
I've found this a very useful technique to use on model classes, since it can dramatically reduce the "thrashing" of bound-property updates when your model classes are not simple data transfer objects - i.e. they have any kind of multi-property logic encapsulated within them.
Since my use-case is for model objects, I didn't need all the methods of IInvalidating.
Here's my particular implementation as a starting point for your own efforts. Note that this comes from a "base model class" we use called RAFModel and that this is for the Flex 4 SDK.
// INVALIDATION AND COMMITPROPERTIES PATTERN
private var invalidatePropertiesFlag:Boolean;
public function invalidateProperties():void
{
if (!invalidatePropertiesFlag)
{
invalidatePropertiesFlag = true;
invalidateModelObject(this);
}
}
protected function commitProperties():void
{
// override this
}
// -- INVALIDATION SUPPORT
public static var invalidObjects:Dictionary = new Dictionary(true);
public static var validatePending:Boolean = false;
public static function invalidateModelObject(obj:RAFModel):void
{
invalidObjects[obj] = true;
if (!validatePending)
{
validatePending = true;
FlexGlobals.topLevelApplication.callLater(validateObjects);
}
}
protected static function validateObjects():void
{
var invalidQueue:Dictionary = invalidObjects;
// start a fresh tracker for further invalidations
// that are a side effect of this pass
invalidObjects = new Dictionary(true);
// ready to receive another call
validatePending = false;
for (var o:* in invalidQueue)
{
var rm:RAFModel = o as RAFModel;
if (rm)
{
// clear the flag first, in case we're reentrant
// on any given instance
rm.invalidatePropertiesFlag = false;
rm.commitProperties();
}
}
}
Invalidation and commitProperties isn't linked to MXML (you can use it with as components) but it is linked to the flex managed visual component lifecycle (as they are the only ones which need to be synchronized with the flash frame by frame rendering). So unless you're talking about visual components it will not work out of the box.
But if you're looking to implement the same mechanism for your non-visual classes, you should probably start by implementing IInvalidating (docs) and creating a mechanism that calls the validateNow() function when the validation needs to be done.
The Flex Component LifeCycle is designed to handle a User Interface Component's creation, destruction, and changes during the time in between. I, personally, do not find the approach appropriate for non-User Interface components.
You could, if you wanted, extend UIComponent in your domain model objects and then add that domain model as a child to a container. it would then go through the Flex Component LifeCycle validation phases (commitProperties, updateDisplayList, and measure).
But, I would not recommend that approach.

Binding to a read-only getter in AS3

Consider the following code:
[Bindable(event="ReportHeaderVO_effectiveFromDateJulian_updated")]
public function set effectiveFromDateJulian ( value:Number ) : void
{
_effectiveFromDateJulian = value;
dispatchEvent( new FlexEvent("ReportHeaderVO_effectiveFromDateJulian_updated") );
}
public function get effectiveFromDateJulian () : Number
{
return _effectiveFromDateJulian;
}
public function get effectiveFromDate () : Date
{
return DateUtil.convertJDEJulianToDate(_effectiveFromDateJulian);
}
There is a setter and a getter for the effectiveFromDateJulian which is a number representation of the date. I have provided a seperate getter which retrieves the same value, only converted to a proper date. It is a getter only though and relies on the setter for the numeric property to get its data from; so the effectiveFromDate property is effectively read-only.
Data binding works on the effectiveFromDateJulian property; any updates work fine and notify everything properly. But when binding to the effectiveFromDate (getter only) property, I get a warning from the compiler:
warning: unable to bind to property 'effectiveToDate' on class 'com.vo::ReportHeaderVO'
Is there a way to make it possible to bind to this read-only property? I would assume I would have to dispatch an event on the setter that effects the read-only property, but I don't know what that would look like.
This is a simple example, you could imagine a read-only property that depends on several setters to function and when any of those setters are updated the read-only property would need to fire a propertyChanged event as well. Any ideas? Please let me know if I need to clarify anything.
Update:
From the Adobe documentation here:
http://livedocs.adobe.com/flex/3/html/help.html?content=databinding_8.html
Using read-only properties as the
source for data binding
You can automatically use a read-only
property defined by a getter method,
which means no setter method, as the
source for a data-binding expression.
Flex performs the data binding once
when the application starts.
Because the data binding from a
read-only property occurs only once at
application start up, you omit the
[Bindable] metadata tag for the
read-only property.
And this makes sense for constant values, but in this case the value does change, it just doesn't get set directly.
Make the readonly getter Bindable and dispatch the corresponding event from the original setter method.
[Bindable(event="ReportHeaderVO_effectiveFromDateJulian_updated")]
public function set effectiveFromDateJulian ( value:Number ) : void
{
_effectiveFromDateJulian = value;
dispatchEvent( new FlexEvent("ReportHeaderVO_effectiveFromDateJulian_updated") );
dispatchEvent( new FlexEvent("ReportHeaderVO_effectiveFromDate_updated") );
}
[Bindable(event="ReportHeaderVO_effectiveFromDate_updated")]
public function get effectiveFromDate (date:Date) : Date
{
return DateUtil.convertJDEJulianToDate(_effectiveFromDateJulian);
}

Resources