如何正确清理popover中的ownerWindow侦听器? / JavaFX

问题描述 投票:0回答:1

我已经用JDK8创建了JavaFX应用程序,其中包含一个窗口和多个对象。我现在正在尝试将无用的可用对象用于GarbageCollector。(使用JVisualVM测试)。

但是我遇到了一个问题:清理一个包含窗口元素上的处理程序和侦听器的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缓存中清除弹出窗口。

测试类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;
    }

}

我希望CustomPopover类可以从GC中清除。

memory memory-management javafx-8 handler
1个回答
0
投票

感谢@fabian,将WeakEventHandler放在强引用的侦听器内的Handler上,有助于清理它。

有效的代码:

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);
            }
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.