Java Timeout for an inactive user - java

I have a problem with the ScheduledThreadPoolExecutor.
I need a timeout which will redirect to the first page after 'n'-seconds. If the user enters a character, the timer should start counting again and may not redirect to the first page.
(So the counter should abort his scheduled Task)
The Problem is, that the Timer starts, but it doesn't cancel the scheduled Task if a key was typed. The stop() - Method will be called. But scheduledThreadPool.shutdownNow(); seems not to work.
My TimerClass looks like this:
public class MyTimer {
private final Runnable logicalWorker;
private final long delay;
private final ScheduledThreadPoolExecutor scheduledThreadPool;
public MyTimer(final Runnable logicalWorker, final long delay) {
scheduledThreadPool = new ScheduledThreadPoolExecutor(1);
this.logicalWorker = logicalWorker;
this.delay = delay;
}
public void start() {
scheduledThreadPool.schedule(logicalWorker, delay, TimeUnit.SECONDS);
}
public void stop() {
scheduledThreadPool.shutdownNow();
scheduledThreadPool.getQueue().clear();
}
public void restart() {
start();
}
public boolean isScheduled() {
return !scheduledThreadPool.isTerminated() && !scheduledThreadPool.isShutdown();
}
}
The method in a superclass which calls the timerClass is this one:
protected void startTimeout() {
if (currentInstance.getAutoTimeout() != null && currentInstance.getAutoTimeout().isScheduled()) {
currentInstance.getAutoTimeout().restart();
return;
}
currentInstance.setAutoTimeout(new MyTimer(new Runnable() {
#Override
public void run() {
Platform.runLater(new Runnable() {
#Override
public void run() {
if (!PageContent.PAGE_ID.equals(currentInstance.getPageId()) && !forceOpen) {
cancelCurrentProcesses();
switchPageByPageId(PageContent.PAGE_ID);
}
}
});
}
}, currentInstance.getPageDelay()));
if (currentInstance.getPageDelay() > 0) {
currentInstance.getAutoTimeout().start();
}
}
The KeyListener and the MouseClickListener will be set on the scene at the beginning by this method:
protected void placePage() throws SecurityException, IllegalArgumentException, IllegalAccessException,
InstantiationException, NoSuchMethodException, InvocationTargetException {
startTimeout();
currentInstance.getRoot().setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent arg0) {
startTimeout();
}
});
currentInstance.getRoot().setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent arg0) {
startTimeout();
}
});
}

Take a look at this -> http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledThreadPoolExecutor.html
The shutdownNow() makes no guarantees to cancel execution.
As for a solution, I recommend you use ScheduledFuture or Future objects rather than Runnable.
[EDIT] A scheduled future, on which you can call a .cancel() function instead of .shutdownNow() is returned by the .schedule() call, which you call, but don't seem to use (or indeed save) the handle to the Future anywhere. And yes, you do still need a Runnable but only insofar as it will give you the Future handle.
Try something like this:
private ScheduledFuture<?> future;
public void start() {
future = scheduledThreadPool.schedule(logicalWorker, delay, TimeUnit.SECONDS);
}
public void stop() {
if(future != null) future.cancel();
}
[/EDIT]
More details here -> http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledFuture.html

Related

libgdx Timer countdown implmentation

I want to create efficienty timer in LibGDX framework, that will count the time left for my character. The doSmth() method should be called as many times as some flag is set to true. I know that the third parametr of Timer is that, how many times should it trigger. For now one Im calling the method recursive, but I dont think it is the efficient way.
public void updateTimer(){
new Timer().scheduleTask(new Timer.Task() {
#Override
public void run() {
doSmth();
updateTimer();
}
},1);
}
It would be more accurate to use a repeat count. Your method will introduce a bit of error each time the task is run, because the task is run on the GL thread, so it will occur just slightly after one second, but you are repeating it one second after that. So with each repeat you are slightly further behind.
private Timer.Task myTimerTask = new Timer.Task() {
#Override
public void run() {
doSmth();
}
};
public void startTimer(){
Timer.schedule(myTimerTask, 1f, 1f);
}
And when you need to stop it:
myTimerTask.cancel();
The com.badlogic.gdx.utils.Timer executes tasks in the future on the main loop thread,even if your game is in a pause screen, a menu or in another state, you can simply control time in the render method by adding delta time.
private float timeSeconds = 0f;
private float period = 1f;
public void render() {
//Execute handleEvent each 1 second
timeSeconds +=Gdx.graphics.getRawDeltaTime();
if(timeSeconds > period){
timeSeconds-=period;
handleEvent();
}
[...]
}
public void handleEvent() {
[...]
}
To keep organized i personally have an array on my main game class that holds all my timed events and process everything on the render cycle. In your case you can put some control variables as you wish.
my implementation example:
// MainGame.java
private ObjectMap<TimedEventEnum, TimedEvent> hshTimedEvent;
public void render(){
executeTimedEvents();
}
private void executeTimedEvents() {
for (ObjectMap.Entry<TimedEventEnum, TimedEvent> entry : hshTimedEvent) {
TimedEvent event = entry.value;
event.process();
}
}
public void killEvent(TimedEventEnum event) {
hshTimedEvent.remove(event);
}
// TimedEventEnum.java
public enum TimedEventEnum {
COUNT_MONEY,
CHECK_FOR_ACHIEVS,
ANOTHER_EVENT_EXAMPLE
}
//CountMoneyTimedEvent.java
public class CountMoneyTimedEvent extends Timer implements TimedEvent {
public CountMoneyTimedEvent() {
super();
init(this, 4f, false);
}
#Override
public void execute() {
//execute logic here
}
#Override
public void reset() {
this.timesFired = 0L;
}
}
//Timer.java
public abstract class Timer {
private Float deltaCount;
private Float timeToEvent;
private Boolean isRepeatable;
protected Long timesFired;
private TimedEvent event;
Timer() {
}
public void init(TimedEvent event, Float eventTime, Boolean isRepeatable) {
this.deltaCount = 0f;
this.timeToEvent = eventTime;
this.isRepeatable = isRepeatable;
this.timesFired = 0L;
this.event = event;
}
public void process() {
if (isEventTime()) {
event.execute();
}
}
private Boolean isEventTime() {
if (event != null && (isRepeatable || timesFired == 0)) {
deltaCount += Gdx.graphics.getRawDeltaTime();
if (deltaCount > timeToEvent) {
deltaCount -= timeToEvent;
timesFired++;
return true;
}
}
return false;
}
protected void executeNextEvent() {
deltaCount = timeToEvent;
}
}
// TimedEvent.java
public interface TimedEvent {
void execute();
void reset();
void process();
}

Synchronized, lock and wait blocking main UI thread

I have made a simple TaskManager trying to manage a Runnable queue that is needed for my project. However, with a simple scenario, adding a new Runnable blocks the calling thread (main UI thread).
It happens when you add a new task while a current task is not finished.
You can find below a scenario that reproduces it.
I don't clearly understand why, and how I could prevent this.
This is the task manager class :
public class TaskManager {
private Queue<Runnable> executionQueue;
private final Object lock = new Object();
public TaskManager() {
executionQueue = new LinkedList<>();
startListening();
}
public void executeAsyncWithCompl(Runnable runnable, CompletionHandler completionHandler) {
Runnable runnableWithCompl = new RunnableWithCompl(runnable, completionHandler);
executeRunnable(runnableWithCompl);
}
private void executeRunnable(Runnable runnable) {
synchronized (lock) {
executionQueue.add(runnable);
lock.notifyAll();
}
}
public void release() {
synchronized (lock) {
lock.notify();
}
}
private void startListening() {
Thread executionThread = new Thread(new Runnable() {
#Override
public void run() {
listenTasks();
}
});
executionThread.start();
}
private void listenTasks() {
synchronized (lock) {
while (true) {
try {
if(executionQueue.isEmpty()) {
lock.wait();
}
Runnable runnable = executionQueue.poll();
runnable.run();
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
}
}
Here is the RunnableWithCompl class :
public class RunnableWithCompl implements Runnable {
private CompletionHandler completionHandler;
private Runnable runnable;
public RunnableWithCompl(Runnable runnable, CompletionHandler completionHandler) {
this.runnable = runnable;
this.completionHandler = completionHandler;
}
#Override
public void run() {
runnable.run();
if(completionHandler != null)
completionHandler.onFinish();
}
}
And the CompletionHandler interface :
public interface CompletionHandler {
void onFinish();
}
The scenario. Let's say you have an Activity with a spinner (to show UI is not blocked), and a button to trigger long tasks.
private TaskManager taskManager;
public void init() {
taskManager = new TaskManager();
launchLongTask();
}
private void onButtonClick() {
launchLongTask() ;
}
private void launchLongTask() {
Runnable longTask = new Runnable() {
#Override
public void run() {
try {
Thread.sleep(15000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Log.d(TAG, "Launching long task");
taskManager.executeAsyncWithCompl(longTask, new CompletionHandler() {
#Override
public void onFinish() {
Log.d(TAG, "Long task finished");
}
});
}
The problem is in your startListening() implementation.
It holds the monitor to lock while it is executing tasks which means no other method can obtain the monitor while it is doing work.
This means release() and executeRunnable(...) will block until there are no more runnables queued.
It also means the thread might block if the thread running startListening() is notified before other threads, because it means those threads cannot continue until it releases the monitor.

How to have two timers for Java game applet

I require two timers. One to run the game e.g move objects, and perform checks and another as a countdown timer. I have tried the following:
Timer countdownTimer = new Timer(1000,this);
Timer gameTimer = new Timer(30,this);
public void init()
{
this.actionPerformed(this); //add action listener to content pane
}
#Override
public void actionPerformed(ActionEvent e)
{
if(e.getSource() == gameTimer)
{
// control the game
}
if(e.getSource() == countdownTimer)
{
//decremenet the timer
}
}
However this returns a Null pointer exception when I try to run the applet. How do I properly distinguish each timer from the other and perform the desired actions at each timer tick. Thanks
I'm assuming you're using the javax.swing.Timer class?
this.actionPerformed(this); does not seem right, as your applet is not an ActionEvent.
Besides, you should start the timers in the init() method:
public class GameApplet extends Appel implements ActionListener
public void init()
{
countdownTimer = new Timer(1000,this);
gameTimer = new Timer(30,this);
countdownTimer.start();
gameTimer.start();
}
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == gameTimer) {
// control the game
}
if(e.getSource() == countdownTimer) {
//decremenet the timer
}
}
}
Check the Timer javadoc that also redirects to the Java tutorial about Timers.
Use ScheduledExecutorService. It is more efficient than timer. To see its effect run following code.
class GameControl {
private final ScheduledExecutorService scheduler =
Executors.newScheduledThreadPool(1);
public void beepForGame() {
final Runnable beeper = new Runnable() {
#Override
public void run() {
System.out.println("Game");
}
};
final ScheduledFuture<?> beeperHandle =
scheduler.scheduleAtFixedRate(beeper, 30, 30, SECONDS);
scheduler.schedule(new Runnable() {
#Override
public void run() {
beeperHandle.cancel(true);
}
}, 60 * 60, SECONDS);
}
public void beepCountDown() {
final Runnable beeper = new Runnable() {
#Override
public void run() {
System.out.println("count down");
}
};
final ScheduledFuture<?> beeperHandle =
scheduler.scheduleAtFixedRate(beeper, 1, 1, SECONDS);
scheduler.schedule(new Runnable() {
#Override
public void run() {
beeperHandle.cancel(true);
}
}, 60 * 60, SECONDS);
}
public static void main(String[] args) {
GameControl bc=new GameControl();
bc.beepCountDown();
bc.beepForGame();
}
}

Strange Concurrency Issue in Java Swing

I have a Jframe which is my application's window (appFrame in the following code) that contains a lot of logic and takes like 1 second or so to load. In the meantime I want to show my user a very nice loading frame (initFrame). However, when I run this code, the initFrame does appear but the text in a JLabel on it doesn't appear immediately - it actually doesn't appear at all in the brief moment till the app frame is loaded.
If i comment out all the appFrame, and only launch the initFrame, the text is loaded instantly, no waiting time at all. Why is this so? Might this be a concurrency issue?
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() { //as per best practice for concurrency in swing - see http://docs.oracle.com/javase/tutorial/uiswing/concurrency/
#Override
public void run() {
final JFrame initFrame = new InitFrame();
initFrame.setVisible(true);
final AppFrame appFrame = new AppFrame();
appFrame.setVisible(true);
initFrame.setVisible(false);
initFrame.dispose();
}
});
}
I would separate the frames' creation into two threads. The first, initializing InitFrame. Running this thread and calling isShowing() on the InitFrame object. When it returns true, run the second thread to initialize and show AppFrame.
This will force a happens before relationship between the visibility of the two frames.
class Main {
JFrame initFrame = null;
AppFrame appFrame = null;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
initFrame = new InitFrame();
initFrame.setVisible(true);
}
});
while(!initFrame.isShowing()) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
}
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
appFrame = new AppFrame();
appFrame.setVisible(true);
initFrame.setVisible(false);
initFrame.dispose();
}
});
}
}
Here's an example of what might be going wrong in your AppFrame.
You can run the test with threading:
java SplashTest true
or without
java SplashTest
When threading is enabled, you see the SplashFrame and AppFrame updating every 250ms, more or less.
When threading is not enabled, you get to see the SplashFrame with no components showing, the app 'hangs' for 4 seconds, then you see the AppFrame.
The example is somewhat contrived, but might give you some ideas.
Note that the SplashFrame has no 'direct' connection to the AppFrame. All communication is through the AppFrameWorkListener interface.
I've also put the 'work' in the AppFrame. But really if there is a lot of processing to be done it should be extracted out of the UI code, run in a separate Thread, and the AppFrame would be notified of progress by the task, in the same way as the SplashFrame currently is.
import javax.swing.*;
class SplashTest {
static boolean useThread = false;
public static void main(String[] args) {
// Pass true at the command line to turn on threading.
// No args, or any value other than true will turn off threading.
if (args.length > 0) {
useThread = new Boolean(args[0]);
}
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
SplashFrame splashFrame = new SplashFrame();
splashFrame.setVisible(true);
new AppFrame(splashFrame).setVisible(true);
}});
}
private static class BaseFrame extends JFrame {
public BaseFrame() {
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setSize(200, 200);
setTitle(getClass().getSimpleName());
}
}
private static class SplashFrame extends BaseFrame implements AppFrameWorkListener {
JLabel status;
public SplashFrame() {
setLocation(0, 0);
status = new JLabel("Splash Frame");
getContentPane().add(status);
}
public void appFrameWorkStart() {
status.setText("Work started");
}
public void appFrameWorkProgress(long timeElapsed) {
status.setText("Work has taken " + timeElapsed + "ms so far");
}
public void appFrameWorkDone() {
// http://stackoverflow.com/questions/1234912/how-to-programmatically-close-a-jframe
setVisible(false);
dispose();
}
}
private static class AppFrame extends BaseFrame {
JLabel status;
AppFrameWorkListener listener;
public AppFrame(AppFrameWorkListener listener) {
setLocation(200, 200);
status = new JLabel("App Frame");
getContentPane().add(status);
this.listener = listener;
// None of this 'heavy lifting' should be in a constructor.
if (useThread) {
new Thread(new Runnable() {
#Override
public void run() {
doLotsOfWork(4);
}
}).start();
} else {
doLotsOfWork(4);
onWorkDone();
}
}
private void doLotsOfWork(int workLengthSeconds) {
// We're starting. Ensure onWorkStart is called on the EDT,
// as this method may be called from a different Thread.
invokeOnWorkStartOnEDT();
long start = System.currentTimeMillis();
// Hammer the CPU for "workLengthSeconds" number of seconds.
// And do some contrived progress reporting.
long workLengthMs = workLengthSeconds * 1000;
while (System.currentTimeMillis() - start < workLengthMs) {
long innerStart = System.currentTimeMillis();
// Consume 250ms CPU before issuing progress update.
while (System.currentTimeMillis() - innerStart < 250);
invokeOnWorkProgressOnEDT(System.currentTimeMillis() - start);
}
// We're done now. Ensure onWorkDone is called on the EDT,
// as this method may be called from a different Thread.
invokeOnWorkDoneOnEDT();
}
private void invokeOnWorkStartOnEDT() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
onWorkStart();
}
});
}
private void invokeOnWorkProgressOnEDT(final long timeElapsed) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
onWorkProgress(timeElapsed);
}
});
}
private void invokeOnWorkDoneOnEDT() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
onWorkDone();
}
});
}
private void onWorkStart() {
status.setText("Work Started");
if (null != listener) {
// Tell someone who's interested in the work status.
listener.appFrameWorkStart();
}
}
private void onWorkProgress(long timeElapsed) {
status.setText("Work has taken " + timeElapsed + "ms so far");
if (null != listener) {
// Tell someone who's interested in the work status.
listener.appFrameWorkProgress(timeElapsed);
}
}
private void onWorkDone() {
status.setText("Work Done");
if (null != listener) {
// Tell someone who's interested in the work status.
listener.appFrameWorkDone();
}
}
}
interface AppFrameWorkListener {
public void appFrameWorkDone();
public void appFrameWorkStart();
public void appFrameWorkProgress(long timeElapsed);
}
}
You Should use Java Thread and you can show an interactive Splash Screen (Custom made) to your user in the mean while while your code is generating whatever you want here is a tutorial just take a look
You should use Threads for good and efficient concurrency thats it

Cannot update Swing component under a heavy process

I am running a very heavy process under an anonymous SwingWorker thread. In the meantime, I'm reporting progress to the GUI using a progress bar. However, Swing threading is doing me in. It's simply not updating anything in time. I'm not sure how to do it, as I've tried updating the GUI from the SwingWorker thread, and outside, and both refuse to work.
How can I reliably update the Swing UI while a heavy worker thread is running?
Things I've tried
This does not work (with or without wrapping in the invokeLater command).
new LocalCompressor(compressor).execute();
while (!compressionDone) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
int percent = compressor.getPercentDone();
progressBar.setValue(percent);
statusLabel.setText(percent);
}
});
}
Additionally, attempting to update the UI from a concurrent measuring thread does not work:
class LocalCompressor extends SwingWorker<Void, Void> {
// [...]
public LocalCompressor(Compressor compressor) {
this.compressor = compressor;
// [...]
}
#Override
protected Void doInBackground() {
final Thread t1 = new Thread(new Runnable() {
#Override
public void run(){
compressor.compress();
}
});
final Thread t2 = new Thread(new Runnable() {
#Override
public void run() {
t1.start();
while (t1.isAlive()) {
updateUI(compressor.getPercentDone());
}
}
});
t2.start();
return null;
}
// [...]
}
You're not really using your SwingWorker. The worker already is a Thread for itself. If you have the possibility to put your long running code into the doInBackground(), put it there. Then just call publish(Integer) with your actual progress and process the chunks you get in the process(List<Integer>)-method. In process() you can update the gui, it's on the EDT.
EDIT:
Actually, what you're doing right now is polling in several-while loops, this is kinda power-consuming. That's why I think its better to you events in your algorithm, everytime you got a percent or everytime the loop starts a new round or something like that.
Did you try the very simple and basic way of using a SwingWorker? Like #Zhedar previously said, a SwingWorker already is a Thread for itself. So remove both your inner threads (t1, t2) and just use your time-consuming compress() method in doInBackground().
Something very basic like the following:
class LocalCompressor extends SwingWorker<Void, Integer> {
// .....
// Your constructor here
// .....
#Override
protected Void doInBackground() throws Exception {
compress();
return null;
}
#Override
protected void process(List<Integer> chunks) {
for (Integer chunk : chunks) {
progressBar.setValue(chunk);
statusLabel.setText(chunk);
}
}
}
Now this compress() method should be moved inside the SwingWorker and it must have somewhere a publish(), in your case it might be publish(getPercentDone()) or whatever.
private void compress() {
// .....
publish(getPercentDone());
// .....
}
This is how things are usually done with a SwingWorker.
Expanding on the answers and advice provided here already, here is one way to code it. I'm assuming the compressor itself has no ability to do callbacks but you can ask it for the percent done.
Within the swingworker thread (doInBackground) we start the real compression thread. Then start a polling loop in the background thread, to update the UI a few times a second. To notify the UI thread, call publish. This will cause the overridden method process to be called periodially in the event thread. From here we can safely update the progress bar and status label.
public class LocalCompressor extends SwingWorker<Void, Integer>
{
private Compressor compressor;
public LocalCompressor(Compressor compressor)
{
this.compressor = compressor;
// [...]
}
#Override
protected void done()
{
System.out.println("Compression is done. Going to do something with it...");
}
#Override
protected void process(List<Integer> chunks)
{
for (Integer percent : chunks)
{
progressBar.setValue(percent);
statusLabel.setText(percent);
}
}
#Override
protected Void doInBackground() throws Exception
{
final Thread t1 = new Thread(new Runnable()
{
#Override
public void run()
{
compressor.compress();
}
});
t1.start();
while (t1.isAlive())
{
int percentDone = compressor.getPercentDone();
publish(percentDone);
Thread.sleep(200);
}
return null;
}
}
You could employee a producer/consumer pattern...
Here's a really basic concept...
public class ProducerComsumer {
public static void main(String[] args) {
new ProducerComsumer();
}
public ProducerComsumer() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JPanel panel = new JPanel(new GridBagLayout());
panel.setBorder(new EmptyBorder(12, 12, 12, 12));
JProgressBar progressBar = new JProgressBar();
panel.add(progressBar);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Producer producer = new Producer();
producer.start();
Consumer consumer = new Consumer(producer, progressBar);
consumer.start();
}
});
}
public class Producer extends Thread {
private volatile float progress;
private volatile boolean done;
public Producer() {
setPriority(NORM_PRIORITY - 1);
setDaemon(true);
}
public float getProgress() {
return progress;
}
public boolean isDone() {
return done;
}
#Override
public void run() {
done = false;
for (int index = 0; index < Integer.MAX_VALUE; index++) {
progress = (float) index / (float) Integer.MAX_VALUE;
}
done = true;
System.out.println("All done...");
}
}
public class Consumer extends Thread {
private Producer producer;
private JProgressBar progressBar;
public Consumer(Producer producer, JProgressBar progressBar) {
setDaemon(true);
setPriority(NORM_PRIORITY - 1);
this.producer = producer;
this.progressBar = progressBar;
}
public JProgressBar getProgressBar() {
return progressBar;
}
public Producer getProducer() {
return producer;
}
#Override
public void run() {
while (!producer.isDone()) {
updateProgress();
try {
sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(ProducerComsumer.class.getName()).log(Level.SEVERE, null, ex);
}
}
updateProgress();
}
protected void updateProgress() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
int progress = Math.round(getProducer().getProgress() * 100f);
System.out.println("Update progress to " + progress);
getProgressBar().setValue(progress);
}
});
}
}
}
Have a play around with the Thread.setPriority values and see if it makes any difference
I'm assuming (ya know how that goes) that the call to LocalCompressor.execute() is blocking. If that's the case, your while loop won't run until it's all done, and then you're defeating the purpose of getting a steady stream of updates on your UI.
Give this, or something similar, a shot:
LocalCompressor comp = new LocalCompressor(compressor);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
while (!compressionDone) {
int percent = compressor.getPercentDone();
progressBar.setValue(percent);
statusLabel.setText(percent);
}
}
});
comp.execute();
}

Categories