简体   繁体   中英

How to stop recursive SwingWorker method? JavaFX

I'm using a recursive method which implements the use of the SwingWorker class to do a research in one folder and all its subfolders - in the local hard drive.

Basically works fine but I'm stuck when I want to stop the SwingWorker method: when the user change the 'source folder' (I'm using a JTree - JAVAFX - to show all the folders in the local hard drive), I want to stop the current 'SwingWorker research' in that folder and start a new one, with the newest 'source path' results choosed from the user.

All the results of the research are stored in a private ObservableList - and updated everytime in the done() method, just by filling one TableView - JavaFX: so, when the user change the 'source path' I have to clean the results of the previous research.

Start method:

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() method- recursive function its called here:

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() method: [recursive 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();

}

Basically, the issue is that the SwingWorker thread never stop, I'm thinking because of the recursive calls which creates new pid process at every run or something ? Also by using a dedicated external button, which I prefer to avoid, gives no results:

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

After I click on TreeItem to change source-folder, it just delete all the ObservableList elements created at that moment, but the previous research don't stop. Everything works fine instead if I wait the research its finished - but this can works only when I'm in a deep-level folder, while I can't obviously wait when the research start with the "C:\\" folder.

Ok so that's here how I managed this by using javafx.concurrent.

Just to point my experience with this, it seems using a recursive background Task for potentially long computations, such as scanning the Whole local drive like in my example, it's very memory consuming - also because I stored some results of this background computation in static local variables to access them faster: the result was a data-structure (ObservableList) with over 5000+ instances of a custom class to represent that specific data computed and then the OutOfMemoryError message or the background thread just going like in 'stand-by' without any advice after running for long time (waiting for garbage collection?).

Anyway here's the code that sum up how I solved: the threads are correctly closed. By the way, sometimes, there's a little 'GUI delay' due to cleaning the GUI on the isCancelled() method check: the GUI swing between clear/not clear, because in my opinion it keeps get filled by the results of the previous tasks in the recursion.

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 class:

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;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM