2016-11-26 80 views
0

我正在创建一个程序来抓取图像网站并将这些图像文件名和属性放在列表中,问题是,每当我尝试使用GUI从URL中获取数据时,该程序需要大约20-30秒才能在我的表格模型上显示信息,但是当我在没有GUI的情况下运行它时(只是控制台和简单的系统out println),它只需要2-4秒,在某些时候甚至更快。这里是我的GUI代码:在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

您似乎在Swing事件线程中做了所有繁重的工作,而不是在后台线程(如SwingWorker)中 - 为什么? –

回答

3

SwingWorker类的文档中有你的局面极好的概括:

耗时的任务不应该在事件指派线程上运行。否则,应用程序变得无法响应。

在你的情况下下载一个或多个文件将是那个耗时的任务,并且你正在从Event Dispatch Thread(aka EDT)下载。

的SwingWorker类提供了一个解决问题的方法:

的SwingWorker被设计用于需要在后台线程长期运行的任务运行,并提供更新的用户界面或者完成时,或情况同时处理。


我还认为,动作监听器(或行为)不应该包含的代码长的小段。最好将它包装在方法中,并从动作监听器(action)中调用方法。

而在打开模式对话框的情况下(您正在使用JOptionPane.showMessageDialog)我会invokeLater该方法,以便在打开新对话框之前处理所有待处理的UI消息。这是一般性的建议,将侧重其他用户界面相关的问题(焦点相关的问题浮现在脑海中)。

如果从事件派发线程调用invokeLater - 例如,从JButton的ActionListener调用 - doRun.run()仍将被推迟到所有未决事件已被处理。

2

那么,答案是非常简单明了。当您在GUI模式下运行应用程序时,可视化和图形还有一个额外的线程,以及您拥有的所有组件,如JPanelJButton。这些为程序占用额外的处理,并且在你的网络线程和你的主GUI线程之间有上下文切换。

另一方面,控制台打印不需要繁重的图形处理,因此可以运行得更快。

另外,AFAIK联网是一个阻止的操作,即在网络呼叫完成之前的其他一切必须等待。这就是为什么上下文从GUI线程转移到网络线程并返回的原因。