模态JDialog的1x1维度

Mic*_*ian 4 java swing

问题

我们的独立Swing应用程序在某些特定事件(按钮单击等)上显示模态JDialog.该对话框包含一些其他Swing组件(JLabel,JButtons,...).而不是通过JDialog.setBounds(...)我们调用的方法JDialog.pack()(或者方法隐式调用它)显式设置其维度.通过方法计算的维数总是不变的(例如300x100像素.)不幸的是,有时可见对话框的实际维度是1x1 px().JOptionPaneshowDialog(...)pack()JDialog.getSize().equals(new Dimension(1, 1))

JDialog初始化

我们JDialog在应用程序中初始化s 有两种方法.我们检查了两个初始化方法总是从EventDispatchThread调用.

第一种方法

我们只创建一个ADialog其子类的实例JDialog.这是我们的初始化过程的片段:

ADialog dialog = new ADialog();
dialog.setContentPane(content);
dialog.setVisible(true);
Run Code Online (Sandbox Code Playgroud)

这是我们的ADialog实施:

public class ADialog extends JDialog implements ComponentListener {

    public JfosDialog(Frame owner) {
        super(owner);
        init();
    }

    private void init() {
        super.addComponentListener(this);
    }

    @Override
    public void componentShown(ComponentEvent e) {
        // Calling pack() at this place is really weird, but we
        // have to do it since some subclasses put their
        // content to dialog in overriden componentShown().
        pack();
    }

    @Override
    public void componentMoved(ComponentEvent e) { /** not interested */ }

    @Override
    public void componentResized(ComponentEvent e) { /** not interested */ }
}
Run Code Online (Sandbox Code Playgroud)

第二种方法

public JOptionPane showDialog(...) {
    JOptionPane jop = new JOptionPane(message, msgType, option, null, textMessages);
    JDialog dialog = jop.createDialog(owner, titleMsg);
    setDialogTraversal(dialog);
    dialog.setVisible(true);
    dialog.dispose();
    return jop;
}
Run Code Online (Sandbox Code Playgroud)

再生性

环境

  • Ubuntu 12.04.,JRE 1.6/JRE 1.7/OpenJDK 1.6
  • Windows XP,JRE 1.6.u16

使用第一个初始化方法重现问题

我们只显示然后隐藏对话N次(0 <N <1000)并且一次对话的维度是1x1(线程问题的标志,竞争条件).由于这个问题的本质是非常随机的,我们编写了简单的java.awt.Robot脚本,它在循环中显示和隐藏对话框.它比手动操作更舒服.

使用第二个初始化方法重现问题

步骤与第一种方法相同:只显示然后隐藏对话框N次.不幸的是,我们无法在我们的开发环境中重现它,但它可以很容易地在生产PC中重现(与我们的开发环境中有不同的CPU,安装了一些永久创建系统负载的防病毒软件等)

到目前为止,我们无法在某种测试/样本项目中重现该问题.这可能表明我们的应用程序存在问题.但是,似乎问题可能出现在Swing的本机代码中(请参阅跟踪部分).

追踪

我们在64位OpenJDK 1.6.0_24中追踪了我们问题的原因.我们发现,维度JDialog被修改XConfigureEventXToolkit.事件是在XToolkit.run(boolean)从本机方法调用返回之后的方法的事件循环中构造的.

为简化起见,我在这里只发布了一个代码片段,说明XToolkit了我们跟踪结果的事件循环机制.您可能还会在此处看到完整的源代码.

public class XToolkit ... {
    ...
    public void run(boolean loop) {
        XEvent ev = new XEvent();
        while(true) {
            awtLock();
            try {
                if (loop == SECONDARY_LOOP) {
                    ...
                } else {
                    ...

                    // ===========================================
                    // The following invocation of native method sometimes
                    // updates ev object in a way that ev.get_type() method returns value 22
                    // indicating that the event's type is XConfigureEvent.
                    // In such case, as I mentioned in text above, the value of
                    // ev.get_xconfigure().get_width() / .get_height()
                    // is sometimes 1.

                    XlibWrapper.XNextEvent(getDisplay(),ev.pData); // <-----

                    // ===========================================
                }
                ...
                // The XConfigureEvent with get_width() == 1 and
                // get_heigth() == 1 is dispatcher here:
                dispatchEvent(ev); // <-----
                ...
            } catch (...) {
                ...
            }
        }
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

你有任何想法如何解决这个错误/更深入地追踪它/更健壮地再现它......?

我很欣赏任何想法,因为这个bug真的很痛苦.

a_h*_*ame 6

pack()应该尊重组件的首选(或最小尺寸,具体取决于layou tmanager).

尝试明确设置对话框中组件的最小和首选大小,看看是否能解决问题.

就竞争条件而言,它也可能是代码中的错误初始化.当布局管理器查询其首选(或最小大小)时,大多数组件将遵循其内容(例如,JLabel的文本).因此,填充组件的代码可能并不总是以相同的顺序运行.您是否确保在AWT线程上创建对话框和所有包含的组件?