ReactiveX / RxJavaFX

RxJava bindings for JavaFX
Apache License 2.0
519 stars 67 forks source link

JavaFxObservable.valuesOf() and Bindings.bindBidirectional() strange behaviour. #76

Closed pkrysztofiak closed 6 years ago

pkrysztofiak commented 6 years ago

EDIT Rx works fine. Described issue is caused by weak refernces. I wanted to bind two properties and listen for changes on both, using RxJavaFx. What I expected was - bind two properties, write to only one of them and listen for changes on both. It turned out it worked as I expected only using pure fx. Using RxJavaFx (please uncomment lines) causes firing change event only on directly updated property (even fx listener stopped to work). Another strange thing is that at first everything seems to work perfectly ("wow" line).

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class BidingsTestApp extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) throws Exception {
        Button button1 = new Button("one");
        Button button2 = new Button("two");
        HBox hBox = new HBox(button1, button2);

        stage.setScene(new Scene(hBox));
        stage.show();

        ObjectProperty<String> property1 = new SimpleObjectProperty<>();
        ObjectProperty<String> property2 = new SimpleObjectProperty<>();

        Bindings.bindBidirectional(property1, property2);

        property1.addListener((ChangeListener<String>) (observable, oldValue, newValue) -> System.out.println("fx property1 changed to value=" + newValue));
        property2.addListener((ChangeListener<String>) (observable, oldValue, newValue) -> System.out.println("fx property2 changed to value=" + newValue));
//        JavaFxObservable.valuesOf(property1).subscribe(text -> System.out.println("rx property1 changed to value=" + text));
//        JavaFxObservable.valuesOf(property2).subscribe(text -> System.out.println("rx property2 changed to value=" + text));

        property2.set("wow");

        button1.setOnMouseClicked(event -> property2.setValue("one"));
        button2.setOnMouseClicked(event -> property2.setValue("two"));
    }
}
thomasnield commented 6 years ago

From my experience, you should avoid mixing bidirectional bindings and Rx. It just gets messy and I've found them to behave strangely. That is an interesting observation though.

Can you pinpoint what would cause the issue? I'll go ahead and see if I can get input from some JavaFX folks as well. I'm a bit busy nowadays and having a hard enough time getting the JavaFX 11 support version out.

pkrysztofiak commented 6 years ago

Can you pinpoint what would cause the issue?

Issue is caused by weak references on which JavaFx bidirectional bind is built on. Sooner or later the same behaviour would occur without using Rx. Using Rx accelerated the symptom by getting garbage collector into the play earlier. The solution is to assign property1, property2 to fields. It's documented in JavaFx so it has got nothing to do with Rx and was just my mistake. For now on everything with bidirectional bind and Rx works just fine.

thomasnield commented 6 years ago

Interesting nonetheless though. I'll go ahead and close and please let me know if anything else pops up, even if it's interesting behaviors.