RFR: JDK-8312058: Documentation improvements for subscription based listeners [v6]
John Hendrikx
jhendrikx at openjdk.org
Fri Aug 18 10:49:34 UTC 2023
On Fri, 11 Aug 2023 22:03:28 GMT, John Hendrikx <jhendrikx at openjdk.org> wrote:
>> Incorporates documentation review comments from #1069.
>>
>> This PR should be kept minimal so it can be backported to JFX21; it should only contain documentation changes.
>
> John Hendrikx has updated the pull request incrementally with one additional commit since the last revision:
>
> Update wording on value subscriber
I think that might be better; changing docs on `Observable` and `ObservableValue` is a good idea, but may take some time to get agreement on. I would like to also make clear in such a change when you can expect listener / subscribers to fire, basically documenting the undocumented rules we already have (invalidations fire first then changes; listeners fire in the order they were added, allowing vetoing (which is used in and outside of JavaFX).
The general rules would be something like:
- Observers on super types fire before observers on sub types (ie. `Observable` before `ObservableValue`)
- Observers added on a single type fire in the order they were added (ie. value subscribers, change subscribers and change listeners are not separate lists as they're all offered by `ObservableValue`)
I've done an attempt to update the `Observable` class documentation with all recent developments:
/**
* An {@code Observable} is an entity that wraps content and allows to
* observe the content for invalidations.
*
* <h2>Lazy evaluation and events</h2>
*
* An implementation of {@code Observable} may support lazy evaluation,
* which means that the content is not immediately recomputed after changes, but
* lazily the next time it is requested. All bindings and properties in
* this library support lazy evaluation.
* <p>
* Implementations of this class should strive to generate as few events as
* possible to avoid wasting too much time in event handlers. Implementations in
* this library mark themselves as invalid when the first invalidation event
* occurs. They do not generate anymore invalidation events until their value is
* recomputed and valid again.
*
* <h2>Observing an {@code Observable}</h2>
*
* Observation is possible using listeners and subscribers. These are
* interchangeable and have the same semantics:
* <li>Observers are notified in the order they were added</li>
* <li>When an observer is registered more than once, it will be notified more than once</li>
* <li>Observers may be safely registered for different {@code Observable}s</li>
* <p>
* When observing an {@code Observable}, it will hold a strong reference to the observer which will
* prevent it from being garbage collected as long as the {@code Observable} is reachable.
* If the lifecycle of the {@code Observable} is longer than a registered observer, care
* must be taken to unregister the observer after use to avoid unwanted retention of the
* observer, and any other objects it may refer to, in memory (also known as a memory leak).
*
* <h2>Unregistering observers</h2>
*
* Unregistering observers when their use ends can be achieved in several ways:
* <li>For listeners, call the corresponding {@code removeListener} method, ensuring to pass the exact same instance as was used to register it</li>
* <li>For subscribers, call {@link Subscription#unsubscribe()} on each {@link Subscription}, or on a combined subscription</li>
* <li>If the {@code Observable} is an {@code ObservableValue}, {@link ObservableValue#when(ObservableValue)} can be used to automatically
* decouple the lifecycle of the observer from the original {@code ObservableValue} when a condition (like visibility) holds:
* <pre>
* {@code
* observableValue.when(condition).subscribe(...)
* }</pre>
* </li>
* <li>For listeners, it's possible to use a weak wrapper, like {@link WeakInvalidationListener}, to automatically unregister the listener when it becomes weakly reachable</li>
* <p>
* Note that the use of weak listener wrappers is no longer recommended when it can be
* avoided as it requires intimate knowledge of how weak references work in Java and how
* they interact with the garbage collector. The timing of when a weak listener is
* unregistered may be very different from the intended or expected timing. It can
* depend on many external factors, including the JVM used, its version, its (GC) settings,
* but also on the system type and its memory availability.
* <li>Garbage collection may be deferred for long periods (or even indefinitely), resulting in weak listeners being removed much later than expected;
* until such time, they will keep receiving events, and hold on to any referenced objects</li>
* <li>Weak listeners may be cleaned up almost immediately, possibly resulting in a listener being removed if care wasn't taken
* to reference it directly</li>
* <li>Registering the same or a similar weak listener again may see strange behavior as a previous weak listener may still be present</li>
* <p>
* The above can lead to hard to reproduce issues that may vary from system to system.
* <p>
* It is therefore highly recommended to always remove observers which observe an {@code Observable}
* that have a longer lifecycle. This can be done either manually (in a clean up or dispose method),
* or automatically using a condition with {@code ObservableValue#when}.
*
* @see javafx.beans.value.ObservableValue
* @see javafx.collections.ObservableList
* @see javafx.collections.ObservableMap
*
*
* @since JavaFX 2.0
*/
-------------
PR Comment: https://git.openjdk.org/jfx/pull/1177#issuecomment-1683728523
More information about the openjfx-dev
mailing list