Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this a valid way to minimize binding invalidations?

I have some complex Observable structures, which may or may not be bad ideas, but which are not the focus of this question.

The problem with those structures is that they generate a lot of invalidations of Observable objects being displayed by the UI. As near as I can tell, when the JavaFX UI is displaying something, it registers a ChangeListener on it, so any attempts to use lazy evaluation go out the window. That is, invalidating the observable seems to tell the UI that it has potentially changed, which causes the UI to immediately request it's value, forcing it to evaluate immediately.

So, I had the idea of deferring the invalidations via Platform.runLater().

I created a class called DeferredBinding that delegates everything to a wrapped Binding, except the invalidate() method, which it defers to the JavaFX UI thread to be processed later. It seems to work... I can invalidate a hundred times and it only seems to actually process the invalidation once.

But, I've not seen this pattern before and I am afraid it might fall into the category of "nice try but bad idea".

So, question: is this a bad idea? I am particularly concerned of errors introduced into other Observable objects that depend on the DeferredBinding. Will they be fine once the Platform.runLater() happens?

package com.myapp.SAM.model.datastructures;

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;

import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.beans.binding.Binding;
import javafx.beans.value.ChangeListener;
import javafx.collections.ObservableList;

/**
 * Specialized binding that defers its invalidations to the JavaFX UI thread in a throttled manner. The idea being that, if invalidate() is called many times,
 * it only basically happens once (when the UI thread gets to it).
 */
public class DeferredBinding<T> implements Binding<T> {

    private static final Logger logger = Logger.getLogger(DeferredBinding.class.getName());

    private final Binding<T> binding;

    private final AtomicBoolean pendingInvalidation = new AtomicBoolean(false);

    public DeferredBinding(Binding<T> binding) {
        this.binding = binding;
    }

    @Override
    public void addListener(ChangeListener<? super T> listener) {
        binding.addListener(listener);
    }

    @Override
    public void removeListener(ChangeListener<? super T> listener) {
        binding.removeListener(listener);
    }

    @Override
    public T getValue() {
        return binding.getValue();
    }

    @Override
    public void addListener(InvalidationListener listener) {
        binding.addListener(listener);
    }

    @Override
    public void removeListener(InvalidationListener listener) {
        binding.removeListener(listener);
    }

    @Override
    public boolean isValid() {
        return binding.isValid();
    }

    /**
     * Override logic for invalidate() method to defer invalidation to runLater. Throttle the invalidations so as not to floor the JavaFX UI thread with
     * multiple calls
     */
    @Override
    public void invalidate() {
        if (pendingInvalidation.getAndSet(true) == false) {
            Platform.runLater(() -> {
                // Signal that the UI is processing the pending invalidation, so any additional invalidations must schedule another update.
                pendingInvalidation.set(false);
                binding.invalidate();
            });
        }
    }

    @Override
    public ObservableList<?> getDependencies() {
        return binding.getDependencies();
    }

    @Override
    public void dispose() {
        binding.dispose();
    }

}
like image 866
Matthew McPeak Avatar asked Aug 26 '17 02:08

Matthew McPeak


People also ask

What is invalidation?

5 statements of invalidation that you shouldn't say when trying to support to someone you care about. Home Book Now (954) 787-6800 What is Invalidation? 5 Things You Shouldn’t Say Validation doesn’t necessarily mean we agree with another’s subjective reality. Validation simply allows another person’s emotional state a space to exist.

What is valid and binding obligation?

Valid and Binding Obligation Sample Clauses. Valid and Binding Obligation . The Transaction Documents to which the Seller or the Depositor is a party constitute, and when executed by the Seller and the Depositor (if not previously) will constitute, the legal, valid and binding obligations of the Seller and the Depositor, as applicable,...

Why do people invalidate each other?

Others may invalidate unintentionally. The well-intentioned invalidators often defend that the goal is to help someone feel better or differently—to an emotion they judge as a more accurate, more valid one. If you’re the recipient of invalidating messages, know this: YOU’RE NOT CRAZY!

What is validation in a relationship?

Validation doesn’t necessarily mean we agree with another’s subjective reality. Validation simply allows another person’s emotional state a space to exist. Validation is a critical communication tool and expression of love and acceptance in relationships.


Video Answer


1 Answers

I would not try to solve performance problems ahead of time. Measure your application to determine if you have a problem and then go ahead..

Let's assume you have a problem, there are many ways to solve your abstract problem. Three solutions come to my mind:

1

Coalesce updates (Platform.runLater()) to prevent saturation of the FX event queue as you have done in your example.

And since you are just invalidating the binding you do not have to fear loosing a value on the way. So it seems (without knowing the full application) that this way should work.


2

Using the Nodes built-in behavior of marking regions dirty for (re-)layouting. At some point you will call javafx.scene.Parent.requestLayout() (this is the "invalidation") which will at some point in the future call javafx.scene.Parent.layoutChildren() to apply the dirty attributes to the region.


3

Using solution 2 in a different way: By using a virtualized layout container. The TableView, TreeTableView and ListView all use the virtualized approach to update only the visible cells. See com.sun.javafx.scene.control.skin.VirtualFlow and com.sun.javafx.scene.control.skin.VirtualContainerBase for some JDK examples, another example would be the Flowless API.

As you did not tell any specifics I can not guide you any further. But it should be clear, that your approach may very well work and there are other ways.

like image 119
eckig Avatar answered Oct 22 '22 13:10

eckig