i'm tried many suggestions but nothing works! When i call Thread.sleep() in background thread, main thread also freezes for this time (Animation frame drop) :(
Version 1:
public void UpdateChannels(final ArrayList<Channel> channels)
{
new AsyncTask<ArrayList<Channel>, Object[], Void>()
{
#Override
protected Void doInBackground(ArrayList<Channel>... arrayLists)
{
for(Channel channel : arrayLists[0])
{
Integer chid = new Integer(channel.arfcn);
ChannelRect channelRect = Channels.get(chid);
publishProgress(new Object[] {channelRect, channel.dBm});
try
{
Thread.sleep(1000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
return null;
}
#Override
protected void onProgressUpdate(Object[]... values)
{
ChannelRect channelRect = (ChannelRect) values[0][0];
int value = (Integer)values[0][1];
channelRect.setValue(value);
channelRect.Animate();
}
}.execute(channels);
}
Version 2:
public void UpdateChannels(final ArrayList<Channel> channels)
{
new Thread(new Runnable()
{
#Override
public void run()
{
for(final Channel channel : channels)
{
int chid = new Integer(channel.arfcn);
final ChannelRect channelRect = Channels.get(chid);
if(channelRect != null)
{
CurrentActivity.runOnUiThread(new Runnable()
{
#Override
public void run()
{
channelRect.setValue(channel.dBm);
channelRect.Animate();
}
});
try
{
Thread.sleep(500);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
}).start();
}
And i tried same thing with post and Handler. Same thing :( When Thread.sleep(500) arrives animation freezes :(
What i'm doing wrong?
UPDATE 1
Found heavy place that slows UI thread:
I have horizontal LinearLayout that stores 175 ChannelRect views. When i call animate (that starts AlphaAnimation all okay, but when i setValue() to ChannelRect it must change his size:
public void setValue(int value)
{
_value = value;
setOpacity((byte)255);
CurrentLayoutParams.height = (int)((-110 - value) * ycoeff * -1);
requestLayout();
}
This is slow downs main UI thread. Why? setValue affects only one view in this LinearLayout not all...
p.s. removing requestLayot() line remove slowdowns but ChannelRect size not changes :(
The reason why animation freezes it's a requestLayout() in ChannelRect.
Main LinearLayout that contains many (175+) custom views trying to redraw every children on requestLayout() called from updated child.
I'm changed my ChannelRect for drawing rectangle inside view and now calling Invalidate() instead requestLayout():
public void setValue(int value)
{
_value = value;
setOpacity((byte)255);
invalidate();
}
#Override
protected void onDraw(Canvas canvas)
{
canvas.drawRect(0,getHeight() - (int)((-110 - _value) * ycoeff * -1),size,getHeight(), pnt);
}
Related
The application reacts on actions which occur on gamepad. When button is pressed something happens on UI. But I ran at the issue with app hangs up or "java.lang.IllegalStateException: Not on FX application thread" exception.
In order to fix it I tried the following approaches: Platform.runLater() and Task usage. But it didn't help.
Here is the problem code:
public class GamepadUI extends Application{
private static final int WIDTH = 300;
private static final int HEIGHT = 213;
private Pane root = new Pane();
private ImageView iv1 = new ImageView();
private boolean isXPressed = false;
#Override
public void start(Stage stage) throws Exception {
initGUI(root);
Scene scene = new Scene(root, WIDTH, HEIGHT);
stage.setScene(scene);
stage.setResizable(false);
stage.show();
}
public void pressBtn() {
if(!isXPressed) {
iv1.setVisible(true);
isXPressed = true;
}
}
public void releaseBtn() {
if(isXPressed) {
iv1.setVisible(false);
isXPressed = false;
}
}
private void initGUI(final Pane root) {
Image image = new Image(Props.BUTTON);
iv1.setImage(image);
iv1.setLayoutX(198);
iv1.setLayoutY(48);
iv1.setVisible(false);
root.getChildren().add(iv1);
runTask();
}
public void runTask() {
Task task = new Task<Void>() {
#Override
protected Void call() throws Exception {
initStubGamepad();
return null;
}
};
new Thread(task).start();
}
public static void main(String[] args) {
launch(args);
}
public void initStubGamepad() {
Random rnd = new Random();
try {
while (true) {
if (rnd.nextInt(30) == 3) {
pressBtn();
} else if (rnd.nextInt(30) == 7) {
releaseBtn();
}
}
} catch (Exception ex) {
System.out.println("Exception: " + ex);
}
}
}
initStubGamepad() emulates gamepad buttons activity polling. When user presses any button (rnd.nextInt(30) == 3) - an image appears on the UI. When user releases that button (rnd.nextInt(30) == 7) - an image disappears from the UI.
In case above java.lang.IllegalStateException: Not on FX application thread occurs. If you change runTask() to something like this:
Platform.runLater(new Runnable() {
#Override
public void run() {
initStubGamepad();
}
});
Then app will hang or even main UI won't appear at all, but gamepad activity continues.
What I want is just to show/hide different images when some activity is detected on gamepad (btw, there's no way to monitor gamepad activity except for gamepad polling in an infinite loop). What did I wrong
Explanation
In the first scenario, when you are using
Task task = new Task<Void>() {
#Override
protected Void call() throws Exception {
initStubGamepad();
return null;
}
}
Inside initStubGamepad(), which is running on a Task, you are trying to update the UI components inside pressBtn() and releaseBtn() methods, which is why you are facing a
java.lang.IllegalStateException: Not on FX application thread
because all the UI updates must occur on the JavaFX thread
In the second scenario, when you are using
Platform.runLater(new Runnable() {
#Override
public void run() {
initStubGamepad();
}
});
the UI doesnt appear, because you have an infinite loop inside the initStubGamepad(), which puts the JavaFX application thread run on an infinite loop
Solution
By the time you have reach here, you must have already found the solution. In case you haven't, try try to put the update the Javafx components on the UI thread. So, instead of calling initStubGamepad() inside Platform.runLater, try calling pressBtn() and releaseBtn() inside it.
Try using
while (true) {
if (rnd.nextInt(30) == 3) {
Platform.runLater(() -> pressBtn());
} else if (rnd.nextInt(30) == 7) {
Platform.runLater(() -> releaseBtn());
}
}
or you may also use
public void pressBtn() {
if(!isXPressed) {
Platform.runLater(() -> iv1.setVisible(true));
isXPressed = true;
}
}
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.
I have a thread that is used to spin an imageview as other things happen in the background. It works great for spinning, but I've noticed as I turn it on and off (based on the spinningMain variable) the button starts to spin faster and faster and faster.
I'm unsure what the cause of this is as the thread is told to sleep every 100ms.
If it helps I also have another thread (runnable thread) which runs the main code in between, which I was wondering whether it was disrupting the thread?
The imageView update thread is:
final Thread t = new Thread() {
#Override
public void run() {
try {
while (!isInterrupted()) {
Thread.sleep(100);
runOnUiThread(new Runnable() {
#Override
public void run() {
if (spinningMain == true) {
// update TextView here!
if (spinningAngleMain >= 360) {
spinningAngleMain = 0;
} else {
spinningAngleMain += 5;
}
imageButton.setRotation(spinningAngleMain);
}
}
});
}
} catch (InterruptedException e) {
}
}
};
t.start();
And the Second thread is pretty much made as follows
new Thread(new Runnable() {
#Override
public void run() {
if (searchStateButton == true) {
//do all my stuff
}
My boolean variables are set to volatile if that helps as well.
In my GUI I have a PDF file creation operation. The operation can take up to 10-15 seconds to complete. When I start the operation, I attach a listener to it. The listener changes the cursor and disables the GUI, until the operation completes.
I would also like to add a progressbar, so the users will have a idea when it is going to complete.
Created a method startProgressBar() and called it from the start of the operation method.
See Below:
private void startSavePdfOperation() {
startProgressBar();
saveOp = new AplotSaveOperation(appReg.getString("aplot.message.SAVETOPDF"), "PDF", session);
saveOp.addOperationListener(new MyOperationListener(this) {
startProgressBar Method - See Below:
public void startProgressBar() {
Shell shell = new Shell(getShell());
shell.setSize(260, 120);
final ProgressBar bar = new ProgressBar(shell, SWT.SMOOTH);
bar.setBounds (20, 20, 200, 20);
shell.open();
final int maximum = bar.getMaximum();
new Thread(new Runnable() {
public void run() {
for (final int[] i = new int[1]; i[0] <= maximum; i[0]++) {
try {Thread.sleep (100);} catch (Throwable th) {}
if (Display.getDefault().isDisposed()) return;
Display.getDefault().asyncExec(new Runnable() {
public void run() {
if (bar.isDisposed ()) return;
bar.setSelection(i[0]);
}
});
}
}
}).start();
The code above created the ProgressBar. The issue is that the operation would end well before the progressbar indicator was close to ending.
Question: Is this because in the method I am creating a new thread and the indicator is updating according to the new thread and not the operation thread?
Question: Is it possible to create a new thread that watches the GUI thread and updates the progressbar accordingly?
Read a article suggesting using ProgressMonitorDialog with IRunnableWithProgress.
Method startProgressBar using ProgressMonitorDialog - see below:
public void startProgressBar() {
ProgressMonitorDialog dialog = new ProgressMonitorDialog(getShell());
try {
dialog.run(true, true, new IRunnableWithProgress(){
public void run(IProgressMonitor monitor) {
monitor.beginTask("Some nice progress message here ...", 100);
** getThread(); **
monitor.done();
}
});
}
catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void getThread() {
new Thread etc.. etc...
}
It seems that it will have the same issues with threading and updating as the code above.
Question: So now I am thinking can I just add or update the ProgressBar to my existing Listener
OperationListener Code - see below:
public abstract class MyOperationListener implements InterfaceAIFOperationListener {
AplotCreatePDFDialog w = null;
public MyOperationListener(AplotCreatePDFDialog win) {
w = win;
}
public void startOperation(String startMessage) {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
w.getShell().setCursor(new Cursor(Display.getCurrent(), SWT.CURSOR_WAIT));
w.recursiveSetEnabled(getShell(), getShell().getEnabled());
w.getShell().setEnabled(!getShell().getEnabled());
}
});
}
public void endOperation() {
try {
endOperationImpl();
}
finally {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
w.getShell().setCursor(new Cursor(Display.getCurrent(), SWT.CURSOR_ARROW));
w.recursiveSetEnabled(getShell(), true);
w.getShell().setEnabled(!getShell().getEnabled());
w.close();
}
});
}
}
abstract protected void endOperationImpl();
} // end class MyOperationListener
Thanks for any help you can give me with this.
EDIT
Baz, your answer below is exactly what the question asked, so thank you for answering.
But I am starting to think that what I am trying to do is not possible.
When my operation starts, I wanted the progress bar indicator to start and when my operation ended I wanted the indicator be at the end and the monitor would close.
I thought there might bee a way to use my listener to add the progressbar. Something like the following.
public void startOperation(String startMessage) {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
->monitor.beginTask("Creating PDF File(s)", IProgressMonitor.UNKNOWN);<-
w.getShell().setCursor(new Cursor(Display.getCurrent(), SWT.CURSOR_WAIT));
w.recursiveSetEnabled(getShell(), getShell().getEnabled());
w.getShell().setEnabled(!getShell().getEnabled());
}
});
}
public void endOperation() {
try {
->monitor.worked(1);<-
endOperationImpl();
}
finally {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
w.getShell().setCursor(new Cursor(Display.getCurrent(), SWT.CURSOR_ARROW));
w.recursiveSetEnabled(getShell(), true);
w.getShell().setEnabled(!getShell().getEnabled());
->monitor.done();<-
w.close();
}
});
}
}
abstract protected void endOperationImpl();
} // end class MyOperationListener
But I am starting to see that the ProgressBar has to have some sort of measurement to display the indicator correctly.
I would be happy if the indicator just went back and forth and the monitor would close at the end of the operation.
Why not use ProgressMonitorDialog?
Here is a related answer from me showing a simple example.
This is what it looks like:
If you are not sure about the workload, use this code:
monitor.beginTask("Copying files", IProgressMonitor.UNKNOWN);
It will show the idle bar while running.
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();
}