在后台发生的进程会触发回调以询问各种问题。
在这种情况下,问题是“是否可以迁移数据?”,所以我必须询问用户。因为我们必须在EDT上完成所有Swing工作,所以最终看起来像这样(我只删除了注释,引用我们自己的便捷方法和allowMigration()
的参数 - 除此之外,其他一切都是相同的) :
public class UserMigrationAcceptor implements MigrationAcceptor {
private final Window ownerWindow;
public UserMigrationAcceptor(Window ownerWindow) {
this.ownerWindow = ownerWindow;
}
// called on background worker thread
@Override
public boolean allowMigration() {
final AtomicBoolean result = new AtomicBoolean();
try {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
result.set(askUser());
}
});
} catch (InterruptedException e) {
Thread.currentThread.interrupt();
return false;
} catch (InvocationTargetException e) {
throw Throwables.propagate(e.getCause());
}
return result.get();
}
// called on EDT
private boolean askUser() {
int answer = JOptionPane.showConfirmDialog(ownerWindow, "...", "...",
JOptionPane.OK_CANCEL_OPTION);
return answer == JOptionPane.OK_OPTION;
}
}
在某些情况下,在确认或取消出现的对话框后,Swing似乎进入以下状态:
JOptionPane
不再可见#invokeAndWait
内,等待InvocationEvent#isDispatched()
返回true
。我们在这里做错了什么,还是我在看Swing / AWT中的错误?
唯一可能值得注意的是,这是模态对话的第二级。有一个模态对话框显示操作的进度,然后此确认对话框将进度对话框作为其父对象。
更新1:以下是EDT当前被阻止的位置:
java.lang.Thread.State: WAITING
at sun.misc.Unsafe.park(Unsafe.java:-1)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
at java.awt.EventQueue.getNextEvent(EventQueue.java:543)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:211)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:154)
at java.awt.WaitDispatchSupport$2.run(WaitDispatchSupport.java:182)
at java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:221)
at java.security.AccessController.doPrivileged(AccessController.java:-1)
at java.awt.WaitDispatchSupport.enter(WaitDispatchSupport.java:219)
at java.awt.Dialog.show(Dialog.java:1082)
at java.awt.Component.show(Component.java:1651)
at java.awt.Component.setVisible(Component.java:1603)
at java.awt.Window.setVisible(Window.java:1014)
at java.awt.Dialog.setVisible(Dialog.java:1005)
at com.acme.swing.progress.JProgressDialog$StateChangeListener$1.run(JProgressDialog.java:200)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:733)
at java.awt.EventQueue.access$200(EventQueue.java:103)
at java.awt.EventQueue$3.run(EventQueue.java:694)
at java.awt.EventQueue$3.run(EventQueue.java:692)
at java.security.AccessController.doPrivileged(AccessController.java:-1)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:703)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:154)
at java.awt.WaitDispatchSupport$2.run(WaitDispatchSupport.java:182)
at java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:221)
at java.security.AccessController.doPrivileged(AccessController.java:-1)
at java.awt.WaitDispatchSupport.enter(WaitDispatchSupport.java:219)
at java.awt.Dialog.show(Dialog.java:1082)
at javax.swing.JOptionPane.showOptionDialog(JOptionPane.java:870)
这里奇怪的是底部的showOptionDialog()
是迁移提示,但Dialog#setVisible
进一步向上是进度对话框。换句话说,有时孩子对话框会出现在父对象之前,也许这就是破坏Swing的原因。
更新2:
事实上,我可以在不使用任何自己的代码的情况下在测试程序中实现这一点。虽然测试程序中的对话框定位不同,但它以完全相同的方式挂起,只能更加可重复。 gist
答案 0 :(得分:2)
我在自己的代码中遇到了同样的问题。
你对JOptionPane.showOptionDialog()
的调用永远不会返回,因为当它的事件调度循环坐在那里等待用户输入时,一个计时器(或其他东西)已经触发并导致另一个模态对话框安装它自己的事件循环。在您的堆栈中,罪魁祸首是JProgressDialog$StateChangeListener$1.run()
,您可以看到它启动自己的事件调度循环。
在JProgressDialog关闭之前,它不会退出循环,之前对JOptionPane.showOptionDialog()的调用将永远不会返回。
如果父对话框似乎暗示了事件队列不支持的层次结构,那么这可能并不明显。
两种解决方案可能是避免模态进度对话框,或立即显示进度对话框。如果事件线程的其余部分很高兴坐下来等待它关闭,那么从事件中启动模态对话框只是个好主意。
答案 1 :(得分:1)
invokeAndWait
必须在EDT之外调用,
小心地使用invokeAndWait
,因为可以冻结整个Swing GUI,被RepaintManager
的异常锁定,而不是在所有情况下只创建GUI,重新启动,刷新一些方法,当{{ 1}}从嵌套方法调用
需要repaint()
来测试invokeAndWait
/ if (EventQueue.isDispatchThread()) {
从if (SwingUtilities.isEventDispatchThread()) {
你可以isDispatchThread()
得到真,没有任何副作用,输出是在EDT上完成的,但是关于包裹在result.set(askUser());
内的好实践
我看到invokeLater
的一些用法但仅在应用程序start_up上使用invokeAndWait
代替
答案 2 :(得分:0)
有了这种混乱,首先要怀疑的是,某处,正在从EventQueue(EDT)调用Swing方法。 Swing通常可以很好地处理多个线程,但是每隔一段时间就会发生这种情况。不幸的是,我知道没有其他方法来解决问题,而不是检查每个swing方法调用并在EDT上确定它。 (注意,可以在其他线程上运行一个或两个Swing方法,例如repaint
,但请检查源代码和Javadoc以确保每个线程。)