In my Eclipse plugin, I have a TableViewer that shows some data. These data is retrieved from a remote REST service. Therefore it takes some seconds to retrieve all items. That's why I wanted to add items to the TableViewer's ContentProvider as soon as I retrieve them. I do my REST query in a separate thread:
Thread retrieveThread = new RetrieveThread();
retrieveThread.run();
In this thread I add items to my TableViewer:
MyView.instance.addItems(items);
And the same method in the class MyView:
public void addItems(List<Result> items) {
ModelProvider.INSTANCE.addItems(items);
resultLabel.setText(ModelProvider.INSTANCE.getItems().size() + " items");
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
#Override
public void run() {
viewer.refresh();
}
});
}
I expected that this procedure will add items to my TableViewer and then refresh the view. But what happens now is that my Eclipse freezes and adds all them items at the same time. How can I do this in a more responsive way?
The issue is that you are calling the run method of your thread directly. This does not spawn a new thread, it just calls the run method in-line. retrieveThread.start() would give you the behaviour you are looking for.
However, there is a better way of doing it (at least more 'eclipse friendly'), and that is to use an Eclipse Job instead of a raw Thread.
Job retrieveJob = new Job("Retrieving Data")
{
#Override
protected IStatus run(IProgressMonitor monitor) {
// do your REST call
ModelProvider.INSTANCE.addItems(items);
// if something goes wrong - don't return ok, obviously
return Status.OK_STATUS;
}
};
retrieveJob.addJobChangeListener(new JobChangeAdapter() {
#Override
public void done(IJobChangeEvent event) {
if(event.getResult().isOK())
{
resultLabel.setText(ModelProvider.INSTANCE.getItems().size() + " items");
viewer.refresh();
}
}
});
// this will run in a background thread
// and nicely integrate with the UI
retrieveJob.schedule();
Related
I am working on a JavaFX desktop application and I have one button that should read from the memory of an embedded device and print that into a JSON. I have implemented a Task that does that, and this Task is passed as argument to a new thread in the button event handler. The problem is, this only works once. After that, even though new threads are generated on button click, the call() method of the Task is never called again. Here is the code:
The Task definition:
Task readValDaemon = new Task<Void>() {
#Override
public Void call() {
//This functions reads from memory and writes the JSON
readDataHI(connection,commandListHI,statusHI);
return null;
}
};
The Thread creation:
readData.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
Thread readValThread = new Thread(readValDaemon);
readValThread.setDaemon(true);
readValThread.start();
}
});
As observed in other answers, a Task is an implementation of FutureTask. From the Task documentation:
As with FutureTask, a Task is a one-shot class and cannot be reused. See Service for a reusable Worker.
So you cannot reuse a task. Second and subsequent attempts to run it will just silently fail.
You could just create a new task directly every time:
private Task<Void> createReadValTask() {
return new Task<Void>() {
#Override
public Void call() {
//This functions reads from memory and writes the JSON
readDataHI(connection,commandListHI,statusHI);
return null;
}
};
}
and then do
readData.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
Thread readValThread = new Thread(createReadValTask());
readValThread.setDaemon(true);
readValThread.start();
}
});
You could also consider using a Service, which is designed for reuse. It basically encapsulates the "create a new task every time" functionality, but adds in a lot of useful UI callbacks. A Service also manages a thread pool for you (via an Executor), so you no longer need to worry that you may be creating too many thread. (The Executor can also be specified, if you want to control it.)
So, e.g.:
Service<Void> readValDaemon = new Service<Void>() {
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
public Void call() {
//This functions reads from memory and writes the JSON
readDataHI(connection,commandListHI,statusHI);
return null;
}
};
}
};
and then
readData.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
readValThread.restart();
}
});
If the mouse is clicked while the service is already running, this will automatically cancel the already running task, and restart a new one. You could add in checks if you wanted, or bind the disable state of readData to the state of the Service, if you wanted.
Task is kind of the wrong tool for this. It's very purposefully only designed to run once because it's a kind of future. It stores the result (in your case null) as a kind of memoization to avoid doing expensive operations more times than is necessary. So Task is best suited for situations where an expensive computation must be done just once, and usually you would want a result from it at some point down the line.
The documentation for Task is very thorough so I would give that a read.
In your case, just use a plain Runnable. You can use a lambda expression:
readData.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event)
{
Thread readValThread = new Thread(() -> readDataHI(a, b, c));
readValThread.setDaemon(true);
readValThread.start();
}
});
As an aside, creating threads manually isn't considered very good practice in modern Java. Strongly consider an ExecutorService instead.
I have an interface method which is supposed to return a Future object.
Future<Result> doSomething()
The implementation of this method shows some ui (javafx).
One of the ui elements has a listener, that needs to be called in order to receive the actual result, I need.
How do I achieve this?
Is there a better solution?
Here an example action I need to wait for:
// this is some framework method I cannot change
#Override
public Data execute(Data data) {
Future<Data> dataFuture = handler.doSomething(data);
// this should basically wait until the user clicked a button
return dataFuture.get();
}
// handler implementation
public Future<Data> doSomething(Data data) {
// the question is how to implement this part, to be able to
// return a future object
Button button = new Button("Wait until click");
// create thread that waits for the button click ?!????
// modify incoming data object when the button was clicked
// somehow create the Future object that's bound to the button click
return future;
}
This is what I want to achieve:
my method doSomething shows a new scene(ui) with a button on it
and returns immedeately the future object
future.get() waits until the user pressed the button
limitations: it has to be done with no extra library and on >=Java7
Use a javafx.concurrent.Task. It derives from FutureTask. There are extensive examples in the linked javadoc on Task usage.
Oracle also provide a tutorial which discusses Task usage:
Concurrency in JavaFX
I think this is what you want, but I may have understood the question, if so, please edit the question a bit to clarify requirements (perhaps with an mcve). The bit that makes me a little unsure is the part in your title "waiting for ui event?", I'm not quite sure what that means in this context.
This is a solution I was searching for. It's not very nice, since the Thread.sleep doesn't convince me.
but now you propably get an idea of what I want to achieve
// make sure this is not called on the ui thread
public Future<Data> doSomething(Data data) {
WaitingFuture future = new WaitingFuture(data);
Platform.runLater(() -> {
Button button = new Button("Wait until click");
button.setOnAction(future);
// show button on ui...
});
favouriteExecutorService.submit(future);
return future;
}
static class WaitingFuture extends Task<Data> implements EventHandler<ActionEvent> {
private Data data;
WaitingFuture(Data originalData) {
this.data = originalData;
}
private Data waitingData;
#Override
public void handle(ActionEvent event) {
waitingData = data.modify();
}
#Override
protected Data call() throws Exception {
while (waitingData == null) {
Thread.sleep(100);
}
return waitingData;
}
}
I have some code which executes a download in a separate thread, created so that the JFrame GUI will continue to update during the download. But, the purpose is completely defeated when I use Thread.join(), as it causes the GUI to stop updating. I need a way to wait for the thread to finish and still update the GUI.
You can have the task that does the download also fire an event to the GUI.
For example:
Runnable task = new Runnable() {
public void run() {
// do your download
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// call some method to tell the GUI that the download finished.
}
});
}
};
and then to run it, either use an Executor (preferred method) or a raw thread:
executor.execute(task);
or
new Thread(task).start();
As pointed out in the comments, you'd generally use a SwingWorker to do this kind of thing but you can also do the manual approach outlined above.
SwingWorker provides a doInBackground method where you would stick your download logic in, a done method where you would stick in code to notify the GUI that the download finished and a get method to get the result of doInBackground (if there was one).
E.g.,
class Downloader extends SwingWorker<Object, Object> {
#Override
public Object doInBackground() {
return doDownload();
}
#Override
protected void done() {
try {
frame.downloadDone(get());
} catch (Exception ignore) {
}
}
}
(new Downloader()).execute();
I'm trying to update the tab being displayed, however it seems to wait until the end of the method and then update. Is there a way to make the tab being displayed update immediately?
Here is an example of the code where I'm having this issue:
private static void someButtonMethod()
{
Button = new JButton("My Button");
Button(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
tabs.setSelectedIndex(1);
// Do some other things (In my case run a program that takes several seconds to run).
runProgram();
}
});
}
The reason for this is that the method is being executed in the Event Dispatch thread, and any repaint operations will also occur in this thread. One "solution" is to update the tab index and then schedule the remaining work to be invoked later on the EDT; this should cause the tab state to be updated immediately; e.g.
public void actionPerformed(ActionEvent evt) {
tab.setSelectedIndex(1);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Do remaining work.
}
});
}
EDIT
Per your comment below an example of how to invoke a SwingWorker in order to call your runProgram method would look something like this:
// Typed using Void because runProgram() has no return value.
new SwingWorker<Void, Void>() {
protectedVoid doInBackground() {
runProgram();
return null; // runProgram() doesn't return anything so return null.
}
protected void done() {
// Called on the EDT when the background computation has completed.
// Could insert code to update UI here.
}
}.execute()
However, I sense a bigger problem here: The fact that you are seeing a significant delay in updating the tab makes me think you are performing long running calculations on the EDT. If this is the case you should consider performing this work on a background thread. Take a look at the SwingWorker class.
We have a ViewerFilter for a TableViewer that is a little slow, so to try to give the impression of awesomeness, we wanted to have the viewer wait 500 milliseconds before refreshing the window (otherwise, it was blocking after every key stroke).
Not having any clue what I was doing, I tried creating a class that would check if System.currentTimeMillis() was greater then the time of the last key stroke + 500 from a different thread. This just caused an Invalid thread access exception to be thrown, so I'm lost.
Edit: I was able to use TableViewer.getTable().getDisplay().asyncExec() to sidestep the invalid thread problem, but I don't like my solution, and would love to hear other suggestions.
You might want to try to turn off redraw while updating the viewer.
Viewer.getControl().setRedraw(false);
// update
Viewer.getControl().setRedraw(true);
It can sometimes give a better user experience. You can also schedule a ui-job that you cancel when the user hits a new key or modifies the text. E.g.
class RefreshJob extends WorkbenchJob
{
public RefreshJob()
{
super("Refresh Job");
setSystem(true); // set to false to show progress to user
}
public IStatus runInUIThread(IProgressMonitor monitor)
{
monitor.beginTask("Refreshing", ProgressMonitor.UNKNOWN);
m_viewer.refresh();
monitor.done();
return Status.OK_STATUS;
};
}
and then reschedule the refresh in a separate job.
private RefreshJob m_refreshJob = new RefreshJob();
private Text m_filterText;
void hookModifyListener()
{
m_filterText.addModifyListener(new ModifyListener()
{
public void modifyText(ModifyEvent e)
{
m_refreshJob.cancel();
m_refreshJob.schedule(500);
}
});
}
If the user hits the Enter key you can schedule a refresh job without the delay,
Just wrap your code in display.syncExec, something like this:
Display.getDefault().asyncExec(new Runnable() {
public void run() {
// check refresh time
// refresh.
}
});
You may want to look in to asyncExec too, if syncExec does not meet your needs.