JEditorPaneHTMLEditorKitParserMethodPending连接挂起

wen java案例 3

本文目录导读:

JEditorPaneHTMLEditorKitParserMethodPending连接挂起

  1. 核心原因
  2. 解决方案(按推荐顺序)

你提到的 JEditorPaneHTMLEditorKit 在解析 HTML 时出现的“连接挂起”或“Pending”问题,通常是由于默认的 HTMLEditorKit 在尝试加载外部资源(如图片、CSS、脚本或 iframe 中的内容)时,网络连接被阻塞或超时导致的。

这种情况在 Swing 的 JEditorPane 中非常常见,尤其是在加载包含复杂 HTML(如现代网页、或引用外部网络资源的 HTML)时。

以下是导致该问题的核心原因及解决方案:

核心原因

  1. 默认的 HTMLEditorKit 是阻塞的:Swing 的 HTML 渲染引擎(javax.swing.text.html.HTMLEditorKit)是基于 Java 1.x 时代的旧版解析器,当它遇到 <img src="http://..."><link href="..."> 时,默认会尝试同步(阻塞)地下载该资源,如果该网络地址不可达、DNS 解析缓慢、或者需要代理且未配置,线程就会永远挂起在 URLConnection.connect() 上。
  2. 不支持现代 HTML/CSS/JavaScriptJEditorPane 不是真正的浏览器,如果你加载的 HTML 包含了复杂的 CSS 布局或 JavaScript(即使被忽略),解析器也可能在解析 DOM 树时进入死循环或非常缓慢的状态。
  3. Swing 事件调度线程(EDT)阻塞:如果你在 EDT 上直接调用 setPage()setText() 加载一个需要网络请求的 HTML,EDT 会被阻塞,导致整个 UI 冻结和挂起。

解决方案(按推荐顺序)

禁用外部资源加载(最有效、最安全)

如果你不需要加载网络图片或外部样式表,直接禁用 HTMLEditorKit 的自动资源加载,这可以立即解决 90% 的挂起问题。

import javax.swing.*;
import javax.swing.text.html.*;
import java.io.IOException;
public class SafeHtmlPane {
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        JEditorPane editorPane = new JEditorPane();
        editorPane.setContentType("text/html");
        // 使用自定义的 HTMLEditorKit 并重写 createInputStream 方法
        editorPane.setEditorKit(new HTMLEditorKit() {
            @Override
            public Document createDefaultDocument() {
                HTMLDocument doc = (HTMLDocument) super.createDefaultDocument();
                // 关键:禁用在解析时自动加载外部图片
                doc.setAsynchronousLoadPriority(-1);
                // 或者设为更高的正数,但 -1 表示完全禁止异步加载
                return doc;
            }
        });
        // 加载本地或内联 HTML(不包含外部网络资源)
        editorPane.setText("<html><body><h1>Hello</h1><img src='file:///C:/local/image.png'></body></html>"); // 仅限本地文件
        // 或者加载时需要防止网络请求:
        // editorPane.setPage(new URL("http://example.com")); // 建议放到后台线程
        frame.add(new JScrollPane(editorPane));
        frame.setSize(400, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

重点说明

  • doc.setAsynchronousLoadPriority(-1):这个设置是阻止挂起的关键,它告诉解析器不要尝试异步下载任何外部资源,默认情况下,这个值可能是 0100,导致解析器尝试建立连接。

使用 SwingWorker 在后台加载页面

永远不要在主线程或 EDT 上直接调用 setPage(url)

SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
    @Override
    protected Void doInBackground() throws Exception {
        // 在后台线程获取 HTML 内容(手动获取字符串)
        StringBuilder content = new StringBuilder();
        try (InputStream in = new URL("http://some-slow-site.com").openStream();
             BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
            String line;
            while ((line = reader.readLine()) != null) {
                content.append(line).append("\n");
            }
        }
        // 将结果传递给 EDT
        SwingUtilities.invokeLater(() -> {
            editorPane.setText(content.toString()); // 使用 setText,不会触发网络连接
            // 注意:setText 仍然会尝试解析 HTML,但它不会去下载外部资源(如果内容已包含在内)
        });
        return null;
    }
};
worker.execute();

优点:即使外部网络很慢,UI 也不会卡死,只有当你手动调用了 setPage() 时,才会触发内部网络请求,使用 setText() 则不会触发。

自定义 HTMLEditorKit 阻断网络连接

如果你无法控制 setAsynchronousLoadPriority 的行为,或者需要在解析时彻底屏蔽所有网络请求,可以重写 createInputStream 方法:

editorPane.setEditorKit(new HTMLEditorKit() {
    @Override
    public InputStream createInputStream(HTMLDocument doc, URL url) throws IOException {
        // 完全禁止外部资源加载
        // 抛出一个异常,或者返回一个空流
        if (url.getProtocol().startsWith("http")) {
            // 返回一个空的 InputStream 来替代挂起
            return new java.io.ByteArrayInputStream(new byte[0]);
        }
        return super.createInputStream(doc, url);
    }
});

考虑替代方案(如果必须渲染现代网页)

JEditorPane + HTMLEditorKit 对现代网页的支持非常有限,如果挂起问题持续出现且你无法控制 HTML 内容,建议使用 JavaFX WebViewJxBrowser(商业库):

  • JavaFX WebView(推荐):
    // 在 Swing 应用中嵌入 JavaFX
    JFXPanel jfxPanel = new JFXPanel();
    Platform.runLater(() -> {
        WebView view = new WebView();
        WebEngine engine = view.getEngine();
        engine.load("http://example.com");
        jfxPanel.setScene(new Scene(view));
    });

    WebView 使用 Chromium 内核,没有 JSON 解析挂起问题,且支持现代 Web 标准。

原因 解决方案
解析器尝试下载外部资源(图片/CSS)导致网络阻塞 设置 document.setAsynchronousLoadPriority(-1)
在 EDT 上直接调用 setPage(url) 使用 SwingWorker 在后台线程中获取 HTML 内容,然后调用 setText()
HTML 包含复杂 JS 或格式 清理 HTML,或改用 JavaFX WebView
代理或 DNS 问题 配置 Java 网络代理:System.setProperty("http.proxyHost", "...")

第一步尝试: 先检查你的代码中是否有 editorPane.setPage(new URL(...)),如果有,换成 editorPane.setText(htmlString) 并启用 setAsynchronousLoadPriority(-1),绝大多数情况下,这足以解决“连接挂起”问题。

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