我在 Java 库中使用 API,该 API 从事件调度线程调用并要求我返回完全初始化的 UI 组件。它看起来像这样:
public JDialog createDialog();
但我只能在从数据库加载后填充对话框,有时可能需要 10 秒。通常我会在后台线程中执行此操作,但由于此方法是从 EDT 调用的,并且由于我必须返回对话框,所以这不起作用。它是一个第三方库,所以我无法更改方法,但有什么办法可以避免阻塞 EDT?
我在 Java 库中使用 API,该 API 从事件调度线程调用并要求我返回完全初始化的 UI 组件。它看起来像这样:
public JDialog createDialog();
但我只能在从数据库加载后填充对话框,有时可能需要 10 秒。通常我会在后台线程中执行此操作,但由于此方法是从 EDT 调用的,并且由于我必须返回对话框,所以这不起作用。它是一个第三方库,所以我无法更改方法,但有什么办法可以避免阻塞 EDT?
“已初始化”不一定与“已填充”相同。“初始化”通常意味着对象已经完全构造,但可能没有任何数据。“已填充”当然意味着数据存在并且任何数据获取任务都已完成。因此,可以为您的第三方库提供一个完全初始化的 JDialog,而无需任何数据。
我一直喜欢解决这个问题的方法是创建一个自定义的 JDialog,它显示一个忙碌的消息或进度条等,然后在另一个线程中请求数据。返回数据时,我用数据替换繁忙消息(在 EDT 上!)。至于如何在后台线程中执行请求,我建议使用 SwingWorkers。SwingWorker
我喜欢在我的自定义 JDialog 中使用 private来处理方法中的请求doInBackground()
,并在方法中处理与 Display 相关的任务done()
。这样做将确保与显示相关的任务仅发生在 EDT 上,而与数据库相关的任务仅发生在 EDT 之外。如果您想对使用 SwingWorkers 有一个相当好的介绍,请查看Sun 的工作线程教程。一个简单的例子是:
public class DBDIalog extends JDialog{
private JLabel busyLabel = new JLabel("Fetching data from DataBase");
public DBDialog(){
//do your initialization stuff here
}
private class DBFetcher extends SwingWorker<Void,DBInfo>{
@Override
protected DBInfo doInBackground() throws Exception{
return fetchDataFromDB(); //or whatever database call to make
}
@Override
protected void done(){
try{
DBInfo info = get();
//replace your busy label with your DBInfo
}catch(InterruptedException e){
//do appropriate thread interrupted stuff
}catch(ExecutionException e){
//do appropriate general error handling stuff
}
}
}
}
不过要记住一些事情:该done()
方法不是抽象的,因此您不需要重写它。不过,你应该。如果您的doInBackground()
实现抛出异常,除非done()
被覆盖,否则该异常将被吞没。doInBackground()
另外,除非您使用SwingUtilities.invokeLater(Runnable)
, asdoInBackground()
从与 EDT 不同的线程执行,否则不要从内部更改您的 GUI,并且从后台线程进行 GUI 更改要求出现奇怪和莫名其妙的错误。
什么时候应该使用这个?与其他编程任务不同,在 GUI 中响应时间过长的时间点要短得多——我通常看到写下来的数字约为 250 毫秒。如果您的任务花费的时间比这更长,它应该在后台线程中。在您的情况下,10 秒肯定应该在后台线程中,但是您已经知道了:)
编辑:
看到你的评论,我发现我的大部分帖子都没有实际意义。但是,您仍然可以使用 SwingWorker:
让您的 SwingWorker 执行数据检索,并在该done()
方法中,让它从数据中构造 JDialog 并将该对话框交给您的第三方库。
构建没有数据的对话框,然后启动一个任务来填充它。
从用户体验的角度来看,从开始到完成需要 10 秒的任何事情都会成为问题。最好是立即给他们一些东西,即使它不是最终形式。如有必要,您可以弹出一个模式对话框,上面只显示“正在加载”。