我在Java库中使用API,它从事件调度线程调用,并要求我返回一个完全初始化的UI组件。它看起来像这样:
public JDialog createDialog();
但我只能在从数据库加载后填充对话框,有时可能需要10秒。通常我会在后台线程中执行此操作,但由于此方法是从EDT调用的,因为我必须返回对话框,这将无效。这是第三方库,所以我无法改变方法,但是我能做些什么来避免阻止EDT?
答案 0 :(得分:5)
“已初始化”不一定与“已填充”相同。 “已初始化”通常表示对象已完全构建,但可能没有任何数据。 “填充”当然意味着数据存在且任何数据获取任务都已完成。因此,可以为您的第三方库提供完全初始化的JDialog,而不需要任何数据。
我总是喜欢解决这个问题的方法是创建一个显示忙消息或进度条等的自定义JDialog,然后在另一个线程中请求数据。返回数据时,我将忙信息替换为数据(在EDT上!)。至于如何在后台线程中执行请求,我建议使用SwingWorkers。我喜欢在我的自定义JDialog中使用私有SwingWorker
,它在doInBackground()
方法中处理请求,并在done()
方法中处理与显示相关的任务。这样做将确保与显示相关的任务仅在EDT上发生,并且与数据库相关的任务仅在EDT之外发生。如果您想要使用SwingWorkers的相当好的介绍,请查看Sun's tutorial on worker threads。一个简单的例子是:
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()
内部对GUI进行更改,除非使用SwingUtilities.invokeLater(Runnable)
,因为doInBackground()
是从与EDT不同的线程执行的,并且从后台进行GUI更改线程要求奇怪和莫名其妙的错误。
什么时候应该使用?与其他编程任务不同,GUI中响应时间过长的点要短得多 - 我通常看到写下的数字大约是250ms。如果您的任务花费的时间比这要长,那么它应该在后台线程中。在你的情况下,10秒应该肯定在后台线程中,但是你已经知道:)
编辑:
看到你的评论,我看到我的大多数帖子都没有实际意义。但是,您仍然可以使用SwingWorker:
让您的SwingWorker执行数据检索,并在done()
方法中,让它从数据构建JDialog并将该对话框交给您的第三方库。
答案 1 :(得分:1)
构造没有数据的对话框,然后启动任务以填充它。
从用户体验的角度来看,从启动到完成需要10秒的任何事情都将成为一个问题。最好是你马上就应该 ,即使它不是最终形式。如有必要,您可以弹出一个简单的“加载”模式对话框。