为什么在EDT上调用此方法会导致编译错误?

时间:2014-11-27 06:35:01

标签: java multithreading user-interface event-dispatch-thread

我正在尝试弹出自定义对话框。当我尝试在EDT上调用该方法时,我得到以下错误:

Exception in thread "AWT-EventQueue-0" java.lang.Error: Unresolved compilation problem: 

at danind.com.gmail_coem.ui.CredentialEditor.promptPossibleDialog(CredentialEditor.java:29)
at danind.com.gmail_coem.ui.HomeScreen$ConfigureDatabase.<init>(HomeScreen.java:281)
at danind.com.gmail_coem.ui.HomeScreen.configureDatabase(HomeScreen.java:230)
at danind.com.gmail_coem.ui.HomeScreen.lambda$1(HomeScreen.java:105)
at danind.com.gmail_coem.ui.HomeScreen$$Lambda$7/2092062410.actionPerformed(Unknown Source)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.AWTEventMulticaster.mouseReleased(Unknown Source)
at java.awt.AWTEventMulticaster.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$400(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)

在Eclipse中清理我的项目并进行一些隔离测试后,我发现在EDT上调用该方法是造成问题的原因。当我将方法移动到后台线程时,它工作,但我不想要,因为我想在EDT上创建我的对话框GUI。

//Creates compilation error
private class ConfigureDatabase extends SwingWorker<Void, String[]>
{
    private CredentialEditor instance;
    public ConfigureDatabase()
    { //Runs on EDT
        this.instance = CredentialEditor.promptPossibleDialog(true);
    }

    @Override
    protected Void doInBackground()
    { //Runs in background thread
        try(Database database = CredentialEditor.getCredentials(instance))
        {
            //code
        }
    }
}

VS

//Runs just fine, but dialog GUI is not on EDT
private class ConfigureDatabase extends SwingWorker<Void, String[]>
{
    @Override
    protected Void doInBackground()
    { //Runs in background thread
        try(Database database = CredentialEditor.getCredentials(CredentialEditor.promptPossibleDialog(true)))
        {
            //code
        }
    }
}

有问题的方法:

public static CredentialEditor promptPossibleDialog(boolean reset)
{
    if(reset || ConnectionPool.getInstance() == null)
    { //Checks to see if a dialog box needs to be created.
        if(SwingUtilities.isEventDispatchThread())
        { //Checks to make sure the thread is on the EDT.
            return new CredentialEditor();
        }
        else
        { //If it's not on the EDT throw an exception warning.
            throw new IllegalStateException("Must run on EDT!");
        }
    }
    return null; //If no dialog box needs to be created return nothing.
}

要更详细地了解问题,它似乎只是调用该方法会导致问题。它没有在方法中设置实例变量或任何东西,它只是在EDT中专门调用静态方法。事实上,堆栈跟踪指向它只是简单说明方法的行,就像它所说的行 public static CredentialEditor promptPossibleDialog(boolean reset)

那么导致错误的原因是什么,如果我无法绕过它,我怎样才能在EDT上运行我的GUI代码,即使在后台线程上调用它的方法也是如此?

2 个答案:

答案 0 :(得分:0)

尝试直接在EDT上运行对话框。

public ConfigureDatabase()
{
    //some code
    this.instance = CredentialEditor.promptPossibleDialog(true); //This is line 281
}

这意味着您正在工作线程中运行对话框,这不是一个好主意。对于非交互式后台任务,工作者线程主要是afaik。如果你必须在工作线程中运行一个对话框,你必须在EDT中单独启动它,如:

public ConfigureDatabase()
{
    SwingUtilities.invokeLater(new Runnable() { //or if you must wait for its end, use invokeAndWait

        public void run() {
                CredentialEditor.promptPossibleDialog(true); //This is line 281
        }
    });
}

这应该有效。如果提供完整的SSCE,首先如何执行你的工作人员会更有帮助。

此外,对话框用于与用户交互并返回结果。因此,在实例中保存对话框不是最好的事情。而是存储其结果或重新考虑您的设计。

请参阅here,例如:

  

注意:调用事件调度线程上的get阻止所有事件,   包括重绘,从处理到SwingWorker   完整。

     

当您希望SwingWorker阻止事件调度线程时我们   建议您使用模态对话框。

     

例如:

class SwingWorkerCompletionWaiter extends PropertyChangeListener {
     private JDialog dialog;

     public SwingWorkerCompletionWaiter(JDialog dialog) {
         this.dialog = dialog;
     }

     public void propertyChange(PropertyChangeEvent event) {
         if ("state".equals(event.getPropertyName())
                 && SwingWorker.StateValue.DONE == event.getNewValue()) {
             dialog.setVisible(false);
             dialog.dispose();
         }
     }
 }

运行方式:

     JDialog dialog = new JDialog(owner, true);
     swingWorker.addPropertyChangeListener(
         new SwingWorkerCompletionWaiter(dialog));
     swingWorker.execute();
     //the dialog will be visible until the SwingWorker is done
     dialog.setVisible(true);
  

指定者:接口Future中的get返回:计算结果   抛出:InterruptedException - 如果当前线程被中断   等待ExecutionException - 如果计算投掷了   例外

答案 1 :(得分:0)

你正在绊倒允许运行代码的Eclipse功能,即使它有编译错误,这比任何好的,imho更有害。您可以考虑将其关闭。但是,您必须了解的是,查看堆栈跟踪的行号没有用,因为它不一定会告诉您编译错误的行号,而是在运行时生成异常的行号

运行时不会尝试编译代码。相反,编译器生成的代码会在执行到达Eclipse无法编译的代码片段时无条件地抛出该异常。因此,它与您的代码运行的线程没有任何关系。由于虚假的编译器错误出现在CredentialEditor内,令人难以置信的是,在调用者进行的修改是否会改变它是否正确编译。但是,由于代码在后台线程中调用时抛出IllegalStateException,但是你说它在第二个场景中有效,似乎 更改你没有告诉我们。

另一方面,如果由bug引起,则行为不必看起来合乎逻辑。通常,异常包含编译器错误,但堆栈跟踪中的空行与IDE没有告诉您该错误的观察到的行为完全匹配。所以你遇到了一个错误,在这个错误中,如果假定编译器错误不存在,或者存在缺少消息的编译器错误。

考虑到您未提及的内容,即使用Java 8甚至主动使用新功能,遇到编译器错误并不令人惊讶。 Eclipse的Java 8支持是......嗯......有很大的改进空间。只需检查您是否真的使用最新的Eclipse版本。如果您正在使用或者在更新后问题仍然存在,我建议您创建一个重现问题的最小示例,并且不依赖于任何其他(第三方)类,并向Eclipse团队提交错误报告。