小编典典

为什么InvokeLater导致我的JFrame无法正确显示?

java

好的,我已经在网上搜索了所有内容,但还没有找到解决问题的方法,也许我缺少一些简单的东西,所以我在这里…

我有一个相当大的项目,处理维修业务的工单。它是所有与数据库连接的页面,其中包含许多页面的代码和类。但是我只是在前端添加了一小段代码,基本上检查了我们的注释区域中的新消息。

无论如何,我显示一个带有两个 JLabel 的简单 JFrame
,同时一个单独的线程查询数据库。所有这些都在程序开始时发生。问题是在等待期间(这是程序加载的其余部分,而不是数据库线程),我的小“请稍候” JFrame
出现了它的框架,但是没有胆量,没有背景,也没有 JLabel ,它显示了后缀,但是到那时,它已经失去了意义。 __

我编写了以下示例程序。它显示一个简单的 JFrame (CheckingMessagesGUI:一个带有两个 JLabelJFrame
,仅此而已)睡眠5秒钟,然后显示Example(主程序) JFrame
,然后在此示例中立即关闭(),当然我的真实程序继续执行多很多。我发现这似乎是造成问题的原因。一旦睡眠计时器用完,将显示窗口,但是显示该代码的代码是在命令之前给出的,并且应该以正确的顺序执行吗?
System.exit(0)``invokeLater``Thread.sleep

我的问题是为什么invokeLater导致我的 JFrame 无法正确显示?

我的理解invokeLater是,这样做的目的是使项目在正确的AWT事件线程上运行,这使我认为此窗口将正确绘制。无论如何,我确定我缺少明显的东西。我invokeLater在下面的代码中注释掉了该部分,它可以正确运行,如果将其放回原位,则不会…

提前谢谢了。

package javaapplication6;

public class Example extends javax.swing.JFrame {          
    public Example() {
        System.out.println("Example started");
        setBounds(100,100,200,200);

        System.out.println("cmGUI instantiated");
        CheckingMessagesGUI cmGUI = new CheckingMessagesGUI();
        System.out.println("Set cmGUI visible");
        cmGUI.setVisible(true);
        cmGUI.validate();
        try {
            System.out.println("timer started");
            Thread.sleep(5000);
            System.out.println("timer done");
        } catch(InterruptedException e){
        }
        System.exit(0);
    }

    public static void main(String[] args) {
        /*java.awt.EventQueue.invokeLater(new Runnable() {
        @Override
        public void run() { */
        System.out.println("Started");
        System.out.println("example Instantiated");
        Example example = new Example();
        System.out.println("example visible");
        example.setVisible(true);
        /*      }
        });
        */
    }
}

更新:为澄清起见,我意识到Thread.sleep()将阻止所有内容,但是在调用sleep之前我的CheckingMessagesGUI是否应该已经完全绘制好了?这就是问题。


阅读 210

收藏
2020-10-12

共1个答案

小编典典

invokeLater在事件调度线程中运行Runnable,该线程也用于更新GUI。
您的睡眠正在阻塞此线程,因此GUI也不会得到 服务 ,直到您从invokeLater代码返回之前,无法进行任何更新。
这就是为什么您不应该在此线程中进行任何长时间(耗时的)计算的原因。它们应该在其他(新)线程中完成。

事件调度队列状态

事件分发线程上的任务必须快速完成;如果没有,则将备份未处理的事件,并且用户界面将无响应。

您的代码可以更改为(未经测试):

public Example(){
    System.out.println("Example started");
    setBounds(100,100,200,200);

    System.out.println("cmGUI instantiated");
    CheckingMessagesGUI cmGUI = new CheckingMessagesGUI();
    System.out.println("Set cmGUI visible");
    cmGUI.setVisible(true);
    cmGUI.validate();

    Thread thread = new Thread(new Runnable() {
        try {
            System.out.println("timer started");
            Thread.sleep(5000);
            System.out.println("timer done");
        } catch(InterruptedException e) {
        }
        System.exit(0);
    });
    thread.start();
}

编辑:让我们更“深入”(这是我对Swing / AWT工作的看法)。
我想应该在CheckingMessagesGUI类中显示“请稍候”(请参阅​​注释),但不是。
这与GUI的工作方式有关。如果调用相应的(Swing)方法(draw,setText,setLocation等),它不会直接更改显示内容。它只是将事件在事件队列中排队。事件调度线程是(应该是)唯一读取此队列并处理事件的线程。只要它被阻止(在这种情况下为睡眠),就不会显示对GUI的更改。GUI被冻结。

EDIT2:
invokeLater将Runnable 附加
到队列的末尾,以便在处理所有未决事件之后由EDT稍后执行,然后将执行invokeLater调用之后的下一个命令。
invokeAndWait与上面相同,但是实际的线程会阻塞,直到EDT执行Runnable(在挂起事件之后)为止,也就是说,invokeAndWait之后的命令仅在提交的Runnable执行之后才启动。

2020-10-12