Przeglądaj źródła

make sure to release all strong referenced to the I/O chart when closing the window

Sebastian Stenzel 5 lat temu
rodzic
commit
d00d9d98dc

+ 21 - 0
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<Number> observable) {
+		return new LongBinding() {
+			{
+				bind(observable);
+			}
+
+			@Override
+			protected long computeValue() {
+				return observable.getValue().longValue();
+			}
+		};
+	}
+
 }

+ 38 - 18
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<Number, Number> lineGraph;
+	private final VaultStats stats;
 	private final Series<Number, Number> readData;
 	private final Series<Number, Number> writeData;
-	private Timeline ioAnimation;
+	private final Timeline ioAnimation;
+	private final LongBinding bpsRead;
+	private final LongBinding bpsWritten;
 
+	public LineChart<Number, Number> 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<Vault> vaultProperty() {
-		return vault;
-	}*/
 }

+ 6 - 1
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<? extends VaultState> observable, VaultState oldValue, VaultState newValue) {
 				if (newValue != VaultState.UNLOCKED) {
-					stage.hide();
+					Stage stage = weakStage.get();
+					if (stage != null) {
+						stage.hide();
+					}
 					observable.removeListener(this);
 				}
 			}

+ 2 - 2
main/ui/src/main/resources/fxml/vault_statistics.fxml

@@ -16,12 +16,12 @@
 	<HBox alignment="CENTER_RIGHT" spacing="6">
 		<Label styleClass="label-small,label-muted" text="%main.vaultDetail.bytesPerSecondRead"/>
 		<ThrougputLabel styleClass="label-small,label-muted" alignment="CENTER_RIGHT" minWidth="60" idleFormat="%main.vaultDetail.throughput.idle" kibsFormat="%main.vaultDetail.throughput.kbps"
-						mibsFormat="%main.vaultDetail.throughput.mbps" bytesPerSecond="${controller.vault.stats.bytesPerSecondRead}"/>
+						mibsFormat="%main.vaultDetail.throughput.mbps" bytesPerSecond="${controller.bpsRead}"/>
 	</HBox>
 	<HBox alignment="CENTER_RIGHT" spacing="6">
 		<Label styleClass="label-small,label-muted" text="%main.vaultDetail.bytesPerSecondWritten"/>
 		<ThrougputLabel styleClass="label-small,label-muted" alignment="CENTER_RIGHT" minWidth="60" idleFormat="%main.vaultDetail.throughput.idle" kibsFormat="%main.vaultDetail.throughput.kbps"
-						mibsFormat="%main.vaultDetail.throughput.mbps" bytesPerSecond="${controller.vault.stats.bytesPerSecondWritten}"/>
+						mibsFormat="%main.vaultDetail.throughput.mbps" bytesPerSecond="${controller.bpsWritten}"/>
 	</HBox>
 
 	<LineChart