如何正确清理 popover 中的 ownerWindow 侦听器? /JavaFX
How to properly clean ownerWindow listener in popover ? /JavaFX
我使用 JDK8 创建了一个 JavaFX 应用程序,其中包含一个 window 和多个对象。
我现在正在尝试为 GarbageCollector 提供无用的已用对象。(使用 JVisualVM 进行测试)。
但我遇到了一个问题:
清除 window 元素上包含处理程序和侦听器的 Popover。
弹窗原代码:
public class CustomPopOver extends PopOver {
/**
* Constructor with the Content of the PopOver.
* @param content the Node.
*/
public CustomPopOver (Node content) {
super(content);
addHandler();
}
/**
* Empty Constructor.
*/
public CustomPopOver () {
super();
addHandler();
}
private void addHandler() {
this.ownerWindowProperty().addListener((observable, oldValue, newValue) -> {
if (newValue != null) {
EventHandler<WindowEvent> preExistingHandler = newValue.getOnCloseRequest();
newValue.setOnCloseRequest(event -> {
if (this.isShowing()) {
this.hide(Duration.millis(0));
}
if (preExistingHandler != null) {
preExistingHandler.handle(event);
}
});
}
});
}
}
我尝试了很多方法来解决这个问题,但它无法正常工作:
public class CustomPopOver extends PopOver implements DisposableBean {
private MyListener listener = new MyListener();
public CustomPopOver (Node content) {
super(content);
addHandler();
}
/**
* Empty Constructor.
*/
public CustomPopOver () {
super();
addHandler();
}
private void addHandler() {
this.ownerWindowProperty().addListener(listener);
}
@Override
public void destroy() {
if (this.getOwnerWindow() != null){
this.getOwnerWindow()
.removeEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, listener.windowCloseEventHandler);
this.getOwnerWindow()
.removeEventHandler(WindowEvent.WINDOW_HIDING, listener.windowHidingEventHandler);
}
this.ownerWindowProperty().removeListener(listener);
listener = null;
}
/**
* ChangeListener that removes itself when needed.
*/
private class MyListener implements ChangeListener<Window> {
EventHandler<WindowEvent> windowCloseEventHandler;
EventHandler<WindowEvent> windowHidingEventHandler;
@Override
public void changed(ObservableValue<? extends Window> observable, Window oldValue, Window newValue) {
if (oldValue != null) {
oldValue.removeEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, windowCloseEventHandler);
oldValue.removeEventHandler(WindowEvent.WINDOW_HIDING, windowHidingEventHandler);
}
if (newValue != null) {
EventHandler<WindowEvent> preExistingHandler = newValue.getOnCloseRequest();
windowCloseEventHandler = new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
if (isShowing()) {
hide(Duration.millis(0));
ownerWindowProperty().removeListener(MyListener.this);
}
if (preExistingHandler != null) {
preExistingHandler.handle(event);
}
newValue.removeEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, this);
}
};
newValue.setOnCloseRequest(windowCloseEventHandler);
windowHidingEventHandler = new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
ownerWindowProperty().removeListener(MyListener.this);
newValue.removeEventHandler(WindowEvent.WINDOW_HIDING, this);
}
};
newValue.setOnHiding(windowHidingEventHandler);
}
}
}
}
然后我们调用 destroy 方法从 jvm 缓存中清除 popover。
要测试的代码 class CustomPopOver:
public class PopOverViewer extends Application {
private BorderPane pane;
public PopOverViewer() {
pane = new BorderPane();
pane.setCenter(button());
}
private Node button() {
HBox hBox = new HBox();
List<CustomPopOver > lists = new ArrayList<>();
Button show = new Button("click");
show.setOnAction(event -> {
CustomPopOver popOver = new CustomPopOver ();
lists.add(popOver);
popOver.show(show);
});
Button clean = new Button("clean");
clean.setOnAction(event -> {
lists.forEach(CustomPopOver::destroy);
lists.clear();
});
hBox.getChildren().addAll(show, clean);
return hBox;
}
@Override
public void start(Stage primaryStage) {
PopOverViewer app = new PopOverViewer();
primaryStage.setScene(new Scene(app.getPane()));
primaryStage.show();
}
private Parent getPane() {
return pane;
}
}
我希望 class CustomPopover 从 GC 中清除。
感谢@fabian,将 WeakEventHandler 放在强引用监听器内部的处理程序上,有助于清理它。
有效代码:
public class CustomPopOver extends PopOver implements DisposableBean {
private MyListener listener = new MyListener();
/**
* Constructor with the Content of the PopOver.
* @param content the Node.
*/
public CustomPopOver(Node content) {
super(content);
addHandler();
}
/**
* Empty Constructor.
*/
public CustomPopOver() {
super();
addHandler();
}
private void addHandler() {
this.ownerWindowProperty().addListener(listener);
}
@Override
public void destroy() {
this.ownerWindowProperty().removeListener(listener);
listener = null;
}
/**
* ChangeListener that removes itself when needed.
*/
private class MyListener implements ChangeListener<Window> {
@Override
public void changed(ObservableValue<? extends Window> observable, Window oldValue, Window newValue) {
if (newValue != null) {
EventHandler<WindowEvent> preExistingHandler = newValue.getOnCloseRequest();
EventHandler<WindowEvent> windowCloseEventHandler = new WeakEventHandler<>(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
if (isShowing()) {
hide(Duration.millis(0));
ownerWindowProperty().removeListener(CustomPopOver.MyListener.this);
}
if (preExistingHandler != null) {
preExistingHandler.handle(event);
}
newValue.removeEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, this);
}
});
newValue.setOnCloseRequest(windowCloseEventHandler);
EventHandler<WindowEvent> windowHidingEventHandler = new WeakEventHandler<>(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
ownerWindowProperty().removeListener(CustomPopOver.MyListener.this);
newValue.removeEventHandler(WindowEvent.WINDOW_HIDING, this);
}
});
newValue.setOnHiding(windowHidingEventHandler);
}
}
}
}
我使用 JDK8 创建了一个 JavaFX 应用程序,其中包含一个 window 和多个对象。 我现在正在尝试为 GarbageCollector 提供无用的已用对象。(使用 JVisualVM 进行测试)。
但我遇到了一个问题:
清除 window 元素上包含处理程序和侦听器的 Popover。
弹窗原代码:
public class CustomPopOver extends PopOver {
/**
* Constructor with the Content of the PopOver.
* @param content the Node.
*/
public CustomPopOver (Node content) {
super(content);
addHandler();
}
/**
* Empty Constructor.
*/
public CustomPopOver () {
super();
addHandler();
}
private void addHandler() {
this.ownerWindowProperty().addListener((observable, oldValue, newValue) -> {
if (newValue != null) {
EventHandler<WindowEvent> preExistingHandler = newValue.getOnCloseRequest();
newValue.setOnCloseRequest(event -> {
if (this.isShowing()) {
this.hide(Duration.millis(0));
}
if (preExistingHandler != null) {
preExistingHandler.handle(event);
}
});
}
});
}
}
我尝试了很多方法来解决这个问题,但它无法正常工作:
public class CustomPopOver extends PopOver implements DisposableBean {
private MyListener listener = new MyListener();
public CustomPopOver (Node content) {
super(content);
addHandler();
}
/**
* Empty Constructor.
*/
public CustomPopOver () {
super();
addHandler();
}
private void addHandler() {
this.ownerWindowProperty().addListener(listener);
}
@Override
public void destroy() {
if (this.getOwnerWindow() != null){
this.getOwnerWindow()
.removeEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, listener.windowCloseEventHandler);
this.getOwnerWindow()
.removeEventHandler(WindowEvent.WINDOW_HIDING, listener.windowHidingEventHandler);
}
this.ownerWindowProperty().removeListener(listener);
listener = null;
}
/**
* ChangeListener that removes itself when needed.
*/
private class MyListener implements ChangeListener<Window> {
EventHandler<WindowEvent> windowCloseEventHandler;
EventHandler<WindowEvent> windowHidingEventHandler;
@Override
public void changed(ObservableValue<? extends Window> observable, Window oldValue, Window newValue) {
if (oldValue != null) {
oldValue.removeEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, windowCloseEventHandler);
oldValue.removeEventHandler(WindowEvent.WINDOW_HIDING, windowHidingEventHandler);
}
if (newValue != null) {
EventHandler<WindowEvent> preExistingHandler = newValue.getOnCloseRequest();
windowCloseEventHandler = new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
if (isShowing()) {
hide(Duration.millis(0));
ownerWindowProperty().removeListener(MyListener.this);
}
if (preExistingHandler != null) {
preExistingHandler.handle(event);
}
newValue.removeEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, this);
}
};
newValue.setOnCloseRequest(windowCloseEventHandler);
windowHidingEventHandler = new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
ownerWindowProperty().removeListener(MyListener.this);
newValue.removeEventHandler(WindowEvent.WINDOW_HIDING, this);
}
};
newValue.setOnHiding(windowHidingEventHandler);
}
}
}
}
然后我们调用 destroy 方法从 jvm 缓存中清除 popover。
要测试的代码 class CustomPopOver:
public class PopOverViewer extends Application {
private BorderPane pane;
public PopOverViewer() {
pane = new BorderPane();
pane.setCenter(button());
}
private Node button() {
HBox hBox = new HBox();
List<CustomPopOver > lists = new ArrayList<>();
Button show = new Button("click");
show.setOnAction(event -> {
CustomPopOver popOver = new CustomPopOver ();
lists.add(popOver);
popOver.show(show);
});
Button clean = new Button("clean");
clean.setOnAction(event -> {
lists.forEach(CustomPopOver::destroy);
lists.clear();
});
hBox.getChildren().addAll(show, clean);
return hBox;
}
@Override
public void start(Stage primaryStage) {
PopOverViewer app = new PopOverViewer();
primaryStage.setScene(new Scene(app.getPane()));
primaryStage.show();
}
private Parent getPane() {
return pane;
}
}
我希望 class CustomPopover 从 GC 中清除。
感谢@fabian,将 WeakEventHandler 放在强引用监听器内部的处理程序上,有助于清理它。
有效代码:
public class CustomPopOver extends PopOver implements DisposableBean {
private MyListener listener = new MyListener();
/**
* Constructor with the Content of the PopOver.
* @param content the Node.
*/
public CustomPopOver(Node content) {
super(content);
addHandler();
}
/**
* Empty Constructor.
*/
public CustomPopOver() {
super();
addHandler();
}
private void addHandler() {
this.ownerWindowProperty().addListener(listener);
}
@Override
public void destroy() {
this.ownerWindowProperty().removeListener(listener);
listener = null;
}
/**
* ChangeListener that removes itself when needed.
*/
private class MyListener implements ChangeListener<Window> {
@Override
public void changed(ObservableValue<? extends Window> observable, Window oldValue, Window newValue) {
if (newValue != null) {
EventHandler<WindowEvent> preExistingHandler = newValue.getOnCloseRequest();
EventHandler<WindowEvent> windowCloseEventHandler = new WeakEventHandler<>(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
if (isShowing()) {
hide(Duration.millis(0));
ownerWindowProperty().removeListener(CustomPopOver.MyListener.this);
}
if (preExistingHandler != null) {
preExistingHandler.handle(event);
}
newValue.removeEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, this);
}
});
newValue.setOnCloseRequest(windowCloseEventHandler);
EventHandler<WindowEvent> windowHidingEventHandler = new WeakEventHandler<>(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
ownerWindowProperty().removeListener(CustomPopOver.MyListener.this);
newValue.removeEventHandler(WindowEvent.WINDOW_HIDING, this);
}
});
newValue.setOnHiding(windowHidingEventHandler);
}
}
}
}