I update the gui of my eclipse rcp application with asyncExec() in the run()-method of a job.
Job job = new Job("The Job") {
#Override
protected IStatus run(IProgressMonitor monitor) {
try {
monitor.beginTask("", IProgressMonitor.UNKNOWN);
Display.getDefault().asyncExec(new Runnable() {
#Override
public void run() {
\\update gui
}
});
...
PlatformUI.getWorkbench().getProgressService().showInDialog(Display.getCurrent().getActiveShell(), job);
job.schedule();
I am trying to show the user some indication of how long it is going to take with a progress monitor and the showInDialog method but i would also like to have a busy cursor in the first few moments before the dialog appears. How does it work in this case with the BusyIndicator? I also have the problem that i can't cancel or exit the progress dialog (cancel button and exit button don't show any effect).
Job is not suitable for passing to the progress service like this.
You should just call job.schedule() and let it run. If you call
job.setUser(true);
before scheduling the job it will display a progress dialog of its own after a few seconds.
Your job should call monitor.worked(1) regularly to update the progress monitor. The job should call monitor.isCanceled() to check for the cancel button.
If your job is mostly doing GUI work then use UIJob to run the whole job in the UI thread (but this is more likely to slow down the UI).
Related
I have a simple app which updates the data in the background and while it updates, it disables all the other buttons and enables a TextArea to show the progress.
Steps:
Disable all the other buttons in the mainUI (Button name: plotButton)
Enable a TextArea showing that the updating has started (TextArea name: infoLogTextArea)
Then only start the update method (update() throws Exceptions).
Here is the code:
#FXML
public void handleUpdateButton() {
infoLogTextArea.setVisible(true);
infoLogTextArea.appendText("Please wait while downloading data from internet.....\n");
plotButton.setDisable(true);
updateButton.setDisable(true);
if(c!=null) {
Runnable task = new Runnable() {
#Override
public void run() {
// Thread.sleep(10000); -> sleep for 10secs
Platform.runLater(new Runnable() {
#Override
public void run() {
try {
c.updateData();
infoLogTextArea.appendText(c.getErrorLog().toString());
plotLabel.setText(c.getCityData().size()+" cities found and updated from internet");
infoLogTextArea.appendText("Successfully updated the data from Internet\n");
}catch (IOException e) {
infoLogTextArea.setText("Couldnot update the data from web: "+e.getMessage()+"\n");
}
finally {
plotButton.setDisable(false);
updateButton.setDisable(false);
}
}
});
}
};
new Thread(task).start();
}else {
System.out.println("c not initialized");
}
}
Now the code works well but sometimes steps 1 and 2 are not executed and it starts step 3 (updating) which can freeze the program.
If I put Thread.sleep(10 secs) in between step 2 and 3 then it works completely fine. (it is commented in the code)
But can anybody explain what is going on behind the scenes and why Platform.runLater() doesn't work all the time?
A JavaFX application runs on the Application thread, which handles all the UI elements. This means that if you click Button A and clicking that button starts method A that takes 5 seconds to complete, and then one second after clicking that button, you try to click Button B which starts method B, method B won't start until method A finishes. Or possibly Button B won't even work until method A finishes, I'm a little fuzzy on the detail there.
A good way to stop your application from freezing is to use Threads. To fix the above scenario, clicking Button A will start method A that starts a new Thread. Then the Thread can take as long as it wants to complete without locking up the UI and preventing you from clicking Button B.
Now, say something in method A needed to be on the application thread, for example, it updated a UI component, like a Label or a TextField. Then inside your Thread in Method A you would need to put the part that affects the UI into a Platform.runLater(), so that it will run on the Application Thread with the rest of the UI.
What this means for your example is that you have two options.
1. Don't use threads at all, since you don't want the user to be interacting with the UI while the updates are happening anyway.
2. move c.updateData() out of the Platform.runLater() like this:
Runnable task = new Runnable() {
#Override
public void run() {
c.updateData();
Platform.runLater(new Runnable() {
#Override
public void run() {
try {
infoLogTextArea.appendText(c.getErrorLog().toString());
plotLabel.setText(c.getCityData().size()+" cities found and updated from internet");
infoLogTextArea.appendText("Successfully updated the data from Internet\n");
}catch (IOException e) {
infoLogTextArea.setText("Couldnot update the data from web: "+e.getMessage()+"\n");
}
finally {
plotButton.setDisable(false);
updateButton.setDisable(false);
}
}
});
}
};
Either one of those will work, but what you're doing right now is you're on the application thread, and then you start another thread whose only purpose is to run something on the application thread.
The documentation of the Platform class explain everything very well :
public static void runLater(Runnable runnable)
Run the specified Runnable on the JavaFX Application Thread at some
unspecified time in the future. This method, which may be called from
any thread, will post the Runnable to an event queue and then return
immediately to the caller. The Runnables are executed in the order
they are posted. A runnable passed into the runLater method will be
executed before any Runnable passed into a subsequent call to
runLater. If this method is called after the JavaFX runtime has been
shutdown, the call will be ignored: the Runnable will not be executed
and no exception will be thrown. NOTE: applications should avoid
flooding JavaFX with too many pending Runnables. Otherwise, the
application may become unresponsive. Applications are encouraged to
batch up multiple operations into fewer runLater calls. Additionally,
long-running operations should be done on a background thread where
possible, freeing up the JavaFX Application Thread for GUI operations.
This method must not be called before the FX runtime has been
initialized. For standard JavaFX applications that extend Application,
and use either the Java launcher or one of the launch methods in the
Application class to launch the application, the FX runtime is
initialized by the launcher before the Application class is loaded.
So use the runLater to only update any UI elements on a non JavaFX thread and leave any heavy job to sit on the background thread.
I'm running my application in headless mode and for some execution i need to display a dialog in between for some specified time.
Code Sample
Display.getDefault().syncExec(new Runnable() {
#Override
public void run() {
//Jface Dialog code
RuntimePauseDialog dlg = new RuntimePauseDialog();
dialogResult = dlg.open();
}
});
The above code will be called multiple times and for the first time the Dialog appears. From 2nd time onwards the dialog doesn't show up. Moreover run() will not be executed at all and freezes. Inside syncExec() there is a Runnable lock which gets initialized and calls wait(), which waits forever (Application freezes)
I think this has something to do with Threads.
Note : The same code displays the dialog (multiple times) correctly when run from UI mode. The problem is only in headless mode.
Tried this suggestion from Stackoverflow, but since i'm running in headless mode, there will be no Workbench created and cannot use that.
syncExec (and asyncExec) relies on there being a main Display.readAndDispatch loop running in the UI thread. In headless mode this is not the case so this is simply not going to work at all.
I am developing a Bluetooth application.In that I have 1 button, on click of the button I am starting a thread.Inside the thread, I am discovering and connecting ble devices.Repeated click of the button causing the UI to hang.
Code I am using to create the thread is:
new Thread(new Runnable() {
#Override
public void run() {
//do bluetooth stuffs
}
}).start();
I am not stopping this thread anywhere.
I don't know what is causing the UI to hang please help me.
Do you mean that if you keep smashing the button repeatedly (without waiting for the task to finish), then the ui lags? Or when you press the button, wait a bit, then press again.
If it's the first case (where you're mashing the button in quick succession), try this: If you set some boolean flag when you first start the process, then each time you press the button check if that flag is set to true, and only execute the search if the flag is false. Not sure if this is your issue but it's worth a shot?
For the Android, you can use handler instead thread or handle your thread using handler is a better way, for example, you can use like
new Handler().post(new Runnable() {
#Override
public void run() {
}
});
if you want to use the main thread then use like.
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
}
});
for information, you can refer this link
difference between Thread and Handler
I would advise you to use a thread pool instead. Resources are limited.I didn't understand why are you creating a new thread for every button press. a bunch of threads banging for a resource might freeze Your app, or there could be implementation related issues like a deadlock, thread contention, or thread starvation which will definately prompts to freeze your application.
I'm quite new to ZK and the concept of event queues. What I'm trying to do is run a long operation in the server and update the UI of the progress in real-time, instead of blocking the UI while the long operation runs. So for example, if there are 3 tasks (this number is not fixed) to do in the long operation, it should update the UI by updating a "log trace" textbox and a progress bar that same number of times.
My code structure looks like:
if (EventQueues.exists("longop")) {
print("It is busy. Please wait");
return; //busy
}
EventQueue eq = EventQueues.lookup("longop"); //create a queue
String result;
//subscribe async listener to handle long operation
eq.subscribe(new EventListener() {
public void onEvent(Event evt) {
if ("doLongOp".equals(evt.getName())) {
//simulate a long operation
doTask1();
eq.publish(new Event("printStatus", null, "Task1 completed."));
doTask2();
eq.publish(new Event("printStatus", null, "Task2 completed."));
doTask3();
eq.publish(new Event("printStatus", null, "Task3 completed."));
result = "success";
eq.publish(new Event("endLongOp")); //notify it is done
}
}
}, true); //asynchronous
//subscribe a normal listener to show the resul to the browser
eq.subscribe(new EventListener() {
public void onEvent(Event evt) {
if("printStatus".equals(evt.getName())) {
printToTextbox((String)evt.getData()); //appends value to the log textbox
}
if ("endLongOp".equals(evt.getName())) {
print(result); //show the result to the browser
EventQueues.remove("longop");
}
}
}); //synchronous
eq.publish(new Event("doLongOp")); //kick off the long operation
This didn't work. All the printStatus events happen AFTER the long operation is finished. The only thing this fixed is that the UI is not getting blocked whenever the long operation runs. I was assuming that since the long operation thread is asynch, it will still send the events to the queue and the synch UI thread will be able to handle them as soon as they happen. So after several hours of trial and error, and after noticing that the server push is NOT used in a desktop scope queue, I changed the scope to application and explicitly enabled server push:
EventQueue<Event> eq = EventQueues.lookup("longop", EventQueues.APPLICATION, true);
desktop.enableServerPush(true);
it just worked. I know that ZK CE only has the client polling, which is fine for my use case. But why is it that in desktop scope, server push is not used? How can we accomplish such task if we don't want the queue to be shared application-wide? I want each desktop to have their own event queue.
It might also be worth mentioning that I have enabled the event thread. And that I tried disabling it but the result was the same. So it looks to me that it doesn't affect my problem.
Any help is greatly appreciated.
PS: I am using ZK CE 7.0.3
There are many possible solutions for your situation.
Please take a look at this section of ZK documents.
You can use the piggyback, but when the user doesn't do anything, you also have no updates on the screen.
So I suggest go for the echoEvents.
So you have to do task 1, update screen and echo onTask2.
In OnTask2 do your stuff, update screen and echo onTask3.
And for onTask3 do task 3 and update the screen.
Edit :
The scope doesn't have to be application scope. The application scope event queue has already server push build in (And I believe Session also). For the desktop you have to do it manually(or other approach). (your desktop.enableServerPush isn't needed for application scope)
If you want to work simple with the eventqueue look here.
Use the EventQueue.subscribe(EventListener, EventListener) what is the async and sync Eventlistener.
The only thing is, in the Sync listener you need to call your Task 2 with again the sync listener for refreshing GUI and start task 3 in same way.
The other way is passing the desktop to the async listener so you can enable (and disable) server push there.(async listener never has reference to desktop, it's a complete new thread)
I'm experiencing a strange behavior on my Android app.
I want to schedule a fixed-rate operation that saves the properties of a "player" object. During the application flow, the user may change it's settings but the saving is performed every 2 minutes by this little task.
The task is run by a static ScheduledExecutorService, initialized when the app starts:
private static ScheduledExecutorService threadExecutor;
//...
public static void initialize()
{
if (!initialized)
{
threadExecutor = Executors.newScheduledThreadPool(1);
// start the thread that will perform a scheduled check for unsaved player state
threadExecutor.scheduleAtFixedRate(new Runnable() {
#Override public void run() {
// ... the saving operation ...
}
}, 0, 120, TimeUnit.SECONDS);
initialized = true;
}
}
When I'm in debug mode, this thing works both when the app is in the foreground and in the background too, as espected.
Problems come when I switch to RELEASE mode: once the app is in the background (by pressing the home button, for example), this thread stops and the operation is not repeated anymore!
Is there any documentation about this (unexpected?) behavior?
Try using AlarmManager, to run a task every 'x' minutes.
Please refer to this for more information.
And here is an example of an AlarmManager that runs every 2 minutes.