如何停止递归SwingWorker方法? JavaFX的

时间:2018-01-22 16:39:25

标签: java recursion javafx

我正在使用一个递归方法,它实现了使用SwingWorker类在一个文件夹及其所有子文件夹中进行研究 - 在本地硬盘中。

基本上工作正常,但是当我想要停止SwingWorker方法时我就陷入困境:当用户更改'源文件夹'时(我正在使用JTree - JAVAFX - 显示本地硬盘中的所有文件夹) ,我想在该文件夹中停止当前的“SwingWorker研究”,并开始一个新的,从用户中选择最新的“源路径”结果。

研究的所有结果都存储在一个私有的ObservableList中 - 并且每次都在done()方法中更新,只需填写一个TableView - JavaFX:因此,当用户更改“源路径”时,我必须清理以前的研究结果。

开始方法:

private static ObservableList<msg> data = FXCollections.observableArrayList();
private static SwingWorker<Void, Void> worker;
private static String currentFolder;

@Override
public void start(Stage primaryStage) throws Exception {
    // TODO Auto-generated method stub

    stage = primaryStage;
    primaryStage.setScene(new Scene(createContent()));
    styleControls();
    primaryStage.initStyle(StageStyle.UNDECORATED);
    primaryStage.setMaximized(true);
    primaryStage.setFullScreen(false);
    primaryStage.show();
    msgp = new MsgParser();
}

createContent()方法 - 这里调用的递归函数:

public Parent createContent() {
tree.getSelectionModel().selectedItemProperty().addListener( new ChangeListener<Object>() {

        @Override
        public void changed(ObservableValue observable, Object oldValue,
                Object newValue) {

            TreeItem<File> selectedItem = (TreeItem<File>) newValue;
            currentFolder = selectedItem.getValue().getAbsolutePath();

            // I want to stop here the previous SwingWorker call : the tree
            // ChangeListener event is called when the user change the
            // source folder of the research, by selecting one TreeItem on it. 
            if(worker!= null)
                worker.cancel(true);
            //Here I clean previous results
            data.clear();
            TV.setItems(data);

            //And I call again the method with the new source Folder
            ListMail(new File(currentFolder));

        }

    });
}

ListMail()方法:[递归SwingWorker]

private void ListMail(File dir) {

    worker = new SwingWorker<Void, Void>() {
        @Override
        protected Void doInBackground() throws Exception {
            File[] directoryListing = dir.listFiles();
            if (directoryListing != null) {
                for (File child : directoryListing) {
                    if(!worker.isCancelled()) {
                        if(child != null){
                            if(!child.isDirectory()) {
                                if(child.getAbsolutePath().substring(child.getAbsolutePath().lastIndexOf('.')+1).equals("msg")) {
                                    Message message = msgp.parseMsg(child.getPath());
                                    String percorsoMail = child.getAbsolutePath().toUpperCase();
                                    if(message != null) {
                                        String fromEmail = message.getFromEmail();
                                        String fromName = message.getFromName();
                                        String subject = message.getSubject();
                                        String received = message.getDate().toString();

                                        String name;
                                        if(fromEmail != null)
                                            name = fromName + "(" + fromEmail + ")";
                                        else name = fromName;

                                        msg Message = new msg(name, subject, received);

                                        if(!data.contains(Message))
                                            data.add(Message);
                                        //I use the Platform.runLater to
                                        // take count of the number of results found
                                        //It updates the GUI - works fine
                                        Platform.runLater(new Runnable() {
                                            @Override public void run() {
                                                if(data != null && data.size() > 0)
                                                    setStatusLabel(data.size());
                                                else
                                                    setStatusLabel(0);
                                            }
                                        });
                                    }
                                }
                            } else {
                                /**
                                 * Recursive call here : I do the research
                                 * for the subfolders
                                 */
                                ListMail(child);
                            }
                        } else {
                        }
                    }
                }
            }
            return null; 
        }    

        // Update GUI Here
        protected void done() {
            // I refresh here the TableView: works fine on-the-fly added results
            TableView.setItems(data);
            TableView.refresh();
        }

    };

    //This doesn't do anything
    if(!worker.isCancelled())
        worker.execute();

}

基本上,问题是SwingWorker线程永远不会停止,我在想,因为递归调用会在每次运行时创建新的pid进程? 此外,通过使用我希望避免的专用外部按钮,没有结果:

refreshBtn.setOnAction(e -> {
            //Handle clicks on refreshBtn button
            worker.cancel(true);
        });

单击TreeItem更改source-folder后,它只删除当时创建的所有ObservableList元素,但之前的研究并未停止。 如果我等待研究完成,一切都运行良好 - 但这只有在我处于深层文件夹时才有效,而当研究从“C:\”文件夹开始时我无法等待。

1 个答案:

答案 0 :(得分:0)

好的,这就是我如何通过使用javafx.concurrent来管理它。

仅仅指出我对此的体验,似乎使用递归背景Task进行潜在的长时间计算,例如扫描整个本地驱动器,就像在我的示例中一样,它非常耗费内存 - 也因为我存储了一些结果这个背景计算在静态局部变量中更快地访问它们:结果是一个数据结构(ObservableList),有超过5000个自定义类实例来表示计算的特定数据,然后是OutOfMemoryError消息或背景在长时间运行(等待垃圾收集?)后,线程就像在“待机”中没有任何建议一样。

无论如何这里的代码总结了我如何解决:线程正确关闭。顺便说一句,有时,由于在isCancelled()方法检查上清理GUI,有一点'GUI延迟':GUI在清除/不清楚之间摆动,因为在我看来它一直被先前任务的结果所填充在递归中。

private static BackgroundTask backgroundTask;
private static Thread thread;

tree.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Object>() {

        @Override
        public void changed(final ObservableValue observable, final Object oldValue, final Object newValue) {

            //I close previous running background tasks if there's any 
            if (backgroundTask != null) {
                while (backgroundTask.isRunning()) {
                    backgroundTask.cancel(true);
                    // reset GUI nodes here used to show results of the previous thread
                }

            }

            backgroundTask = new BackGoundTask();
            thread= new Thread(backgroundTask);
            thread.setDaemon(true);
            thread.start();

            //This will be called only when latest recursion is finished, not at every run
            backgroundTask.setOnSucceeded(e -> {});

        }

    });

BackgroundTask类:

public static class BackgroundTask extends Task<Object> {

    // .. variables used by the task here

    //constructor: initialize variables at every run of the Task
    public BackgroundTask() {

    }

    @Override
    protected Object call() throws Exception {
        if (!isCancelled()) {
            // ... Do all background work here

            Platform.runLater(new Runnable() {
                @Override
                public void run() {
                    //  GUI progress can goes here                      
                }
            });

            //recursion here            
            if(something) {
                //...
            } else {
                call();
            }   

        } else {
            //user want to cancel task: clean GUI nodes
        }

    return null;
    }
}