4

问题

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

JDialog初始化

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

第一种方法

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

ADialog dialog = new ADialog();
dialog.setContentPane(content);
dialog.setVisible(true);

这是我们的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 */ }
}

第二种方法

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;
}

再现性

环境

  • 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是由XConfigureEvent触发的XToolkitXToolkit.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 (...) {
                ...
            }
        }
    }
    ...
}

你有什么想法如何修复这个错误/更深入地跟踪它/更健壮地重现它......?

我很欣赏任何想法,因为这个错误是一个真正的痛苦。

4

1 回答 1

6

pack() 应该尊重组件的首选(或最小大小,取决于布局管理器)。

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

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

于 2012-05-31T22:10:21.257 回答