I have a problem with a long running task.
After my dialog is shown I want to scan a ftp directory. This task takes some time so I need to run this task no in the UI thread.
My idea was
#Override
protected void postDialogOpen() {
// if invoked via menu button
if (!scanFtp) {
final Display display = Display.getDefault();
new Thread(new Runnable() {
#Override
public void run() {
//initProgressWaitViewer();
scanFtpServer();
//closeProgressWaitViewer();
display.syncExec(new Runnable() {
#Override
public void run() {
updateTree();
}
});
}
}).run();
}
}
But during the execution of scanFtpServer() my dialog is not movable and if I click on it it becomes "unresponsible".
Is there something I am doing wrong?
When calling method run() in class Thread, you are executing the method on the caller thread, just like calling any other method. If you want to spawn a new thread and execute method run() in that thread, you need to call method start() instead, that will do all the work of setting up the thread and running it.
So replace
}).run();
with
}).start();
Try to do .start() instead of .run() .
Related
new Timer().schedule(new TimerTask() {
public void run() {
KeyBoardUtil.showKeyBoard(et_search);
}
}, 300);
I show the keyboard use TimerTask,and it runs fine.
how to explain it?
You can use runOnUiThread :
runOnUiThread(new Runnable(){
public void run() {
// Your code
}
});
Or
Handler mainHandler = new Handler(context.getMainLooper());
Runnable myRunnable = new Runnable() {
#Override
public void run() {
// Your code
}
};
mainHandler.post(myRunnable);
developer.android.com
In the previous lesson you learned how to start a task on a thread managed by ThreadPoolExecutor. This final lesson shows you how to send data from the task to objects running on the user interface (UI) thread. This feature allows your tasks to do background work and then move the results to UI elements such as bitmaps.
Every app has its own special thread that runs UI objects such as View objects; this thread is called the UI thread. Only objects running on the UI thread have access to other objects on that thread. Because tasks that you run on a thread from a thread pool aren't running on your UI thread, they don't have access to UI objects. To move data from a background thread to the UI thread, use a Handler that's running on the UI thread.
I'm developing an Eclipse plugin that will contribute to the GUI with a view.
The view is updated with informations from a versioning system when the user selects a folder or a file in the workspace.
In order to avoid collecting data everytime the user goes through the project subfolders and files, I need to wait for 3 seconds in order to be sure that the file or folder is the one of interest.
I'm currently doing this using a Swing Timer.
This is ok for small amount of data, but for large amount of data the GUI blocks, waiting for the timer to execute the update function.
I know for this kind of task I can use SwingWorker but I can't figure out how to delay the task and to restart the delay when needed.
Can anyone give me a solution on how to correctly solve this problem ?
Here is my current code:
public void resetTimerIfNeeded()
{
if(timer.isRunning())
timer.restart();
else
timer.start();
}
public void timer()
{
selectionTimer = new Timer(3000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
Display.getDefault().syncExec(new Runnable(){
#Override
public void run()
{
updateView();
selectionTimer.stop();
}
});
}
});
}
Since Eclipse uses SWT rather than Swing it is best to avoid using Swing code.
You can run code in the UI thread after a delay using UIJob, something like:
UIJob job = new UIJob("Job title") {
#Override
public IStatus runInUIThread(IProgressMonitor monitor) {
updateView();
return Status.OK_STATUS;
}
};
job.schedule(3000);
Alternatively you can use Display.timerExec:
Display.getDefault().timerExec(3000, new Runnable(){
#Override
public void run()
{
updateView();
}
});
Schedule it as a Job instead: https://eclipse.org/articles/Article-Concurrency/jobs-api.html . Use a UIJob if the entirety of what it's doing is interacting with the UI. The cancel/schedule and sleep/wakeUp methods will be of interest , see http://help.eclipse.org/luna/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/core/runtime/jobs/Job.html for the JavaDoc.
I want to run my program where the value of a label changes after the Timer goes off. But whenever the Timer runs I will keep getting the Invalid Thread access error and my label does not get updated.
protected void createContents() {
<--GUI codes -->
//Timer set to go every 10 seconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
System.out.println("Timer");
lblState.setText("On");
}
};
new Timer(delay, taskPerformer).start();
}
This link from the SWT FAQ explains the error and how to solve it: any code that modifies GUI components (in your case, setting the text of the label) needs to run on the display thread, otherwise this error will occur.
To run on the display thread, wrap the code inside a Runnable and call Display.getDefault().syncExec( with the provided Runnable:
Display.getDefault().syncExec(new Runnable() {
public void run() {
// code that affects the GUI
}
});
All access to UI objects must be done in the user interface thread. You can do this using Display.asyncExec (or Display.syncExec).
Change your line:
lblState.setText("On");
to
Display.getDefault().asyncExec(() -> lblState.setText("On"));
for Java 8. For Java 7 or earlier use:
Display.getDefault().asyncExec(new Runnable() {
#Override
public void run()
{
lblState.setText("On");
}
});
When I click Print Button it should show a Gif Animation followed by the text "Working..."
but here only the text "Working..." appears , not the animation.
Here's the Code:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
jLabel1.setVisible(true);
/* This portion is Time Consuming so I want to display a Loading gif animation. */
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
empPrint=new HashMap();
if(!empPrint.isEmpty())
empPrint.clear();
if(jRadioButton1.isSelected())
empPrint.put("PNO",parent.emp.getPAN());
else
empPrint.put("PNO",records.get(jComboBox1.getSelectedItem()));
REPORT="Report.jrxml";
try {
JASP_REP =JasperCompileManager.compileReport(REPORT);
JASP_PRINT=JasperFillManager.fillReport(JASP_REP,empPrint,parent.di.con);
JASP_VIEW=new JasperViewer(JASP_PRINT,false);
JASP_VIEW.setVisible(true);
JASP_VIEW.toFront();
}
catch (JRException excp) {
}
setVisible(false);
}
});
}
You should use a SwingWorker for time consuming tasks. Using invokeLater() just pushes it to the event queue, and it gets run in the EDT, blocking it.
Drawing in swing is done in the event dispatch thread, but since the EDT is busy running your printing task, swing has no chance to process repaint requests.
// Note the upped case "Void"s
SwingWorker worker = new SwingWorker<Void, Void>() {
#Override
public Void doInBackground() {
// Do the printing task here
return null;
}
#Override
public void done() {
// Update the UI to show the task is completed
}
}.execute();
The SwingUtilities.invokeLater() method will not help you in this case. The Runnable you pass still gets executed on the Event Dispatch Thread (EDT, the thread responsible for drawing the UI and responding to clicks etc.).
You could look at SwingWorkers, but you could just as well use a simple ExecutorService and pass the Runnable to there. The Executor framework - which was added in Java 5 or 6 - offers relatively simple to use tools to have stuff run in the background without having to worry about your own threads. I recommend going with something like this (pseudo code):
private ExecutorService executor = Executors.newFixedExecutorService()
....
public void buttonPressed() {
label.setVisible(true);
...
executor.submit(new Runnable() {
// create the report etc.
// DO NOT ACCESS ANY UI COMPONENTS FROM HERE ANYMORE!
// ...
SwingUtilities.invokeLater(new Runnable() {
// update the UI in here
label.setVisible(false);
});
});
}
As you can see, SwingUtilities.invokeLater is used here, too. However, it is called from a background thread to make sure your UI code gets executed on the EDT instead of on the background thread. That is what it is designed for, because UI components must never be accessed (not even read from!) from a background thread. That way you have a convenient mechanism to update you label nevertheless. You could also use it to update some progress bar etc.
In my java application I am using swing to implement the UI. There is a button called theButton which is engaged with some IO operation in the following timely steps :
the button originally has the text "Click to connect"
then before the connect operation starts I want the theButton reads
"Connecting in progress..."
then the IO operation gets started
once the IO operation is done theButton now reads "connected ( click to disconnect)".
Issue:
I am using the following code, but first of all the button's text doesn't change to "Connecting in progress..." before the IO starts! as well button doenst actually get disabled before the IO starts! What should I do here?
--
// theButton with text "Click to connect is clicked"
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
theButton.setText("Trying to connect...");
theButton.setEnabled(false);// to avoid clicking several times! Some users cannot wait
theButton.repaint();
// doing some IO operation which takes few seconds
theButton.setText("connected ( click to disconnect)");
theButton.setEnabled(true);
theButton.repaint();
}
});
Your problem is here:
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
theButton.setText("Trying to connect...");
theButton.setEnabled(false);
theButton.repaint();
// doing some IO operation which takes few seconds // **********
theButton.setText("connected ( click to disconnect)");
theButton.setEnabled(true);
theButton.repaint();
}
});
The code marked with the ******* comment is running on the EDT and will tie it up freezing your app and all it's painting.
Use a SwingWorker instead to run the code in a background thread.
Note that there is no need to use invokeLater(...) for code in an ActionListener since this code is already running on the EDT by default.
Also get rid of your repaint() calls since they aren't needed and they don't help.
Add a PropertyChangeListener to your SwingWorker to listen for when it is done, and then you can reset your JButton.
Instead do:
// code not compiled nor tested
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
theButton.setText("Trying to connect...");
theButton.setEnabled(false);
MySwingWorker mySwingWorker = new MySwingWorker();
mySwingWorker.addPropertyChangeListener(new PropertyChangeListener() {
// listen for when SwingWorker's state is done
// and reset your button.
public void propertyChange(PropertyChangeEvent pcEvt) {
if (pcEvt.getNewValue() == SwingWorker.StateValue.DONE) {
theButton.setText("connected ( click to disconnect)");
theButton.setEnabled(true);
}
}
});
mySwingWorker.execute();
}
});
and
// code not compiled nor tested
public class MySwingWorker extends SwingWorker<Void, Void> {
#Override
public void doInBackground() throws Exception {
// doing some IO operation which takes few seconds
return null;
}
}
And be sure to read: Concurrency in Swing.