刷新 GUI 时线程异常
我的问题很简单,代码很复杂,而且答案不太容易找到,所以我寻求帮助。
我正在编写一个多线程应用程序,其工作原理如下:
1)一个线程正在读取文件并从文本文件解析信息(每 500 毫秒尝试到达文件末尾)
2)当找到相关信息时,线程存储它并通知作为观察者的控制器
3)当控制器收到通知时,它会通知另一个线程(GUI),该线程更新我想向用户显示的信息。
除非我调用 revalide()
或 validate()
或 repaint()
,否则会出现异常,因为 GUI 更新得太频繁。
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: No such child: 7
at java.awt.Container.getComponent(Container.java:323)
at javax.swing.JComponent.rectangleIsObscured(JComponent.java:4393)
at javax.swing.JComponent.paint(JComponent.java:1052)
at javax.swing.JComponent.paintToOffscreen(JComponent.java:5206)
at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1493)
at javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1424)
at javax.swing.RepaintManager.paint(RepaintManager.java:1217)
at javax.swing.JComponent._paintImmediately(JComponent.java:5154)
at javax.swing.JComponent.paintImmediately(JComponent.java:4964)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:781)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:739)
at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:688)
at javax.swing.RepaintManager.access$700(RepaintManager.java:59)
at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1632)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:660)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:211)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:128)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:117)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:113)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
我考虑过运行另一个线程,每 500 毫秒可能会尝试重新验证 GUI,但可能有更好的替代方案。 有什么我应该担心的事情,比如在调用 revalidate() 之前停止 GUI 线程,然后恢复它或类似的事情吗?
是的,我确实从面板中删除了组件(JLabels),以用新数据替换它们。我会尝试你说的。
顺便问一下,invokelater 和 start 像另一个线程一样有很大的区别吗?
解决方案:
我创建了一个如下所示的方法:
protected void updateGUI(final Param param1, final Param param2){
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
//write your code
param1.validate();
param1.repaint();
}
});
My problem is simple, the code is complex and the answer is not so easy to find, so I'm asking for help.
I'm programming a multi threaded application, here's how it works:
1) One thread is reading a file and parsing information from a textfile (every 500 milliseconds tries to reach the end of the file)
2) When relevant information is found, the thread stores it and notifies the controller which is an observer
3) When controller is notified it notifies the other thread (the GUI) which updates the information I'd like to show to the user.
Except if I call revalide()
or validate()
or repaint()
there's an exception because the GUI is too often updated.
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: No such child: 7
at java.awt.Container.getComponent(Container.java:323)
at javax.swing.JComponent.rectangleIsObscured(JComponent.java:4393)
at javax.swing.JComponent.paint(JComponent.java:1052)
at javax.swing.JComponent.paintToOffscreen(JComponent.java:5206)
at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1493)
at javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1424)
at javax.swing.RepaintManager.paint(RepaintManager.java:1217)
at javax.swing.JComponent._paintImmediately(JComponent.java:5154)
at javax.swing.JComponent.paintImmediately(JComponent.java:4964)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:781)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:739)
at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:688)
at javax.swing.RepaintManager.access$700(RepaintManager.java:59)
at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1632)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:660)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:211)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:128)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:117)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:113)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
I thought about running another thread which every 500 ms might try to revalidate()
the GUI but may be there's a better alternative.
Is there something I should worry about, like stopping the GUI thread before I call revalidate()
and then resume it or something like that ?
Yes indeed I remove components (JLabels) from the panel, to replace them with those with the new data. I'll try what you said.
By the way is there a big difference between the invokelater and start like another thread ?
Solution :
I created a method which looks like this:
protected void updateGUI(final Param param1, final Param param2){
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
//write your code
param1.validate();
param1.repaint();
}
});
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
在我看来,这个异常就像 EDT 之外的 GUI 发生了一些事情,这把一切搞乱了:更具体地说,一个组件似乎被删除了,而 GUI 想要重新绘制它。
尝试将添加/删除组件的代码作为
Runnable
传递给SwingUtilities.invokeLater()
,以确保以任何方式更改 GUI 的所有内容都得到同步。The exception seems to me like something happening to the GUI outside the EDT which messes all up: more specifically, a component seems to be removed while the GUI wants to repaint it.
Try to have the code that adds / removes components as a
Runnable
passed toSwingUtilities.invokeLater()
to make sure everything that changes the GUI in any way is synchronized.