简体   繁体   中英

Execute process in Java without JFrame freezing

how can I execute a process in Java without the program freezing? I've tried using SwingWorker, but I don't quite yet understand how it works.

Is there any other way I can accomplish this? I'm wanting to use something like this in my JDroidLib. For full source code, check out the GitHub: http://github.com/Team-M4gkBeatz/JDroidLib

Thanks in advance!

EDIT:

Thanks for your answers. But I have a class with several methods (well, it's more than one class, but you get my point); how can I use a SwingWorker to interact with those?

Here is one of the classes:

/**
 *
 * @author Simon
 */
    public abstract class Command extends SwingWorker {

    BufferedReader prReader = null;
    ProcessBuilder process = null;
    Process pr = null;
    Date timeNow = new Date();
    String osName = System.getProperty("os.name");

    public void executeProcessNoReturn(String _process, String arg) throws IOException {
        process = new ProcessBuilder(_process, arg);
        pr = process.start();
    }

     public String executeProcessReturnLastLine(String _process, String arg) throws IOException {
        process = new ProcessBuilder(_process, arg);
        pr = process.start();
        prReader = new BufferedReader(new InputStreamReader(pr.getInputStream()));
        String line;
        while ((line = prReader.readLine()) != null) {
            // Wait for input to end.
        }
        return line;
    }

    public StringBuilder executeProcessReturnAllOutput(String _process, String arg) throws IOException {
        process = new ProcessBuilder(_process, arg);
        pr = process.start();
        prReader = new BufferedReader(new InputStreamReader(pr.getInputStream()));
        StringBuilder output = null;
        String line;
        while ((line = prReader.readLine()) != null) {
            output.append(line);
        }
        return output;
    }

    public boolean isProcessRunning(String processName) throws IOException {
        boolean value = false;
        if (osName.equals("Linux") | osName.contains("Mac")) {
            process = new ProcessBuilder("ps", "-e");
            pr = process.start();
            String line;
            prReader = new BufferedReader(new InputStreamReader(pr.getInputStream()));
            while ((line = prReader.readLine()) != null) {
                if (line.contains(processName)) { value = true; break; }
            }
        } else {
            String winDir = System.getenv("windir") + "/System32/tasklist.exe";
            process = new ProcessBuilder(winDir);
            pr = process.start();
            String line;
            prReader = new BufferedReader(new InputStreamReader(pr.getInputStream()));
            while ((line = prReader.readLine()) != null) {
            if (line.contains(processName)) { value = true; break; }
            }
        }

        return value;

    }

     public String executeProcessReturnError(String processName, String arg) throws IOException {
        process = new ProcessBuilder(processName, arg);
        process.redirectError();
        pr = process.start();
        prReader = new BufferedReader(new InputStreamReader(pr.getErrorStream()));
        String line;
        String output = "";
        while ((line = prReader.readLine()) != null) {
        output += line + "\n";
        }
        return output;
    }
}

Yes you can use a SwingWorker the idea is that a task that takes a lot of time you run in a separate thread (background thread) then you don't block your gui and your JFrame is not gonna to freeze. Here is a complete example i really like Swing Worker Example .

Basically as an example you create your own class that extends SwingWorker override doInBackground .

NOTE: You can have fields like a normal class.

Example :

class Worker extends SwingWorker<Void, String> {
    private SomeClass businessDelegate;    
    private JLabel label;

    @Override
    protected Void doInBackground() throws Exception {
       //here you make heavy task this is running in another thread not in EDT
        businessDelegate.callSomeService();
        setProgress(30); // this is if you want to use with a progressBar
        businessDelegate.saveToSomeDataBase();
        publish("Processes where saved");
        return null;
    }

    @Override
    protected void process(List<String> chunks){
       //this is executed in EDT you can update a label for example
       label.setText(chunks.toString());
    }

   //add setters for label and businessDelegate    
}

You also read about process(..) publish(..) and done() .

And in your client code just put.

SwingWorker<Void,String> myWorker = new Worker();
myWorker.execute();

You need to run the code in a separate Thread . The keyword for running and coordinating multiple paths of code in a program is "concurrency":

http://docs.oracle.com/javase/tutorial/essential/concurrency/index.html

Multithreaded code can become quite tricky and difficult to debug, but Java provides some higher level abstractions/concepts that make working with multi-threaded code somewhat less error-prone.

Take a look at the java.util.concurrent package for these concepts.

Since multithreading is an issue you're bound to run into when working with AWT/Swing, there are some utility classes/methods especially designed to facilitate multithreading in graphics applications. SwingWorker is one one of them (another one is eg SwingUtilities.invokeLater() ). It would be a good idea to familiarize yourself with them, they can make your life easier, especially for simple tasks that are just long enough to otherwise freeze the GUI.

If you are using Swing, SwingWorker is the way to do it.

Since you have mentioned that you don't understand how it works, I will give a simple explanation.

Swing event handling code is done in the event dispatch thread . So if a user clicks a button (say update button which fetches the data from server and shows them in the window), the ActionListener of that button is called in the event dispatch thread.

If you fetch the data in the same thread then the UI would freeze, because it cannot process any events (you are running code in the event dispatch thread).

So you should fetch the data in a separate thread. But after fetching the data, you should set it to the view in the event dispatch thread.

The Swing worker makes this easy for you. You put the heavy task in the doInBackground method(in our case fetching the data) and return the output of the heavy task (in our case the data) from the method.

In the done method of the swing worker you call the get method which will give you the data returned in the doInBackground method. There, you update the view with the new data(For example add the data to a TableModel).

When you call exectue on your swing worker now, it will take care of running the doInBackground in a separate thread (which is not the Event Dispatch Thread) and it will run the done method in the event dispatch thread.

So by using the SwingWorker, you don't have to worry about the internals of handling UI events in the EDT and the time consuming tasks in another thread.

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