I am currently working on developing an IDEA extension that embeds JCEF for frontend interaction. Once the user clicks on a button, the implemented onQuery method in MessageRouterHandler would be executed, and a time-consuming task would be performed.
The time-consuming task is implemented in a method (asynchronous on a thread other than the main thread in case the UI to be frozen) and is wrapped in a form like
private void task(CefBrowser browser, CefQueryCallback callback) {
ApplicationManager.getApplication().executeOnPooledThread(() -> {
...DETAILED IMPLEMENTATION FOR THE TASK...
});
}
The runnable only contains the operation related to file reading (selected by the user clicking on another button on the JCEF webview) and has nothing to do with the IDE interface so I did not add runWriteAction or runReadAction here.
Since the task might take a longer time, I would like to add a button for the user's cancelling order. However, I am quite new to extension development, and could anyone help me to figure out how to implement the cancel function? Great great many thanks for any suggestions.
A time consuming task should be performed under a progress indicator, see com.intellij.openapi.progress.ProgressManager. During the task you can then call com.intellij.openapi.progress.ProgressManager#checkCanceled to see if the task has been canceled by the user. This call is supposed to be cheap, so you can call it often to make sure your process responds promptly to the users' request for cancellation.
Related
first of all I'm sorry about my English level, I'm Spanish.
I have a little problem with a progressBar in SWT application:
I have 1 Class(The application (SWT)) with all controls(progressBar, textboxes, combos, etc).
I have 1 Thread class who makes a file upload to an FTP server.
My problem is, I'm getting invalid thread access when I try to update my ProgressBar.Selection(int) from my UploadThread.
I'm trying hard to solve this problem, with Timertask(I wanna upload my progressBar every second), with events (an Event fires when UploadThread stay active) but it didn't work.
I hope you can help me with this problem.
When you use SWT, you must remember that all access to SWT objects must be performed in the SWT UI Event thread. There are a few exceptions to this, but they are typically noted in the API or rather obvious.
To make any changes to a control use the following template:
c.getDisplay().asyncExec(new Runnable() {
#Override
public void run() {
if (c.isDisposed()) return;
// ...
}
});
This will run the Runnable object in the event thread at the soonest possible time. You can even use this construct in the event thread itself, to postpone work for later - usually to get a rapid UI response.
The if (c.isDisposed()) return; construct is here to guard against the situation where the control is disposed in the time between asyncExec(...) and run() are executed.
If you need to wait for the result to be performed, use syncExec(...) instead of asyncExec(...).
How are you creating your upload thread?
If your thread implements IRunnableWithProgress and is run by a class that implements IRunnableContext, your progress bar should be able to run in a separate thread fine.
Just specify true for the fork parameter on the run method.
The run method on IRunnableWithProgress provides an IProgressMonitor for your thread to update.
I have been reading introductory material on Concurrency in Java as well as multi-threading techniques specific to the Java Swing GUI. I am currently unsure on what the best approach to use for my situation is. My situation is the following:
I am developing a program where one piece of its functionality is to listen to a users speech using code from a voice recognition API while the user remains on that particular GUI screen. Every word that the voice recognition detects will be added into a Java Swing Text Field on the UI in real-time. It is also very important that every word detected is added to the Text Field so it is important that the voice recognition thread runs until the user chooses to quit.
My code is currently contained within a method in a dedicated class.
public class VoiceRecognitionCore
{
public void RunVoiceRegonition() throws VoiceRecognitionException
{
//Voice recognition code here
}
}
What would be the most efficient and safest way to have this thread constantly running and how can I give it access to the text field on the UI.
Thank you for your time
I will recommend you to have a look at SwingWorkers here
From the pages of docs.oracle.com:
SwingWorker provides a number of communication and control features:
•The SwingWorker subclass can define a method, done, which is automatically invoked on the event dispatch thread when the background task is finished.
•SwingWorker implements java.util.concurrent.Future. This interface allows the background task to provide a return value to the other thread. Other methods in this interface allow cancellation of the background task and discovering whether the background task has finished or been cancelled.
•The background task can provide intermediate results by invoking SwingWorker.publish, causing SwingWorker.process to be invoked from the event dispatch thread.
•The background task can define bound properties. Changes to these properties trigger events, causing event-handling methods to be invoked on the event dispatch thread.
One possible way of going about it would be to run your voice recognition on a seperate thread and then, when you need to update your GUI, just make use of SwingUtilities.invokeLater(Runnable runnable) to update your GUI.
I have a swing worker which configures a serial port connection. This process takes an indeterminable time to complete, sometimes 1 minute sometimes a lot more.
My problem arises when users click a button that needs configuration data whilst the worker thread is still configuring.
I would like to know how to execute a user's request only if the worker thread has completed. Else if worker thread in still alive, I want execution to wait until worker thread has finished.
add PropertyChangeListener to SwingWorker, then you can to determine of status from SwingWorker
have to implemented get() in the SwingWorker's method done(), otherwise isn't possible to get an exeption(s) from invoked methods
depend of code but I doubt that is possible to change setting, untill current thread ended, better could be to SwingWorker#cancel() and then to restart this instance
You could check in your buttons ActionEvent if task.isDone() - where task is your SwingWorker - and continue only if it is true. But you might want to show a popup or something, otherwise the user might get confused why nothing is happening.
Another simple solution is to expose the button and disable it while the task is running and enable it again when it's finished. Then the user can't click the button until it's ready.
I have a MouseListener Thread where this method is called each time there is a click:
public void mousePressed(MouseEvent event){
//my Logic here
}
Now what happens is that there are so many clicks in little time, while my logic here, takes more time to process. What i was expecting is that further clicks will be discarded and this method will continue on the latest upcoming clicks.
But what is happening that while the logic is processing, the incoming clics are queued and even when clicking is stopped, qued clicks keep calling this mousePressed method, as a result i have multiple delayed executions.
Unfortunatelly i do not have control over the Listener (why it ques and keep sending delayed clicks), so in such a scenario, can you tell me what is the efficient way of handling such that i do not make a que due to delay in my processing.
I think the most obvious way would be using another thread, but i am wondering this might trigger so many threads in little time, or is there any way i can lock just one thread while the rest of clicks just go through empty loop?
Updates: See Phillip's comments as to why this doesn't work for Swing!
It's unfortunate that you don't have access to the eventListener. The ideal solution would be to unregister the callback while it is being executed.
Here's a solution that emulates unregistering the callback function while it is being executed, assuming you want queued clicks to go away:
private AtomicBoolean engaged = new AtomicBoolean(); // thread-safe boolean
public void mousePressed(MouseEvent event){
if (!engaged.get()) {
engaged.set(true);
// your logic here
engaged.set(false);
}
}
The AtomicBoolean acts as a test-and-set lock preventing multiple threads concurrently running the pressed event callback. Queued presses will be dissipated during the lock.
One solution would be to access the component on which the user clicked and disable it during the processing of the MouseListener. This way it doesn't accept new clicks. This would have the additional benefit of giving the user visual feedback that he is not supposed to click on the component at this time.
You can access the component through the MouseEvent:
public void mousePressed(MouseEvent event) {
event.getComponent().setEnabled(false);
try {
// ....
} finally {
event.getComponent().setEnabled(true);
}
In general though you shouldn't do too much computation in the Swing event listener thread, because Swing also needs it for processing other events and painting. You should use separate Threads for doing actual work and just start them in the MouseListener. You can also use an ExecutorService to simplify this.
It would still be a good idea to disable the gui component during the whole computation in order to give the user feedback.
Edit:
This solution of course depends on Swing. Any similar solution depends on the details of your GUI library, so if you use your own GUI library, you are one your own.
You can of course use a general solution involving separate threads for doing the work. I really recommend an ExecutorService here, were you don't need to care about the details and just submit tasks to execute. You can then have a simple volatile boolean variable which indicates whether the action is currently being executed and new clicks should be ignored.
in my app i am trying to process a task in the UI Thread when a button is clicked (the task is executed only once when the button is clicked)... since the task is time consuming my UI gets frozen and doesn't allow the user for further input...
my question is that is there any other method apart from "Thread"s to run my task so that my UI doesn't get frozen ..since i don't want the task to run repeatedly..
if possible can u provide me with sample code or links....
thanks :)
AsyncTask is a very convenient way for that, it has great hooks for initialization and clean-up.
Docs including example here.