JEditorPaneHTMLEditorKitParserMethodReadPending读取挂起

wen java案例 1

本文目录导读:

JEditorPaneHTMLEditorKitParserMethodReadPending读取挂起

  1. 网络或 I/O 流阻塞(最常见)
  2. 不完整或格式错误的 HTML
  3. 使用了错误的 Document 类型或 EditorKit
  4. 线程竞争或 Swing 单线程模型违规
  5. 解析器 Bug 或过于复杂/非标准的 CSS/HTML
  6. 调试技巧
  7. 总结排查清单

这是一个非常专业且具体的问题,通常出现在使用 Java Swing 的 JEditorPaneHTMLEditorKit 进行 HTML 解析或渲染时。

“读取挂起”或“read pending”错误通常与 javax.swing.text.html.parser.ParserDocumentParser 的内部状态有关,这个错误的核心原因是 解析器正在等待读取更多的数据来完成当前的结构(如标签、属性或实体),但数据流被中断、提前结束或阻塞了。

以下是导致该问题的常见原因及对应的解决方案:

网络或 I/O 流阻塞(最常见)

JEditorPane 正在加载远程 URL(HTTP/HTTPS)的 HTML 页面时,如果网络连接断开、超时或服务器响应不完整,解析器会卡在 read() 方法上,状态被设置为“等待读取”,但永远不会得到后续数据。

解决方案:

  • 避免直接加载未知 URL:不要使用 pane.setPage("http://...") 来加载不可控的远程页面。

  • 使用自定义的 InputStreamReader:先下载完整的 HTML 内容到本地字符串或字节数组,然后再设置给 JEditorPane

      // 错误方式:直接加载(容易挂起)
      // editorPane.setPage("http://example.com/slow_page.html");
      // 正确方式:先下载完整内容
      String htmlContent = downloadCompleteHtml(url); // 自己实现下载逻辑,增加超时控制
      editorPane.setContentType("text/html");
      editorPane.setText(htmlContent);

不完整或格式错误的 HTML

解析器遇到一个开始标签(如 <div),开始等待 > 或属性,但文本流突然结束(EOF),此时解析器会认为“我正在读取一个标签,还没读完,请继续给我数据”,从而导致状态挂起。

解决方案:

  • 确保 HTML 是完整的:特别是 <table><div><script><style> 等标签必须闭合。

  • 清理 HTML 中的特殊字符:检查是否存在孤立的 <>& 符号。

    • 5 < 10 应写为 5 &lt; 10
    • A&B 应写为 A&amp;B
  • 使用 HTML 清理库:在设置文本前,用 Jsoup 或类似库清理并补全 HTML。

      String dirtyHtml = "..."; // 可能不完整的 HTML
      // 使用 Jsoup 清理和补全
      org.jsoup.nodes.Document doc = Jsoup.parse(dirtyHtml);
      doc.outputSettings().syntax(org.jsoup.nodes.Document.OutputSettings.Syntax.html);
      String cleanHtml = doc.html(); // 现在标签是闭合的,结构是完整的
      editorPane.setText(cleanHtml);

使用了错误的 Document 类型或 EditorKit

HTMLEditorKit 默认与 HTMLDocument 配合,如果你错误地设置了一个 PlainDocument 或自定义的 Document,解析器在试图将解析的内容插入文档时会因类型不匹配而卡住。

解决方案:

  • 确保 EditorKitDocument 匹配
      JEditorPane editorPane = new JEditorPane();
      editorPane.setContentType("text/html");
      // 不要手动 setDocument(new PlainDocument());
      // 让 HTMLEditorKit 自动创建 HTMLDocument

线程竞争或 Swing 单线程模型违规

在后台线程中调用 setText()setPage(),同时主线程或另一个线程正在处理同一个 JEditorPane 的输入事件或重绘,导致解析器的内部状态(如字符缓冲区)被并发修改。

解决方案:

  • 始终在 EDT (Event Dispatch Thread) 上设置 HTML 内容
      SwingUtilities.invokeLater(() -> {
          editorPane.setText(htmlContent);
      });
  • 如果必须进行耗时解析(如大文件),考虑彻底隔离:在一个不可见的 JEditorPane 中完成解析,然后将生成的 HTMLDocument 复制到可见组件。

解析器 Bug 或过于复杂/非标准的 CSS/HTML

Swing 自带的 HTMLEditorKit(基于 javax.swing.text.html)是一个非常老旧且有限的解析器(仅支持 HTML 3.2 及部分 CSS 1.0/2.0),遇到 HTML5、现代 CSS 或 JavaScript 动态内容时,极易触发内部错误,导致解析器进入非法状态。

解决方案:

  • 如果可能,放弃 Swing HTML 渲染,改用 JavaFX WebViewjavafx.scene.web.WebView),它基于 WebKit,支持现代 HTML/CSS/JavaScript。
  • 如果必须用 JEditorPane简化 HTML:移除所有 <script><style><form> 以及复杂的 <table> 嵌套。
  • 考虑使用 Flyingsaucer (XHTML Renderer) 作为替代渲染引擎。

调试技巧

如果你无法定位具体是哪段 HTML 导致的挂起,可以尝试以下步骤:

  1. 二分法定位:每次只设置 HTML 的一半内容,确定触发问题的部分。
  2. 增加超时(Hack 方案):继承 HTMLEditorKit 并重写 createDocument 方法,为 Parser 设置一个有限制的 Reader,但这样做比较复杂。
  3. 捕获异常JEditorPane 通常不会抛出此异常(它发生在内部 EDT 上),尝试在 PropertyChangeListener 或自定义 ErrorListener 中监听。

总结排查清单

原因 检查点 快速解决
I/O 阻塞 正在加载远程 URL? 预下载 HTML 到字符串
HTML 不完整 有未闭合的 <div><table> 用 Jsoup 清理补全
特殊字符 存在裸露的 <& 转义为 &lt; &amp;
线程问题 setText() 是否不在 EDT? 包裹在 invokeLater()
解析器限制 使用了 HTML5 或 CSS3? 改用 JavaFX WebView

最推荐的终极方案

将你的 HTML 先用 Jsoup 解析并输出为标准 HTML 4.01,然后在 EDT 线程上调用 setText(),这能解决 95% 的“读取挂起”问题,如果仍然不行,请考虑迁移到 JavaFX WebView

抱歉,评论功能暂时关闭!