我正在创建一个程序来抓取图像网站并将这些图像文件名和属性放在列表中,问题是,每当我尝试使用GUI从URL获取数据时,程序大约需要20- 30秒显示我的桌面模型上的信息,但是当我在没有GUI的情况下运行它(只需控制台和简单的系统输出println)时,它只需2-4秒,在某些时候甚至更快。这是我的GUI代码:
public class ImageDownloader extends JFrame {
private JPanel contentPane;
private JTextField urlTextField;
private JButton btnCheck;
private JButton btnDownload;
private JButton btnDownloadAll;
private JTable table;
private String imgUrl;
private String url;
Document document;
Elements media;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ImageDownloader frame = new ImageDownloader();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public ImageDownloader() {
setTitle("Image Downloader");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 565, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
JPanel panel = new JPanel();
FlowLayout flowLayout = (FlowLayout) panel.getLayout();
flowLayout.setAlignment(FlowLayout.LEFT);
contentPane.add(panel, BorderLayout.NORTH);
JLabel lblWebsiteUrl = new JLabel("Website URL:");
panel.add(lblWebsiteUrl);
urlTextField = new JTextField();
panel.add(urlTextField);
urlTextField.setColumns(30);
btnCheck = new JButton("Check");
btnCheck.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
List<Images> images = new ArrayList<>();
url = urlTextField.getText();
if(url.isEmpty()) {
JOptionPane.showMessageDialog(ImageDownloader.this, "Please enter a website URL", "Input Error", JOptionPane.ERROR_MESSAGE);
} else {
try {
document = Jsoup.connect(urlTextField.getText()).userAgent("Mozilla").timeout(10 * 1000).get();
media = document.select("[src]");
for(Element src : media) {
if(src.tagName().equals("img")) {
imgUrl = src.attr("abs:src");
URL url = new URL(imgUrl);
long size = url.openConnection().getContentLengthLong();
images.add(new Images(src.tagName(), src.attr("abs:src"), src.attr("width"), src.attr("height"), size));
}
}
ImageDownloaderTableModel tableModel = new ImageDownloaderTableModel(images);
table.setModel(tableModel);
} catch (IOException e1) {
JOptionPane.showMessageDialog(ImageDownloader.this, "Error loading website, The site that you are trying to reach is either down or does not exist..", "Error Loading", JOptionPane.ERROR_MESSAGE);
e1.printStackTrace();
}
}
}
});
panel.add(btnCheck);
JPanel panel_1 = new JPanel();
contentPane.add(panel_1, BorderLayout.SOUTH);
btnDownloadAll = new JButton("Download All");
btnDownloadAll.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
try {
media = document.select("img");
for(Element src : media) {
String strImgUrl = src.attr("abs:src");
downloadImage(strImgUrl);
}
} catch(Exception ex) {
ex.printStackTrace();
}
}
});
panel_1.add(btnDownloadAll);
btnDownload = new JButton("Download");
panel_1.add(btnDownload);
JScrollPane scrollPane = new JScrollPane();
contentPane.add(scrollPane, BorderLayout.CENTER);
table = new JTable();
scrollPane.setViewportView(table);
}
public static void downloadImage(String imgUrl) {
String strImgUrl = imgUrl.substring(imgUrl.lastIndexOf("/") + 1);
try {
URL urlImage = new URL(imgUrl);
InputStream in = urlImage.openStream();
byte[] buffer = new byte[4096];
int n = -1;
OutputStream os = new FileOutputStream(strImgUrl);
while((n = in.read(buffer)) != -1) {
os.write(buffer, 0, n);
}
os.close();
System.out.println("Saved..");
} catch(IOException ex) {
ex.printStackTrace();
}
}
}
答案 0 :(得分:3)
SwingWorker课程的文档可以很好地回顾您的情况:
不应在Event Dispatch Thread上运行耗时的任务。否则应用程序将无响应。
在您的情况下,下载一个或多个文件将是一项耗时的任务,您正在从Event Dispatch Thread(又称EDT)下载。
SwingWorker类为您的问题提供了解决方案:
SwingWorker适用于需要在后台线程中运行长时间运行任务并在完成或处理时为UI提供更新的情况。
我还会争辩说,动作听众(或动作)不应该包含很长的代码。最好将其包装在方法中,并从动作侦听器(动作)调用该方法。
如果您要打开模态对话框(您使用JOptionPane.showMessageDialog
执行此操作),我会invokeLater
该方法,以便在您打开新内容之前处理所有待处理的UI消息对话。这是一般建议,可以解决其他与UI相关的问题(与焦点相关的问题)。
如果从事件调度线程调用invokeLater - 例如,从JButton的ActionListener调用 - doRun.run()仍将延迟,直到所有待处理事件都被处理完毕。
答案 1 :(得分:2)
嗯,答案非常简单明了。当您在GUI模式下运行应用程序时,有一个额外的可视化和图形线程以及您拥有的所有组件JPanel
或JButton
。这些会占用程序的额外处理,并且在您的网络线程和主GUI线程之间存在上下文切换。
另一方面,控制台打印不需要繁重的图形处理,因此可以更快地运行。
此外,AFAIK,网络是阻止操作,即其他所有内容必须在网络呼叫完成之前等待。这就是上下文从GUI线程转移到网络线程再返回的原因。