你有没有听说过因为重复调用javax.swing.Document.insertString方法而导致的GUI冻结?
有我的代码:
private int insertNotation(GameNode startNode, StyledDocument doc, int pos) {
String s = "";
int startPos = pos;
boolean isContinuous = false;
Style boldStyle, regularStyle, commentStyle, currentNodeStyle, nagStyle, grayStyle;
grayStyle = notationTextPane.getStyle("gray");
GameNode currentNode = history.getCurrentGameNode();
if ((currentNode.isLeaf() && startNode == currentNode.getParent()) || startNode == currentNode) {
try {
if (startNode.getComment().length() > 0) {
s = startNode.getComment() + " ";
commentStyle.addAttribute("gameNode", startNode);
doc.insertString(pos, s, commentStyle);
pos += s.length();
}
for (int n = 0; n < startNode.getChildCount(); n++) {
GameNode node = (GameNode) startNode.getChildAt(n);
boolean isCurrentNode = (node == currentNode);
if (node.isLeaf()) {
if (node.isWhiteMove()) {
s = node.getFullMoveNumber() + ". ";
boldStyle.addAttribute("gameNode", node);
doc.insertString(pos, s, boldStyle);
pos += s.length();
s = node.getMove();
Style style = isCurrentNode ? currentNodeStyle : regularStyle;
style.addAttribute("gameNode", node);
doc.insertString(pos, s, style);
pos += s.length();
isContinuous = true;
} else {
if (isContinuous) {
s = node.getMove();
Style style = isCurrentNode ? currentNodeStyle : regularStyle;
style.addAttribute("gameNode", node);
doc.insertString(pos, s, style);
pos += s.length();
} else {
isContinuous = true;
s = node.getFullMoveNumber() + "... ";
boldStyle.addAttribute("gameNode", node);
doc.insertString(pos, s, boldStyle);
pos += s.length();
s = node.getMove();
Style style = isCurrentNode ? currentNodeStyle : regularStyle;
style.addAttribute("gameNode", node);
doc.insertString(pos, s, style);
pos += s.length();
}
}
doc.insertString(pos++, " ", regularStyle);
}
} catch (BadLocationException e) {
e.printStackTrace();
}
return pos - startPos;
}
我简化了很多,但正如你所看到的,我的'doc'StyledDocument变量中有很多对insertString()方法的调用。 此StyledDocument对象添加在JTabbedPane中。
我已阅读here(在“性能分析”部分中)javax.swing.Document.insertString
方法非常慢(每次调用超过1毫秒)。
可以重复调用它来冻结GUI吗?
答案 0 :(得分:3)
每当您在主GUI线程中执行某些操作时,您将冻结GUI。它根据处理事件重绘。想象一下,您的事件处理代码是在一个while循环中从队列中拉出事件 - 如果您没有从函数返回,则无法处理下一个事件。
考虑在后台线程中进行长时间运行或慢速处理。
请参阅此文章:http://java.sun.com/products/jfc/tsc/articles/threads/threads2.html
答案 1 :(得分:2)
考虑使用后台主题来限制文档的添加。最好使用 SwingWorker
。
首先我们为限制定义队列。插入文本的请求将简单地添加到此队列。这些请求不必在Event Dispatch线程上。
BlockingQueue<String> toAdd = new LinkedBlockingQueue<String>();
toAdd.add("Some text");
toAdd.add("Some more text");
接下来,我们调用SwingWorker
,后台线程不断轮询队列,并将结果以块的形式发布回Event Dispatch线程。
new SwingWorker<Void, String>() {
// Implementation of 'process' and 'doInBackground' methods to go here.
}.execute();
现在我们实现doInBackground
轮询直到输入队列为空,然后一次性发布回Event Dispatch线程以获得更有效的限制。
public String doInBackground() {
while (!Thread.interrupted()) {
List<String> l = new LinkedList<String>();
String s = toAdd.poll();
if (s == null) {
publish(l.toArray(new String[l.size()]));
l.clear();
} else {
l.add(s);
}
}
// Thread interrupted but publish anything pending before returning.
if (!l.isEmpty()) {
publish(l.toArray(new String[l.size()]));
}
return null;
}
最后我们实施process
。在后台线程上调用发布后,在Swing线程上调用此方法。我们使用chunks
加入StringBuilder
,以避免将多次插入放入文档中(使用此方法主要优势)。
public void process(String... chunks) {
StringBuilder sb = new StringBuilder();
for (String chunk : chunks) {
sb.append(chunk);
}
// Insert sb.toString() into buffer HERE
}
答案 2 :(得分:0)
你实际上是在看到冻结(没有前进的进展),还是只是非常严重的放缓?
我们没有您的整个程序代码,因此不清楚,但是当您开始更改文档时,您的文档是否已经显示?每次调用insertString不仅会修改文档,还会创建一系列事件,GUI会对此做出反应。
如果您实际上是在构建文档而您只想显示最终状态,则可能需要在将新文档添加到文本组件窗口小部件之前构建新文档,然后设置就绪文档。它会更闪烁,但速度要快得多,因为在您使用setDocument将文档添加到窗口小部件之前,您并没有真正触发事件和更新。
然后,您可以在不同的线程上构建文档。
答案 3 :(得分:0)
当我使用SwingWorker对象来缓解GUI线程时,问题就消失了:对javax.swing.Document.insertString的调用就在这个SwingWorker中。
我还有另一个已解决的错误:图形重载处理过程太多了。 在这种情况下,我也使用了SwingWorker。
以下是代码:
new SwingWorker<Void, Void>()
{
@Override
protected Void doInBackground() throws Exception
{
visualBoard.update(); // here your method call, deported from the GUI thread.
return null;
}
}.execute();
谢谢大家的快速回答!
答案 4 :(得分:0)
这实际上可能发生。这也不是要做太多;方法只是挂起(CPU = 0%)。
以下是测试示例:http://tinybrain.de/1000816
当错误发生时,您根本看不到框架,控制台上的最后一行是“插入”。
这是一个严重的错误,反对使用JTextPane ......
编辑:我现在也用JTextArea.append复制了冻结! http://tinybrain.de/1000822
似乎围绕始终在AWT线程中创建元素。以前,我只是在main()中创建它们,并在那里使用附加调用SwingUtilities.invokeLater。这似乎是错误的。
如果我在一个“awt {}”块(SwingUtilities.invokeLater的JavaX简写)中创建整个GUI,一切都很好,你可以在这里看到:http://tinybrain.de/1000823。
干杯
答案 5 :(得分:-2)
您应该考虑在线程中运行重复调用。那应该为你做。