2016-11-26 82 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線程轉移到網絡線程並返回的原因。