Changing Rendering Screen on separate thread after an HTTP request - java

My goal is to change the screen of a user when the game can't reach the backend anymore. My code executes as expected except the screen never changes. Here's the initial call:
timer.testTimeToServer(api, game);
Here's the timer object's class. I put (my url) in place of the actual IP address of my backend:
public class CustomTimer {
private static final float timeToDrop = 2000;
private float time = 0;
private StopWatch watch = new StopWatch();
public void testTimeToServer(ApiCall api,final proofOfConcept game){
watch.start();
api.httpGetWithCallback("(my url)/api/v1/character", new CallBack(){
#Override
public void callback(String resp){
System.out.println("Server Responded");
time = watch.getTime();
watch.stop();
watch.reset();
if(time > timeToDrop){
game.setScreen(new GameOverScreen(game, false));
System.out.println("Should have switched screen")
}
}
});
}
}
Here's the httpGetWithCallback method in the api object:
public void httpGetWithCallback (final String URL, final CallBack callback){
Thread th = new Thread(new Runnable(){
#Override
public void run() {
Gdx.app.postRunnable(new Runnable() {
#Override
public void run() {
Net.HttpRequest httpRequest = new Net.HttpRequest(Net.HttpMethods.GET);
httpRequest.setUrl(URL);
httpRequest.setHeader("Content-Type", "application/json");
httpRequest.setTimeOut(timeoutTimeInMilli);
Gdx.net.sendHttpRequest(httpRequest, new Net.HttpResponseListener() {
#Override
public void handleHttpResponse(Net.HttpResponse httpResponse) {
String successValue = httpResponse.getResultAsString();
if (successValue.contains("\"total_count\": 0"))//wrong credentials
{
callback.callback("EMPTY");
} else//there was a match yo! should probably have a unique conststraint on username. too hard eff it
{
callback.callback(successValue);
}
}
#Override
public void failed(Throwable t) {
callback.callback("FAILED");
}
#Override
public void cancelled() {
callback.callback("CANCELLED");
}
});
}
}
);
}
});
th.start();
threads.add(th);
}
I'm stumped because the code prints out "Should have switched screens" so it's acting like expected except for the fact that the game is frozen up and the screen switch never actually happens.

The Lazy Way:
on your main game class:
public static ProofOfConcept game;
and your method
public void testTimeToServer(ApiCall api){
watch.start();
api.httpGetWithCallback("(my url)/api/v1/character", new CallBack(){
#Override
public void callback(String resp){
System.out.println("Server Responded");
time = watch.getTime();
watch.stop();
watch.reset();
if(time > timeToDrop){
Main.game.setScreen(new GameOverScreen(false));
System.out.println("Should have switched screen")
}
}
});
}
}
The Right Way
You can create a callback inside your ProofOfConcept class that every frame on the render method checks the result and change the screen.

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();
}

"Bluetooth failes to initialize" when automating Bluetooth steps

I'm trying to create a class that sequentially performs the Bluetooth tasks without user intervention aside from starting the process. In this class an external event calls the overridden method "executeCentral" from there it calls setup() to enable and request permissions. If they complete successfully the initialize() method is called and waits for one second before calling the Bluetooth initialize() which is executed in the EDT. If it runs without exception it calls startScanning() which also waits 1 second before calling Bluetooth startScan() in the EDT. After scanning has started it waits 10 seconds before calling Bluetooth stopScan() in the EDT.
I recreated the project for a clean setup and used the "downloader" in the Codename One Settings. It compiles successfully and runs, but reports an exception on "Bluetooth not initialized"
Any idea on what I am doing wrong? I'm under the impression that all calls must be done in the EDT.
The single form BTDemo compiles and executes each task as a separate user initiated event.
public class UITaskBluetoothEx extends com.crumptech.library.mobile.ui.tasks.UITaskBluetooth {
protected Bluetooth bt = new Bluetooth();
protected Map devices = new HashMap();
public UITaskBluetoothEx() {
super();
}
#Override
public String getReplacement() {
return "UITaskBluetoothEx";
}
protected void showDebug(String message) {
Display.getInstance().callSerially(new Runnable() {
#Override
public void run() {
UIApplication.showDebug("UITaskBluetoothEx " + message);
completed(result(false));
}
});
}
#Override
protected void executeCentral() {
bt = new Bluetooth();
try {
setup();
initialize();
} catch (Exception e) {
showDebug(e.getMessage());
}
}
protected void setup() throws IOException {
if (!bt.isEnabled()) {
bt.enable();
}
if (!bt.hasPermission()) {
bt.requestPermission();
}
}
protected void initialize() {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
Display.getInstance().callSerially(new Runnable() {
#Override
public void run() {
try {
if (!bt.isInitialized()) {
bt.initialize(true, false, "ShopMyLocalStores");
}
startScanning();
} catch (Exception e) {
showDebug(e.getMessage());
}
}
});
}
}, 1000);
}
protected void startScanning() {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
Display.getInstance().callSerially(new Runnable() {
#Override
public void run() {
try {
if (!bt.isScanning()) {
bt.startScan(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
try {
JSONObject res = (JSONObject) evt.getSource();
if (res.getString("status").equals("scanResult")) {
if (!devices.containsKey(res.getString("address"))) {
devices.put(res.getString("address"), res);
}
}
} catch (JSONException e) {
}
}
}, null, true, Bluetooth.SCAN_MODE_LOW_POWER, Bluetooth.MATCH_MODE_STICKY, Bluetooth.MATCH_NUM_MAX_ADVERTISEMENT, Bluetooth.CALLBACK_TYPE_ALL_MATCHES);
stopScanning();
}
} catch (Exception e) {
showDebug(e.getMessage());
}
}
});
}
}, 1000);
}
protected void stopScanning() {
try {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
Display.getInstance().callSerially(new Runnable() {
#Override
public void run() {
try {
if (bt.isScanning()) {
bt.stopScan();
}
} catch (Exception e) {
showDebug(e.getMessage());
}
showResults();
}
});
}
}, 10000);
} catch (Exception e) {
}
}
protected void showResults() {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
Display.getInstance().callSerially(new Runnable() {
#Override
public void run() {
String text = "";
Iterator it = devices.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry) it.next();
text += (pair.getKey() + " = " + pair.getValue() + "\r\n");
}
UIApplication.showDebug(text);
completed(result(true));
}
});
}
}, 1000);
}
}
It looks like some methods currently aren't supported on iOS. These will throw IOExceptions if they are called on iOS. This is a limitation that is baked into the Cordova plugin that we ported. These methods literally return "Unsupported Operation" inside the plugin. I'm not sure if these are just omissions of the plugin, or if they can't be supported. The list of methods that are currently unsupported on iOS are:
isEnabled()
enable()
disable()
mtu()
requestConnectionPriority()
hasPermission()
requestPermission()
isLocationEnabled()
requestLocation()
I have marked these in the javadocs for the Bluetooth class to help identify them. We'll likely have to do something here to clean it up ... perhaps an exception is not the best thing.
In any case, your test app is failing because you call isEnabled() and initialize() inside the same try/catch block. isEnabled throws an exception so it never gets to initialize() and your tests aren't run.
I have adapted your code into my own test case, and made that modification, and it appears to run fine.

making pop up window by using SwingUtilities.invokeLater

I am writing a turn-based game on the internet. I try to pop up a window that should be in front until the input stream is ready. I created smth like this, but it seems that it does not work.
class CustomBlockerDialog extends JDialog {
/**
*
*/
private static final long serialVersionUID = 1L;
public CustomBlockerDialog(Frame owner, String text) {
super(owner, true);
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
setSize(300, 100); // Adjust if needed
setTitle("");
add(new JLabel(text, SwingConstants.CENTER));
}
}
final CustomBlockerDialog block = new CustomBlockerDialog(null, "Not your turn");
SwingUtilities.invokeLater(new Runnable() {//A
#Override
public void run() {
System.out.println("show");
block.setVisible(true);
}
});
boolean one_write_only = true;
while(in.ready()){ /* C*/
if(one_write_only){
System.out.println("waiting server");
one_write_only = false;
}
};
System.out.println("suppose to hide");
SwingUtilities.invokeLater(new Runnable() {//B
#Override
public void run() {
System.out.println("hide");
block.setVisible(false);
}
});
It looks like "A" and "B" are executed after "C" and I have no idea why.
Your problem must be due to "C" being called on the Swing event thread and not in a background thread, since it sounds like "C" is blocking the event thread from running "A". Solution: be sure that "C" is not called on the Swing event thread. Also if this is the case, and this can be tested by running the SwingUtilities.isEventDispatchThread() method, then you don't need all those other runnables.
// note that this all must be called on the Swing event thread:
final CustomBlockerDialog block = new CustomBlockerDialog(null, "Not your turn");
System.out.println("show");
// block.setVisible(true); // !! no this will freeze!
final SwingWorker<Void, Void> worker = new SwingWorker<>() {
public void doInBackground() throws Exception {
boolean one_write_only = true;
while(in.ready()){ /* C*/
if(one_write_only){
System.out.println("waiting server");
one_write_only = false;
}
}
}
}
worker.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChanged(PropertyChangeEvent pcEvt) {
if (pcEvt.getNewValue() == SwingWorker.StateValue.DONE) {
System.out.println("hide");
block.setVisible(false);
// call worker's get() method here and catch exceptions
}
}
});
worker.execute();
// moved to down here since the dialog is modal!!!
block.setVisible(true);
Caveat: code not compiled nor tested. There may be errors present as it was typed off the cuff.
Thanks to Hovercraft Full Of Eels, I created a little different solution which works in my case:
final SwingWorker<Object,Object> worker2 = new SwingWorker<Object, Object>() {
public Object doInBackground() throws Exception {
boolean one_write_only = true;
while(!in.ready()){ /* C*/
if(one_write_only){
System.out.println("waiting server");
one_write_only = false;
}
}
return one_write_only;
}
protected void done() {
try {
block.setVisible(false);
} catch (Exception ignore) {}
}
};
worker2.execute();
block.setVisible(true);

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