Feedback requested for infrastructure for properties that wish to delay registering listeners
John Hendrikx
john.hendrikx at gmail.com
Thu Jan 19 15:49:31 UTC 2023
Hi list,
I've been looking into what it would take to make the design of
properties which only register listeners when needed easier to implement
with JavaFX, both for JavaFX itself as well as for the user. This is
the result of my investigation into ListProperty (and other collection
properties) and how hard it would be to change their semantics to only
register listeners when they're absolutely required for the correct
functioning of the class.
Currently, JavaFX doesn't offer the tools one would need to register
listeners in a just-in-time fashion. This is because there is no
notification mechanism available in its observable values when they
become observed or unobserved. The closest thing there currently exists
is what ObjectBinding and LazyObjectBinding offer:
From ObjectBinding:
/**
* Checks if the binding has at least one listener registered on
it. This
* is useful for subclasses which want to conserve resources when
not observed.
*
* @return {@code true} if this binding currently has one or more
* listeners registered on it, otherwise {@code false}
* @since 19
*/
protected final boolean isObserved() {
return helper != null;
}
From LazyObjectBinding:
/**
* Called after a listener was added to start observing inputs if
they're not observed already.
*/
private void updateSubscriptionAfterAdd() { ... }
/**
* Called after a listener was removed to stop observing inputs if
this was the last listener
* observing this binding.
*/
private void updateSubscriptionAfterRemove() { ... }
I'm proposing to extend the JavaFX with new methods that can be used to
stay informed about the observable's current observed status. These
methods will facilitate implementations of properties that wish to delay
registering listeners until they are themselves observed. This can save
memory, save unnecessary notifications via events and reduces the need
for weak listener constructs.
The proposal does not require any new fields, and would default to
reporting observable values as being observed. Most JavaFX observable
values should be retrofitted to support this functionality -- the ones
relying on a form of ExpressionHelper will only require minimal changes
with minimal impact.
The methods that I would like to see added are the following:
In ObservableValue:
/**
* Checks if this ObservableValue is currently observed. If unknown
or unsupported,
* {@code true} is returned.
*
* @return {@code true} if this ObservableValue currently has one
or more
* listeners registered on it, otherwise {@code false}
*/
default boolean isObserved() { return true; }
The above method is useful for debugging, but its primary use is for
complex properties which observed status is determined by not only its
direct listeners but also any child properties it may offer.
ListProperty is such an example, which offers child properties size and
empty. Its observed status would be determined like this:
helper != null || (size0 != null && size0.isObserved()) || (empty0
!= null && empty0.isObserved())
Further, we need two protected callback methods. These should be added
in all Binding and Property Base classes (basically all classes that
implement addListener/removeListener methods). These are called when the
observed status of the property changes:
/**
* Called when this property transitions from unobserved to observed.
*/
protected void observed() {}
/**
* Called when this property transitions from observed to unobserved.
*/
protected void unobserved() {}
These methods are implemented by child properties in order to inform the
parent that their observed status has changed, which may require the
parent property to change its status as well.
When implemented, LazyObjectBinding can be simplified as some or all of
its functionality will be part of ObjectBinding (LazyObjectBinding is
not a public class at the moment). The isObserved method in
ObjectBinding would go from protected to public (it's already final).
Implementation for classes that rely on a form of ExpressionHelper is
simple; they can check if the helper is null or not (for isObserved) and
check if the nullity changed during addListener/removeListener calls to
make their call to "observed" or "unobserved". No additional fields are
required.
I've added a proof of concept here
(https://github.com/openjdk/jfx/pull/1004) where `ListPropertyBase` was
altered to use these new methods to delay its listener registration and
to remove its listener when no longer observed. This PoC includes the
tests written by Florian Kirmaier which fail on the current ListProperty
implementation (but pass with this version).
--John
More information about the openjfx-dev
mailing list