Java GUI冻结是因为insertString方法?

时间:2009-08-27 15:50:59

标签: java user-interface

你有没有听说过因为重复调用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吗?

6 个答案:

答案 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)

您应该考虑在线程中运行重复调用。那应该为你做。