问题
我们的独立 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
触发的XToolkit
。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 (...) {
...
}
}
}
...
}
你有什么想法如何修复这个错误/更深入地跟踪它/更健壮地重现它......?
我很欣赏任何想法,因为这个错误是一个真正的痛苦。