2015-10-27 48 views
1

我使用Java,Swing(窗口生成器)在Windows上開發了一個應用程序。
單擊按鈕,我的應用程序將轉到另一個類(FileManager.java文件)以統計輸入文件夾中的文件總數(同時progressBar將處於不確定模式)。一旦文件的數量已知,則設置最大值progressBarJava Swing Worker for Progress Bar - 用戶界面長時間保持無響應狀態

然後我打電話convertToXLS(fileMgr)來讀取每個文件的內容(1 kb),並在讀取每個文件時更新progressBar

這裏是它的代碼:

public class xmlToXL { 
      public static void main(String[] args) { 
     javax.swing.SwingUtilities.invokeLater(new Runnable() { 
      public void run() { 
       xmlToXL window = new xmlToXL(); 
       window.frame.setVisible(true); 
      } 
     }); 
     private void initialize() { 
      ...... some UI code ........ 
     btnConvertXmlTo.addActionListener(new ActionListener() { 
      public void actionPerformed(ActionEvent e) {  
       try { 
        preConvertToXLS(); 
        Task task = new Task(folderPath.getText()); 
        task.execute(); 
       } catch (Exception e1) { 
        e1.printStackTrace(); 
       } 
      }// end of actionPerformed method 
     }); // end of action listened 

}//end of initialize 

    public void preConvertToXLS() { //method to set few UI properties 
     btnConvertXmlTo.setEnabled(false); 
     progressBar.setVisible(true); 
     progressBar.setStringPainted(true); 
     progressBar.setIndeterminate(true); 
     progressBar.setString("Calculating Total number of files..."); 
     progressBar.setForeground(new Color(0, 102, 0)); 
    } 

    ParserUtils parUtils = new ParserUtils(); //class to parse XML files (in another .java file) 

    private void convertToXLS(FileManager fileMgr) { 
     try { 
      int i=1; 
      parUtils.reset(); 
      progressBar.setValue(0); 
      List<File> files = fileMgr.getFiles(); 
      for(File file : files) { 
       progressBar.setString("Reading " + i+ " of " + fileMgr.getSize()+ " files"); 
       parUtils.parseFileUsingDOM(file); // This will read content of the input file 
       progressBar.setValue(i++); 
      } 
      btnConvertXmlTo.setEnabled(true); 


     } catch (Exception e) { 

     } 
    } 

    class Task extends SwingWorker<Void, Void> { 
     private FileManager fileMgr; 

     public Task(String srcPath) { 
      this.fileMgr = new FileManager(new File(srcPath)); 

     } 

     /* 
     * Main task. Executed in background thread. 
     */ 
     @Override 
     public Void doInBackground() { 
      try { 
       progressBar.setIndeterminate(true); 
       fileMgr.readFiles(); 
       progressBar.setIndeterminate(false); 
       progressBar.setMaximum(fileMgr.getSize()); 
       convertToXLS(fileMgr); 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } 
      return null; 
     } 

     /* 
     * Executed in event dispatching thread 
     */ 
     @Override 
     public void done() { 
      Toolkit.getDefaultToolkit().beep(); 
      try { 
      progressBar.setString("FileRead Successful"); 
      } catch (Exception e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 
    }//end of task class 
}//end of My class 

我的UI變得fileMgr.readFiles();後反應遲鈍。它需要一兩分鐘,有時需要三分鐘,然後執行convertToXLS(fileMgr)

FileManager.java

import XMLParsing.DetermineEncoding; 

public class FileManager { 

    public HashMap<String, ArrayList<String>> dirFiles = null; 
    public ArrayList<String> dirNames = null; 
    public int numberOfFiles; 
    private File src; 
    private List<File> files; 

    public FileManager(File src) { 
     this.src = src; 
     dirNames = new ArrayList<String>(); 
     dirFiles = new HashMap<String, ArrayList<String>>(); 
     numberOfFiles = 0; 
     files = new ArrayList<File>(); 
    } 



    public int getSize() { 
     return numberOfFiles; 
    } 

    public ArrayList<String> getDirectories(){ 
     return dirNames; 
    } 

    public List<File> getFiles() { 
     Iterator it = dirFiles.entrySet().iterator(); 
     while (it.hasNext()) { 
      Map.Entry pair = (Map.Entry) it.next(); 
      String folderName = (pair.getKey()).toString(); 
      ArrayList<String> FileNames = (ArrayList<String>) pair.getValue(); 
      if (FileNames != null) { 
       for (String fileName : FileNames) { 
        if(replaceSelected(fileName)) { 
         File fXmlFile = new File(fileName); 
         files.add(fXmlFile); 
        } 
        else { 
        } 
       } 
      } 
     } 
     return files; 
    } 

    public void readFiles() throws IOException { 
     readFiles(src); 
    } 

    private void readFiles(File folder) throws IOException { 
     if (folder.isDirectory()) { 
      ArrayList<String> fileNames = new ArrayList<String>(); 
      for (final File file : folder.listFiles()) { 
       if (file.isDirectory()) { 
        readFiles(file); 
       } else { 
        String fileName = (file.getPath()).toString(); 
        if(fileName.toLowerCase().endsWith(".xml")) { 
         fileNames.add(file.getPath()); 
         numberOfFiles = numberOfFiles + 1; 
         System.out.println("."); 
         if(!dirNames.contains(file.getParentFile().getName())) 
           dirNames.add(file.getParentFile().getName()); 
        } 
       } 
      } 
      dirFiles.put(folder.getName(), fileNames); 
     } 
    } 

    private boolean replaceSelected(String filePath) { 
     String line; 
     String input = ""; 
     try { 
      DetermineEncoding DE = new DetermineEncoding(); 
      String encoding = DE.getFileEncoding(filePath); 
      InputStreamReader file = new InputStreamReader(new FileInputStream(
        filePath), encoding); 
      BufferedReader br = new BufferedReader(file); 
      while ((line = br.readLine()) != null) { 
       input += line.toString() + " "; 
      } 
      file.close(); 
      Writer out = new BufferedWriter(new OutputStreamWriter(
        new FileOutputStream(filePath), "UTF-8")); 
      out.append(input.trim()); 
      out.flush(); 
      out.close(); 
     } catch (Exception e) { 
      return false; 
     } 
     return true; 
    } 

} 

DetermineEncoding.java

import java.io.FileInputStream; 
import java.io.FileNotFoundException; 
import java.io.IOException; 

import org.mozilla.universalchardet.UniversalDetector; 

public class DetermineEncoding { 

    public DetermineEncoding() { 
     // TODO Auto-generated constructor stub 
    } 

    public String getFileEncoding(String fileName) throws IOException { 
     byte[] buf = new byte[4096]; 
     java.io.FileInputStream fis = new FileInputStream(fileName); 
     UniversalDetector detector = new UniversalDetector(null); 
     int nread; 
     while ((nread = fis.read(buf)) > 0 && !detector.isDone()) { 
      detector.handleData(buf, 0, nread); 
     } 
     detector.dataEnd(); 
     String encoding = detector.getDetectedCharset(); 
     if (encoding != null) { 
      return encoding; 
     } else { 
      return ""; 
     } 


    } 

} 

請幫我找出問題。

+1

你不應該被修改從事件派發線程的上下文之外的UI組件的狀態,在'doInBackground'方法內調用'progressBar.setIndeterminate(true);'是一個壞主意 – MadProgrammer

+0

好吧,但那是當我的progressBar要更新時。我該怎麼做 ?感謝您的回覆 –

+0

看看這個[示例](http://stackoverflow.com/questions/23125642/swingworker-in-another-swingworkers-done-method/23126410#23126410) – MadProgrammer

回答

2

基本問題是知覺。你「認爲」用戶界面沒有響應,事實上,它只是在等待。

當您撥打readFiles時,它會經歷先前掃描過的所有文件,讀取它們,然後再次寫出它們,所有進度欄都處於「確定」模式,因此它不顯示任何內容。

您需要的是FileManager向工作人員提供有關其進度的更新的一些方法,但工作人員還需要經過許多其他方法,這些方法也必須提供進度通知。

這似乎暗示了某種Observer Pattern的需要,在某些情況發生變化時,工作人員可以通過程序的其他部分得到通知。

我們還需要做到這一切的方式,它允許使用更新UI安全

讓我們開始與觀察者......

public interface ProgressListener { 
    public void progressChanged(double progress); 
    public void setStatus(String text); 
} 

很簡單,它會在你通知狀態變化的進展,允許誰在聽他們認爲合適的時候進行更新。

基本進度值介於0-1之間,這意味着監聽者實際上並不關心您擁有多少個值,它只關心您的進度,因此無需嘗試更新進度條並將其最大化價值,而是,單純強調需要更新之間0-100

現在,我們需要騰出空間的API

private void convertToXLS(FileManager fileMgr, ProgressListener listener) { 
    try { 
     int i = 1; 
     listener.progressChanged(0d); 
     List<File> files = fileMgr.getFiles(listener); 
     for (File file : files) { 
      listener.setStatus("Reading " + i + " of " + fileMgr.getSize() + " files"); 
      parUtils.parseFileUsingDOM(file); // This will read content of the input file 
      listener.progressChanged(i/(double) files.size()); 
     } 
     btnConvertXmlTo.setEnabled(true); 

    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
} 

而且FileManager#getFiles其餘部分的進度條....

public List<File> getFiles(ProgressListener listener) { 
    Iterator it = dirFiles.entrySet().iterator(); 
    int count = dirFiles.size(); 
    for (Map.Entry<String, ArrayList<String>> entry : dirFiles.entrySet()){ 
     count += entry.getValue() == null ? 0 : entry.getValue().size(); 
    } 
    int index = 0; 
    listener.setStatus("Processing files..."); 
    while (it.hasNext()) { 
     Map.Entry pair = (Map.Entry) it.next(); 
     String folderName = (pair.getKey()).toString(); 
     ArrayList<String> FileNames = (ArrayList<String>) pair.getValue(); 
     if (FileNames != null) { 
      for (String fileName : FileNames) { 
       if (replaceSelected(fileName)) { 
        File fXmlFile = new File(fileName); 
        files.add(fXmlFile); 
       } else { 
       } 
       index++; 
       listener.progressChanged(index/(double)count); 
      } 
     } 
    } 
    return files; 
} 

接下來,我們需要更新Task以利用它的進度支持,我們還需要允許更改進度欄的狀態。

這個我們可以通過publish/process方法來做,從後臺線程發送消息到EDT。我們也可以「作弊」一點,並用它發送消息來改變進度條的狀態(fyi:你也可以使用屬性改變監聽器支持來做到這一點,這可能是一個更清潔的方法)

class Task extends SwingWorker<Void, String> { 

    protected static final String INDETERMINATE_ON = "indeterminate.on"; 
    protected static final String INDETERMINATE_OFF = "indeterminate.off"; 

    private FileManager fileMgr; 

    public Task(String srcPath) { 
     this.fileMgr = new FileManager(new File(srcPath)); 

    } 

    @Override 
    protected void process(List<String> chunks) { 
     for (String text : chunks) { 
      if (INDETERMINATE_OFF.equals(text)) { 
       progressBar.setIndeterminate(false); 
      } else if (INDETERMINATE_ON.equals(text)) { 
       progressBar.setIndeterminate(true); 
      } else { 
       progressBar.setString(text); 
      } 
     } 
    } 

    /* 
     * Main task. Executed in background thread. 
    */ 
    @Override 
    public Void doInBackground() { 
     try { 
      publish(INDETERMINATE_ON); 
      fileMgr.readFiles(); 
      publish(INDETERMINATE_OFF); 
      convertToXLS(fileMgr, new ProgressListener() { 
       @Override 
       public void progressChanged(double progress) { 
        setProgress((int) (progress * 100d)); 
       } 

       @Override 
       public void setStatus(String text) { 
        publish(text); 
       } 
      }); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
     return null; 
    } 

    /* 
     * Executed in event dispatching thread 
    */ 
    @Override 
    public void done() { 
     Toolkit.getDefaultToolkit().beep(); 
     try { 
      progressBar.setString("FileRead Successful"); 
     } catch (Exception e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
    } 
}//end of task class 

最後,我們需要在我們創建它添加PropertyChangeListenerTask,所以我們可以得到progress更新和更新進度條...

task.addPropertyChangeListener(new PropertyChangeListener() { 
    @Override 
    public void propertyChange(PropertyChangeEvent evt) { 
     String name = evt.getPropertyName(); 
     switch (name) { 
      case "progress": 
       int value = (int) evt.getNewValue(); 
       progressBar.setValue(value); 
       break; 
     } 
    } 
}); 

簡單:P

+0

謝謝一噸! –

0

該代碼似乎很好。我無法檢查的唯一東西是FileManager。使用FileReader,它可以在單獨的線程中運行,以允許用戶同時操作。所以我猜FileManager一定會導致問題。

+0

你能幫我理解在哪裏添加這部分代碼。此外,任務擴展SwingWorker類(只是要確保你沒有錯過) –

+0

@AkhkhVemavarapu,我編輯我的答案,因爲我沒有錯過;) – marcbrouwer

+1

所以你說FileManager進入一個新的線程?那我該怎麼做呢?我絕對無能爲力,如何處理這個問題。任何幫助,高度讚賞。 –