如何在 EDT 中等待对象创建而不阻塞 EDT?

发布于 2024-08-16 04:04:44 字数 256 浏览 10 评论 0原文

我正在使用 Java 库中的 API,该 API 从事件分派线程调用​​,并要求我返回完全初始化的 UI 组件。它看起来像这样:

public JDialog createDialog();

但我只能在从数据库加载后填充对话框,有时可能需要 10 秒。通常我会在后台线程中执行此操作,但由于此方法是从 EDT 调用的,并且我必须返回对话框,因此这是行不通的。它是第三方库,所以我无法更改方法,但是我可以做些什么来避免阻止 EDT?

I am using an API in a Java library which is called from the event dispatch thread and requires me to return a fully-initialized UI component. It looks like this:

public JDialog createDialog();

But I can only populate the dialog after loading from a database, which can take 10 seconds sometimes. Normally I would do that in a background thread but since this method is called from the EDT and since I have to return the dialog, that won't work. It's a third-party library so I can't change the method, but is there anything I can do to avoid blocking the EDT?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

妖妓 2024-08-23 04:04:44

“初始化”不一定与“填充”相同。 “初始化”通常意味着对象已经完全构造完毕,但可能没有任何数据。 “填充”当然意味着数据存在并且任何数据获取任务都已完成。因此,可以为您的第三方库提供一个完全初始化的 JDialog,而无需任何数据。

我总是喜欢解决这个问题的方法是创建一个自定义 JDialog,它显示繁忙消息或进度条等,然后在另一个线程中请求数据。当数据返回时,我用数据替换繁忙消息(在 EDT 上!)。至于如何在后台线程中执行请求,我建议使用 SwingWorkers。我喜欢在自定义 JDialog 中使用私有 SwingWorker ,它处理 doInBackground() 方法中的请求,并处理 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() 内部更改 GUI,除非您使用 SwingUtilities.invokeLater(Runnable),如 doInBackground() code> 是从与 EDT 不同的线程执行的,并且从后台线程进行 GUI 更改会导致奇怪且无法解释的错误。

什么时候应该使用这个?与其他编程任务不同,GUI 中响应时间过长的时间点要短得多——我通常看到的写下的数字约为 250 毫秒。如果您的任务花费的时间比这长,它应该在后台线程中。在你的情况下,10 秒肯定应该在后台线程中,但你已经知道了:)

编辑:

看到你的评论,我发现我的大部分帖子都没有实际意义。但是,您仍然可以使用 SwingWorker:

让 SwingWorker 执行数据检索,并在 done() 方法中,让它根据数据构造 JDialog 并将该对话框交给第三方图书馆。

"Initialized" is not necessarily the same thing as "Populated". "Initialized" usually means that the object has been fully constructed, but may not have any data. "Populated" of course means that the data is present and any data-fetching tasks are complete. So it is possible to give your third-party library a fully initialized JDialog without any data at all.

The way I always like to solve this problem is to create a custom JDialog which displays a busy message or a progress bar or the like, and then request the data in another thread. When the data is returned, I replace the busy message with the data(On the EDT!). As to how you should perform your request in a background thread, I recommend using SwingWorkers. I like to use a private SwingWorker inside my custom JDialog which handles the request in the doInBackground() method, and handles the Display-related tasks in the done() method. Doing it this way will ensure that display-related tasks only occur on the EDT, and database-related tasks only occur OFF the EDT. If you'd like a reasonably good introduction to using SwingWorkers, check out Sun's tutorial on worker threads. A simple example would be:

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 
           }

        }
     }
}

A few things to remember, though: the done() method is NOT abstract, so you aren't required to override it. You should, though. If your doInBackground() implementation throws an exception, that exception will be swallowed unless done() has been overridden. Also, don't make changes to your GUI from inside the doInBackground(), unless you use SwingUtilities.invokeLater(Runnable), as doInBackground() is executed from a different thread than the EDT and making GUI changes from a background thread is asking for strange and inexplicable bugs.

When should this be used? Unlike other programming tasks, the point at which something takes too long to respond is a lot shorter in GUIs--The number I've usually seen written down is about 250ms. If your task takes longer than that, it should be in a background thread. In your case, 10 seconds should definitely be in a background thread, but then you already knew that :)

EDIT:

Seeing your comment, I see that most of my post is pretty moot. However, you can still use a SwingWorker:

Have your SwingWorker perform the data-retrieval, and in the done() method, have it construct the JDialog from the data and hand that dialog to your third-party library.

ˉ厌 2024-08-23 04:04:44

构造没有数据的对话框,然后启动任务来填充它。

从用户体验的角度来看,任何从启动到完成需要 10 秒的事情都会成为一个问题。最好立即某事,即使它不是最终形式。如有必要,您可以弹出一个模式对话框,只显示“正在加载”。

Construct the dialog without data, and then start a task to populate it.

From a user-experience perspective, anything that takes 10 seconds from initiation to completion is going to be an issue. It's best if you should them something right away, even if it's not in final form. If necessary, you could pop a modal dialog that simply says "Loading."

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文