进度条的Java Swing Worker - UI长时间没有响应

时间:2015-10-27 16:57:47

标签: java swing progress-bar swingworker

我使用Java,Swing(窗口构建器)在Windows上开发了一个应用程序 单击按钮,我的应用程序将转到另一个类(FileManager.java文件)以计算输入文件夹中的文件总数(同时progressBar将处于不确定模式)。知道文件数后,设置progressBar最大值。

然后我调用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

我的用户界面在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 "";
        }


    }

}

请帮我确定问题。

2 个答案:

答案 0 :(得分:3)

基本问题是感知问题。你“认为”UI没有响应,事实上,它只是在等待。

当您致电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。我们还可以“欺骗”一点并使用它来发送消息以更改进度条的indeterminate状态(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

最后,我们需要在创建PropertyChangeListener时向Task添加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; } } }); 更新并更新进度条......

struts.xml

简单:P

答案 1 :(得分:0)

代码似乎很好。我似乎无法检查的唯一事情是FileManager。使用FileReader,它在一个单独的线程中运行,允许用户同时操作。所以我猜FileManager必定会导致问题。