From d00d9d98dc008ea3d93813620408dbb67c0f7e2f Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Tue, 16 Jun 2020 15:35:12 +0200 Subject: [PATCH] make sure to release all strong referenced to the I/O chart when closing the window --- .../cryptomator/ui/common/WeakBindings.java | 21 +++++++ .../VaultStatisticsController.java | 56 +++++++++++++------ .../VaultStatisticsModule.java | 7 ++- .../main/resources/fxml/vault_statistics.fxml | 4 +- 4 files changed, 67 insertions(+), 21 deletions(-) diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/WeakBindings.java b/main/ui/src/main/java/org/cryptomator/ui/common/WeakBindings.java index c02eb1d86..70111dd38 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/common/WeakBindings.java +++ b/main/ui/src/main/java/org/cryptomator/ui/common/WeakBindings.java @@ -1,7 +1,9 @@ package org.cryptomator.ui.common; +import javafx.beans.binding.LongBinding; import javafx.beans.binding.StringBinding; import javafx.beans.value.ObservableObjectValue; +import javafx.beans.value.ObservableValue; /** @@ -29,4 +31,23 @@ public final class WeakBindings { }; } + /** + * Create a new LongBinding that listens to changes from the given observable without being strongly referenced by it. + * + * @param observable The observable + * @return a LongBinding weakly referenced from the given observable + */ + public static LongBinding bindLong(ObservableValue observable) { + return new LongBinding() { + { + bind(observable); + } + + @Override + protected long computeValue() { + return observable.getValue().longValue(); + } + }; + } + } diff --git a/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsController.java b/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsController.java index b23df4939..d76156b42 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsController.java @@ -3,6 +3,7 @@ package org.cryptomator.ui.vaultstatistics; import javafx.animation.Animation; import javafx.animation.KeyFrame; import javafx.animation.Timeline; +import javafx.beans.binding.LongBinding; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.fxml.FXML; @@ -12,7 +13,9 @@ import javafx.scene.chart.XYChart.Series; import javafx.stage.Stage; import javafx.util.Duration; import org.cryptomator.common.vaults.Vault; +import org.cryptomator.common.vaults.VaultStats; import org.cryptomator.ui.common.FxController; +import org.cryptomator.ui.common.WeakBindings; import javax.inject.Inject; @@ -22,31 +25,38 @@ public class VaultStatisticsController implements FxController { private static final int IO_SAMPLING_STEPS = 100; private static final double IO_SAMPLING_INTERVAL = 0.5; - private final Stage window; - private final Vault vault; - @FXML - private LineChart lineGraph; + private final VaultStats stats; private final Series readData; private final Series writeData; - private Timeline ioAnimation; + private final Timeline ioAnimation; + private final LongBinding bpsRead; + private final LongBinding bpsWritten; + public LineChart lineGraph; @Inject public VaultStatisticsController(@VaultStatisticsWindow Stage window, @VaultStatisticsWindow Vault vault) { - this.window = window; - this.vault = vault; + this.stats = vault.getStats(); + this.bpsRead = WeakBindings.bindLong(stats.bytesPerSecondReadProperty()); + this.bpsWritten = WeakBindings.bindLong(stats.bytesPerSecondWrittenProperty()); - readData = new Series<>(); + this.readData = new Series<>(); readData.setName("Read Data"); // For Legend //TODO Add Name to strings.properties - writeData = new Series<>(); + this.writeData = new Series<>(); writeData.setName("Write Data"); //TODO Add Name to strings.properties - ioAnimation = new Timeline(); //TODO Research better timer + this.ioAnimation = new Timeline(); //TODO Research better timer ioAnimation.getKeyFrames().add(new KeyFrame(Duration.seconds(IO_SAMPLING_INTERVAL), new IoSamplingAnimationHandler(readData, writeData))); ioAnimation.setCycleCount(Animation.INDEFINITE); ioAnimation.play(); + + // make sure to stop animating, + // otherwise a global timer (GC root) will keep a strong reference to animation + window.setOnHiding(evt -> { + ioAnimation.stop(); + }); } @FXML @@ -84,20 +94,30 @@ public class VaultStatisticsController implements FxController { } // add latest value: - final long decBytes = vault.getStats().bytesPerSecondReadProperty().get(); + final long decBytes = stats.bytesPerSecondReadProperty().get(); final double decMb = decBytes * BYTES_TO_MEGABYTES_FACTOR; - final long encBytes = vault.getStats().bytesPerSecondWrittenProperty().get(); + final long encBytes = stats.bytesPerSecondWrittenProperty().get(); final double encMb = encBytes * BYTES_TO_MEGABYTES_FACTOR; decryptedBytesRead.getData().get(IO_SAMPLING_STEPS - 1).setYValue(decMb); encryptedBytesWrite.getData().get(IO_SAMPLING_STEPS - 1).setYValue(encMb); } } - public Vault getVault() { - return vault; + /* Getter/Setter */ + + public LongBinding bpsReadProperty() { + return bpsRead; + } + + public long getBpsRead() { + return bpsRead.get(); + } + + public LongBinding bpsWrittenProperty() { + return bpsWritten; + } + + public long getBpsWritten() { + return bpsWritten.get(); } - /* - public ReadOnlyObjectProperty vaultProperty() { - return vault; - }*/ } diff --git a/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsModule.java b/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsModule.java index 0485c17d9..4acad0869 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsModule.java @@ -19,6 +19,7 @@ import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.common.StageFactory; import javax.inject.Provider; +import java.lang.ref.WeakReference; import java.util.Map; import java.util.ResourceBundle; @@ -39,11 +40,15 @@ abstract class VaultStatisticsModule { Stage stage = factory.create(); stage.setTitle(String.format(resourceBundle.getString("vaultstatistics.title"), vault.getDisplayableName())); stage.setResizable(false); + var weakStage = new WeakReference<>(stage); vault.stateProperty().addListener(new ChangeListener<>() { @Override public void changed(ObservableValue observable, VaultState oldValue, VaultState newValue) { if (newValue != VaultState.UNLOCKED) { - stage.hide(); + Stage stage = weakStage.get(); + if (stage != null) { + stage.hide(); + } observable.removeListener(this); } } diff --git a/main/ui/src/main/resources/fxml/vault_statistics.fxml b/main/ui/src/main/resources/fxml/vault_statistics.fxml index 36f0098ce..207950297 100644 --- a/main/ui/src/main/resources/fxml/vault_statistics.fxml +++ b/main/ui/src/main/resources/fxml/vault_statistics.fxml @@ -16,12 +16,12 @@