garlicDoge
garlicDoge

Reputation: 197

SwingWorker doesnt run doInBackground properly

So I got into SwingWorkers to handle my Text manipulating with different Classes and Threads. As shown below, my Swingworker gets a filepath and scans the text, passing the lines to a String. With getData() I return the scanned String to my main Class. But this does not work until I run the method scanFile()in the Constructor of my Worker Class. So my Question is: Why does my SwingWorker Class not run the doInBackground() properly?

public class ScanWorker extends SwingWorker<Void, Void> {

    private File file;
    private String text;

    ScanWorker(File file) {
        this.file = file;
    }

    @Override
    protected Void doInBackground() throws Exception {
        scanFile();
        return null;        
    }

    public String getData() {
        return text;
    }

    private void scanFile() {
        String line = "";
        try {

            Scanner scan = new Scanner(file);

            while(scan.hasNextLine()) {
                line = scan.nextLine();
                if(!scan.hasNextLine()) {
                    text += line;
                } else {
                    text += line + "\n";    
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

Upvotes: 0

Views: 1212

Answers (2)

Boann
Boann

Reputation: 50021

  1. You should not be adding your own getData() method for the return value, because this isn't thread-safe. It's precisely what SwingWorker tries to avoid by providing its own mechanism for transferring the result back to the Swing thread. The result type is the first type parameter of the SwingWorker, so instead of SwingWorker<Void,Void>, you should have SwingWorker<String,Void>.

  2. Change protected Void doInBackground() to protected String doInBackground() (since that is your result type).

  3. Remove the text field, and instead return the result String from doInBackground().

  4. After you return the result from the doInBackground() method on the worker thread, the done() method will be called on the Swing thread. You're missing that method. From that method you can call get() to retrieve the result. Add the method as follows:

    @Override
    protected void done() {
        String result;
        try {
            result = get();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        // Now do whatever you want with the loaded data:
        ...
    }
    
  5. SwingWorker doesn't do anything until you start it by calling its execute() method. Make sure you're doing that!

P.S. You shouldn't build up the text in a String variable directly, as that recopies the entire string every time you append a line to it, which gives atrocious performance. Use a StringBuilder for this sort of thing, calling toString() on it only at the end.

Or, since there is no point splitting the file to lines if you only want to join them back together again, you can read the full file in one go:

@Override
protected String doInBackground() throws Exception {
    return new String(
        java.nio.file.Files.readAllBytes(file.toPath()),
        java.nio.charset.StandardCharsets.UTF_8);
}

Upvotes: 1

Marko Topolnik
Marko Topolnik

Reputation: 200148

If you put scanFile() in the constructor, then you executed that method on the current thread, defeating the purpose of SwingWorker.

Your pull-based approach where you getData() from the worker, probably immediatly after you execute it, is wrong: you must instead override done() and do any work related to the produced results. Alternatively you may use the publish and process approach where you receive pieces of data in the EDT as they are being produced by the worker thread.

Upvotes: 1

Related Questions