Java 在调用SwingWorker.get()时防止GUI冻结
我有一个程序,在加载文件的同时显示一个窗口,通知用户正在加载文件。我决定创建一个FileLoader类,它是一个实际处理加载文件的SwingWorker,以及一个实现PropertyChangeListener的ProgressWindow,以通知用户传递给它的SwingWorker的状态 我的代码当前如下所示:Java 在调用SwingWorker.get()时防止GUI冻结,java,swing,swingworker,Java,Swing,Swingworker,我有一个程序,在加载文件的同时显示一个窗口,通知用户正在加载文件。我决定创建一个FileLoader类,它是一个实际处理加载文件的SwingWorker,以及一个实现PropertyChangeListener的ProgressWindow,以通知用户传递给它的SwingWorker的状态 我的代码当前如下所示: FileLoader loader = new FileLoader(filePath); new ProgressWindow(loader, "Loading File", "Lo
FileLoader loader = new FileLoader(filePath);
new ProgressWindow(loader, "Loading File", "Loading File");
//ProgressWindow's constructor calls loader.execute() inherited from SwingWorker
doc = loader.get(); //GUI Freezes when called
问题是,每当我调用loader.get()时,它都会冻结GUI,因此进度窗口中的进度条不会运行,整个过程毫无意义。据我所知,这是因为控制GUI的线程与调用loader.get()的线程相同,后者在loader.execute()运行时处于挂起状态
到目前为止,我已经尝试为loader.get()命令或loader.execute()方法创建一个新线程,并在该线程上调用SwingUtilities.invokeLater(),但随后整个程序冻结
我考虑过为when SwingWorker.isDone()创建一个ChangeListener,然后运行loader.get(),但这需要对我的代码进行一些我不愿意做的修改
有谁能告诉我,最好的办法是什么
到目前为止,我已经尝试为loader.get()命令或loader.execute()方法创建一个新线程,并在该线程上调用SwingUtilities.invokeLater(),但随后整个程序冻结
如果在将在EDT中执行线程的线程上调用SwingUtilities.invokeLater()
,将冻结GUI。相反,通过调用它的start()
方法来运行线程,并且只在需要更新PropertyChangeListener
中的进度条时使用SwingUtilities.invokeLater()
,get()
类似于join()
,因为它会一直阻塞,直到被调用为止,并将等待SwingWorker完成后再被调用。错误地使用它首先会完全抵消使用SwingWorker的所有优势
解决方案:在知道SwingWorker的处理完成之前,不要调用它,方法是在SwingWorker的done()
方法中调用它,或者如果需要从调用代码中调用它,则在SwingWorker的“状态”为属性为SwingWorker.StateValue.DONE
比如:
final FileLoader loader = new FileLoader(filePath);
loader.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if ("state".equals(evt.getPropertyName())) {
// since DONE is enum, no need for equals(...) method
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
try {
loader.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
});
new ProgressWindow(loader, "Loading File", "Loading File");
注意:代码未编译或测试
编辑:添加try/catch。我创建了一个WorkerThread类,负责线程和GUI当前/主线程。 我已经将我的GUI应用程序放在WorkerThread的construct()方法中,当一个事件触发启动XXXServer时,所有线程都被激活,GUI在不冻结的情况下顺利工作。看一看
/**
* Action Event
*
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
public void actionPerformed(ActionEvent ae) {
log.info("actionPerformed begin..." + ae.getActionCommand());
try {
if (ae.getActionCommand().equals(btnStart.getText())) {
final int portNumber = 9990;
try {
WorkerThread workerThread = new WorkerThread(){
public Object construct(){
log.info("Initializing the Server GUI...");
// initializing the Server
try {
xxxServer = new XXXServer(portNumber);
xxxServer.start();
btnStart.setEnabled(false);
} catch (IOException e) {
// TODO Auto-generated catch block
log.info("actionPerformed() Start button ERROR IOEXCEPTION..." + e.getMessage());
e.printStackTrace();
}
return null;
}
};workerThread.start();
} catch (Exception e) {
log.info("actionPerformed() Start button ERROR..." + e.getMessage());
e.printStackTrace();
}
} else if (ae.getActionCommand().equals(btnStop.getText())) {
log.info("Exit..." + btnStop.getText());
closeWindow();
}
} catch (Exception e) {
log
.info("Error in ServerGUI actionPerformed==="
+ e.getMessage());
}
}
不幸的是,我不能在done()期间调用get(),因为我需要将get()的结果返回给调用方法,而done()不返回任何内容。而PropertyChangeListener正是我希望避免的事情,但我认为这可能是必要的。为什么要避免呢?编写代码很容易,如果需要调用get(),这是必要的。它只需要在程序的另一部分重写代码,但我认为这是必要的。这也意味着我必须在更多的地方编写更多的代码。我希望有一个很好的封装解决方案。@Thunderforge您读过SwingWorker API吗???publish()或process(),btw API描述了正确的解决方案,包括代码我已经尝试过处理这个问题,但由于线程无法返回值,所以我没有找到一个好方法来实现它。您可以改为在线程之外修改值。或者如果你真的需要一个返回值,使用一个线程。我喜欢FutureTask的想法,但是我很难让我的头脑集中在它上面。谢谢