【问题标题】:Execute process in Java without JFrame freezing在没有 JFrame 冻结的情况下在 Java 中执行进程
【发布时间】:2013-12-06 12:29:53
【问题描述】:

如何在 Java 中执行进程而不冻结程序? 我尝试过使用 SwingWorker,但我还不太明白它是如何工作的。

我还有其他方法可以做到这一点吗?我想在我的 JDroidLib 中使用类似的东西。如需完整源代码,请查看 GitHub:http://github.com/Team-M4gkBeatz/JDroidLib

提前致谢!

编辑:

感谢您的回答。但是我有一个有几种方法的类(嗯,它不止一个类,但你明白我的意思);我如何使用 SwingWorker 与这些交互?

这是其中一类:

/**
 *
 * @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;
    }
}

【问题讨论】:

  • StringWorker 是正确的方法。它应该为任务创建单独的线程,让 GUI 线程正常工作。请查看official tutorial 以了解有关如何使用它的更多信息。
  • 也不要发布指向您的代码的链接,而是在此处发布相关部分,或者更好地创建SSCCE,我们可以使用它来重现您的问题。

标签: java swing swingworker


【解决方案1】:

是的,您可以使用SwingWorker 的想法是,您在单独的线程(后台线程)中运行需要大量时间的任务,然后您不会阻塞您的 gui,您的 JFrame 也不会冻结。这是一个我非常喜欢Swing Worker Example的完整示例。

基本上,作为示例,您创建自己的类,扩展 SwingWorker 覆盖 doInBackground

注意: 你可以像普通类一样拥有字段。

例子:

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    
}

您还阅读了有关process(..) publish(..)done() 的信息。

然后在您的客户端代码中放入。

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

【讨论】:

  • 但是我(像我一样)有一个有几种方法的类?我该怎么办?
  • @Beatsleigher 不明白?你可以在worker中有几个方法只是一个普通的类。
  • @Beatsleigher 还是不明白,你可以像普通的类和方法一样拥有字段,只需在doInBackground() 中调用它们你忘记添加SwingWorker&lt;T,V&gt; 参数类型。并且要注意你的类是抽象的!
  • 是的,我明白了。谢谢:)
  • process 应该在这里有返回类型吗?
【解决方案2】:

您需要在单独的Thread 中运行代码。在一个程序中运行和协调多条代码路径的关键词是“并发”:

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

多线程代码可能会变得非常棘手且难以调试,但 Java 提供了一些更高级别的抽象/概念,从而使使用多线程代码不太容易出错。

查看java.util.concurrent 包了解这些概念。

由于多线程是您在使用 AWT/Swing 时必然会遇到的问题,因此有一些实用程序类/方法专门设计用于促进图形应用程序中的多线程。 SwingWorker 是其中之一(另一个是例如SwingUtilities.invokeLater())。熟悉它们是个好主意,它们可以让你的生活更轻松,尤其是对于那些时间长到足以冻结 GUI 的简单任务。

【讨论】:

    【解决方案3】:

    如果您使用的是 Swing,SwingWorker 就是这样做的方式。

    既然你提到你不明白它是如何工作的,我就简单解释一下。

    Swing 事件处理代码在event dispatch thread 中完成。因此,如果用户单击一个按钮(比如从服务器获取数据并将其显示在窗口中的更新按钮),该按钮的 ActionListener 将在事件调度线程中被调用。

    如果您在同一个线程中获取数据,那么 UI 将冻结,因为它无法处理任何事件(您正在事件调度线程中运行代码)。

    因此,您应该在单独的线程中获取数据。但是在获取数据之后,你应该在事件派发线程中将其设置到视图中。

    Swing 工作器让您轻松搞定。您将繁重的任务放在doInBackground 方法中(在我们的例子中是获取数据)并从该方法返回繁重任务的输出(在我们的例子中是数据)。

    在 swing worker 的 done 方法中调用 get 方法,它将为您提供在 doInBackground 方法中返回的数据。在那里,您使用新数据更新视图(例如将数据添加到 TableModel)。

    当你现在在你的 swing worker 上调用exectue 时,它会负责在一个单独的线程(不是事件调度线程)中运行doInBackground,并在事件中运行done 方法调度线程。

    因此,通过使用 SwingWorker,您不必担心在 EDT 中处理 UI 事件的内部机制以及在另一个线程中的耗时任务。

    【讨论】:

      猜你喜欢
      • 2012-02-07
      • 1970-01-01
      • 1970-01-01
      • 2010-12-11
      • 1970-01-01
      • 2023-03-31
      • 2013-03-17
      相关资源
      最近更新 更多