我希望让用户了解I / O操作的进度。目前我有一个内部课程,在我启动I / O之前启动,并在完成后停止。它看起来像这样:
class ProgressUpdater implements Runnable {
private Thread thread;
private long last = 0;
private boolean update = true;
private long size;
public ProgressUpdater(long size) {
this.size = size;
thread = new Thread(this);
}
@Override
public void run() {
while (update) {
if (position > last) {
last = position;
double progress = (double) position / (double) size * 100d;
parent.setProgress((int) progress);
}
}
}
public void start() {
thread.start();
}
public void stop() {
update = false;
parent.setProgress(100);
}
}
parent
是我对UI的引用,position
是我外部类中的一个字段,表示我们在I / O中取得的进展。我在停止时将进度设置为100%,因为有时I / O会完成并停止我的更新程序,然后才能完成更新上一个增量。这只能确保它达到100%。
目前,这是有效的,我这样使用它:
ProgressUpdater updater = new ProgressUpdater(file.length());
updater.start();
//do I/O
//...
updater.stop();
问题是循环非常糟糕地占用CPU。我尝试在那里抛出一个锁(带有等待/通知)但我不知道在使用wait / notify时我正在做什么,所以它只是挂了我的线程。我该怎么做才能阻止它使用这么多CPU周期?
答案 0 :(得分:6)
您应该考虑使用SwingWorker。基本上,你在SwingWorker
提供的后台线程上执行所有IO,并且有内置的方法向swing线程报告进度。
然后,无论何时更新该位置,您都可以自动更新进度,而不是连续轮询,这是您迄今为止所做的。
查看Swingworker Timeout或SwingWorker with FileReader,了解是否有任何帮助。
另一种解决方案,如果您不想使用SwingWorker
,可能只是通过这样的调用直接更新进度条,而不是更新position
值。 :
SwingUtilities.invokeLater(new Runnable() {
@Override public void run() {
getMyProgressBar().setValue(position);
}
});
假设您已设置进度条,以使其最大值为文件大小。
答案 1 :(得分:5)
首先,永远不要使用Thread
来更新java中的GUI(Swing组件)。而是使用javax.swing.Timer
或javax.swing.SwingWorker
。并用于基本I / O操作使用
ProgressMonitorInputStream。
作为读取文件并在JTextArea中使用progrressbar显示文件的简单示例。请查看下面给出的代码:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.List;
import java.io.*;
import java.beans.*;
class ProgressBarFrame extends JFrame
{
JProgressBar progressBar;
int BUFFERSIZE = 10;
JTextArea textArea;
MyWorker worker;
private void createAndShowGUI()
{
progressBar = new JProgressBar(0,100);
progressBar.setStringPainted(true);
JButton button = new JButton("Read File");
textArea = new JTextArea(30,100);
JScrollPane jsp = new JScrollPane(textArea);
Container c = getContentPane();
c.add(jsp);
c.add(progressBar,BorderLayout.NORTH);
c.add(button,BorderLayout.SOUTH);
worker = new MyWorker();
button.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent evt)
{
textArea.setText("");
progressBar.setValue(0);
if (worker.getState()== SwingWorker.StateValue.DONE || worker.getState()==SwingWorker.StateValue.STARTED)
{
worker.cancel(true);
worker = new MyWorker();
}
worker.execute();
}
});
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
class MyWorker extends SwingWorker<Void, String>
{
MyWorker()
{
addPropertyChangeListener(new PropertyChangeListener()
{
public void propertyChange(PropertyChangeEvent evt)
{
if ("progress".equals(evt.getPropertyName()))
{
progressBar.setValue((Integer)evt.getNewValue());
}
}
});
}
@Override
public Void doInBackground() throws IOException
{
File file = new File("ProgressBarFrame.java");
long size = file.length();
long temp = 0;
BufferedInputStream bfin = new BufferedInputStream(new FileInputStream(file));
byte[] buffer=new byte[BUFFERSIZE];
int totalRead = -1;
while ((totalRead=bfin.read(buffer))!=-1 && ! isCancelled())
{
temp = temp + totalRead;
publish(new String(buffer));
if (bfin.available()<BUFFERSIZE && bfin.available()!= 0)
buffer = new byte[bfin.available()];
else if(bfin.available()==0)
buffer = new byte[1];
else
buffer=new byte[BUFFERSIZE];
setProgress((int)((temp/(float)size) * 100));
try{Thread.sleep(1);}catch(Exception ex){}
}
setProgress(100);
return null;
}
@Override
protected void process(List<String> chunks)
{
for (String value : chunks)
{
textArea.append(value);
}
}
}
public static void main(String st[])
{
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
ProgressBarFrame pf = new ProgressBarFrame();
pf.createAndShowGUI();
}
});
}
}