Apparently all Eclipse/SWT has in the way of managing the busy mouse indicator is
BusyIndicator.showWhile(Runnable synchronousStuffToDo)
However, I have a fundamentally event-oriented project where "stuff to do" doesn't happen within a sequential line of execution: an action gets ordered and a continuation-callback is provided to the execution manager. Therefore I have nothing meaningful to put into that synchronousStuffToDo runnable.
Is there another, however low-level and clumsy, but platform-independent way of manipulating the busy indicator asynchronously, which means two separate method calls, "activate it" and "deactivate it"?
I should add ProgressMonitorDialog to this question because it appears to suffer from the same problem. Yes, within the ProgressMonitorDialog#run method an inner event loop will be spinned, but SWT event loop is just one of my execution managers, so the chain will still be broken. Apparently without this class I can't even show a progress monitor except if I reimplement from lower-level primitives.
There is no way you can manipulate the Cursor using the BusyIndicator class.
You can invoke the below util method to show a Busy Icon while running your job on a background Thread
public static void imBusy(final boolean busy){
Display.getDefault().asyncExec(new Runnable()
{
#Override
public void run()
{
Shell shell = Display.getDefault().getActiveShell();
if(busy){ //show Busy Cursor
Cursor cursor = Display.getDefault().getSystemCursor(SWT.CURSOR_WAIT);
shell.setCursor(cursor);
}else{
shell.setCursor(null);
}
}
});
}
Your runnable should wait for the task completion. E.g. (code written in browser, will not compile - I'm ignoring exceptions):
final Object condition = new Object();
BusyIndicator.showWhile(new Runnable() {
public void run() {
synchronized(condition) {
while (!isMyTaskDoneFlag()) {
condition.wait();
}
}
}
});
doTask(new MyTask() {
public void perform() {
try {
// Task logic
...
} finally {
// When done
setMyTaskDoneFlag();
synchronized(condition) {
condition.notify();
}
}
}
});
Make sure all code paths in your tasks do not forget to unblock the runnable. Pretty much the same approach can be used with progress monitors - you may wake your runnable to update progress monitor value.
Note: You need to make sure the waiting runnable is not executed on SWT thread (e.g. set fork to true if running in progress monitor) or else your application will become unresponsive.
I found a solution (which I don't particularly like, but it works) in an older SWT application I'm working on now. It uses BusyIndicator#showWhile, and the synchronous stuff it does inside is:
Start the asynch task in a background thread
Loop waiting for the background thread to finish up while at the same time spinning the
SWT event loop explicitly:
while (!taskDone){
if (!display.readAndDispatch() && !shell.isDisposed()) {
display.sleep();
}
taskDone = //check for task progress
//update something on the main window status bar
}
I'm trying to convert this to something cleaner (along the lines of what Marko suggested):
Set the busy icon
Submit background task
Unset the busy icon
but I'm not sure what would be best for updating the status bar (background tasks are actually remote calls so their thread is blocked until they finish up). I'm thinking of having a dedicated thread that detects when background jobs are running and update the status bar accordingly (the update is just an unfolding dotted line, nothing task specific), but using a thread just for this seems a bit of a waste.
Related
I have an activity, in it there's a public void that executes a lot of code and updates some textviews with some information about the process as it goes. I want to be able to pause the code for about a second every time a textview is updated (and have the update show up on the GUI). The desired result is a smooth series of changes rather than everything happening all at once.
I read that one way to do this is to use a handler with a delay but is that the best way when I have to do it several times (requiring a lot of stacking them together)?
I also have a separate async task running, and I want it to continue running even while the rest of the code is paused.
What is the best way to achieve the desired result?
you can calculate all your data in your code as it is now but don't update any UI elements (like a TextView) then create a new Thread and make it update one TextView, then delay it for 1000ms (1 sec).
{
//Your calculations take place here
//...
//Make your class extend Runnable, or create a new Runnable class - I hope you are familiar with threads
Thread thread = new Thread(this);
thread.start();
}
run()
{
//Update UI - when updating UI from outside main thread use the view.post(Runnable) method instead of directly changing it's text
textView.post(new Runnable()
{
public void run()
{
textView.setText(yourText);
}
}
//Make thread sleep for 1000ms (or 1sec)
try
{
Thread.sleep(1000);
} catch(InterruptedException ex) {}
//Here you can update more UI elements and after each one make Thread delay again for 1 second
//...
}
I haven't worked with handlers yet so I can't say if it's better or not, but I know it works
As part of my efforts to implement a voice recognition program in Java I have implemented the actual voice recognition code in a separate thread. The main thread handles the GUI interface and receives constant updates from the voice recognition thread when words are identified.
When the user clicks the Quit button in the GUI on the main thread I want this thread to immediately run some clean-up code and terminate.
I currently have the following:
public class VoiceRecognitionCore extends SwingWorker<List<String>, String>
{
//Variables and things here
#Override
public List<String> doInBackground() throws VoiceRecognitionException
{
//Code here
while(continueVoiceRecog == true)
{
//More code
Result result = recog.recognize();
//More code
}
}
}
Where I rely on the while loop to constantly check the status of continueVoiceRecog which will be set to false by the main thread when the user clicks "Quit".
The current problem is that the code can sometimes permanently sit inside the recog.recognize() method so it'll never get back to the while check. It should be noted that this was always intended as a temporary solution.
I'm thinking of extending doInBackground() to catch InterruptedException and will use a thread interrupt which will call a cleanup method to deallocate any resources being used.
What is the safest/best approach for this scenario? If it is what I propose, are there any potential issues I should be aware of?
Using thread interrupt is perfectly acceptable route - however in your example (using SwingWorker) you can use the cancel() method instead.
In the calling code after having created the VoiceRecognitionCore you can cancel() the worker exit button action listener:
final VoiceRecognitionCore worker = new VoicRecognitionCore();
worker.execute();
JButton exitButton = new JButton("Exit");
exitButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// True passed to Interrupt the underlying thread.
worker.cancel(true);
// other clean-up
// then System.exit(0); ?
}
});
However, this approach will need to check the status of: Thread.isInterrupted() within your recognize() method. (see link: http://docs.oracle.com/javase/tutorial/uiswing/concurrency/cancel.html)
If you are needing to clean-up stuff and don't have ability to check the isInterrupted() flag - perhaps best approach is to have a method to be able to determine if your recog object is mid recognizing... and when the exit button is pressed - if recog.isRecognizing() then do clean up and then exit?
PS. One might argue that if you are doing a System.exit(0); anyway, then cleanly exiting that loop is perhaps unnecessary ... but it depends if you are doing other clean-up in there... such as finishing writing to files etc.
I'm new to multithreading, so excuse my potentially silly question.
I need to use several threads in my app. However, virtually all of these threads will modify the UI. I've successfully used runOnUiThread, but what I fear is that if I create different threads of the same type, they will all be running on one thread, the "Ui thread", which may slow down my app.
Is this true, or am I greatly misunderstanding?
My thread which I will essentially multiply:
private void goldPerSecondMethod() {
new Thread() {
public void run() {
while (goldCount < 1000) {
try {
runOnUiThread(new Runnable() {
#Override
public void run() {
goldCount += 0.1f;
textGoldCount.setText(goldCount + " Gold");
textGoldCount.setGravity(Gravity.CENTER);
}
});
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
All help is appreciated!
I need to use several threads in my app.
Why? For example, there is no need for a thread that you create yourself in the code sample you have above. Use a postDelayed() loop (no threads), or use ScheduledExecutorService (threads, but you don't have to create them) for timing.
what I fear is that if I create different threads of the same type, they will all be running on one thread, the "Ui thread", which may slow down my app.
I have no idea what "the same type" means. In your code snippet above, everything in the Runnable that you pass to runOnUiThread() will be executed on the main application thread (sometimes called the UI thread). Everything else in your outermost run() will be executed on this background thread.
It is correct that you need to run UI updates on the UI thread. Hence you do textGoldCount.setText(...) on the UI thread. Technically, this is a correct approach.
However, it's unnecessary to call setGravity(...) every time you update the text field. You should be able to set the gravity once. Best place would be probably your XML view description.
At the end you don't do much heavy work on the UI thread, except updating the text view's text. As every thread is sleeping 1 second before updating the UI again, there should be no notable delay for the user as long as you don't run too many of those threads.
I have been messing around a bit with the runOnUiThread method. And if I simply make a method in my activity:
public void Test()
{
runOnUiThread(new Runnable()
{
public void run()
{
Log.v("mainActivity", "test");
}
});
}
I noticed that this runnable only runs once. However, this is not a problem. What I was wondering is if I have completely missed something and it does something in the background that would cause a frame rate drop when I have executed the method a couple times.
This is the full body from Activity.runOnUiThread(Runnable):
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
The method body is still executed in your background thread, and mHandler of class android.os.Handler implements an internal queue for Runnables posted to it, so unless you're doing blocking work in the Runnable (which is a big no-no on the UI Thread) or calling this method upwards of a thousand times in a short period, you should not see any difference.
Now, if you were calling Handler.postAtFrontOfQueue(Runnable), then there'd be an issue, because your Runnable is essentially "cutting in line". In this case, that would likely cause a stutter, because your Runnable is being executed instead of any UI updates that needed to take place (like scrolling).
Note that you only need to run UI updates on the UI thread, like calling any methods on a View (thus the name "UI Thread" and why this method exists) or any operation where the documentation explicitly states that it needs to be run on the UI thread. Otherwise, if you're already on a background thread, there's no real reason to leave it.
It's unlikely that it would cause any significant interruption to your UI process, but there's really no point in running it on the UI thread.
If you are doing any significant amount of work, you should make sure that you do not do it on the UI thread.
I'm writing a chess program in java. So far things are coming along fine but I do have a problem with updating my UI.
Here's a snippet of code from class ChessBoard which extends JPanel. This is called when a user tries to make a move:
if ( isLegalMove( aMove ) ) { // If the move's legal
makeMove( aMove ); // Make that move
select = null; // Reset some info
drag = null;
toggleTurn(); // Change turns
generateMoves( 0 ); // Get legal moves for CPU
repaint(); // Redraw board
thread.run(); // Run chess algorithm
}
The thread is calling "run" on my instance of ChessBoard. The algorithm that finds the move can take several seconds to decide on a move.
I would like for my UI to update to reflect the user's move and then run the algorithm. That's why I run the algorithm on a separate thread. But my UI is not being updated until the computer also makes a move.
So if the user clicks a space to send a piece there, the screen freezes and then all of a sudden the piece has moved but the computer has moved also and it is again the player's turn.
Any help will be greatly appreciated.
thread.run() is going to execute the code in the thread's run method on the current thread. You want thread.start().
Relevant JavaDoc
The repaint method doesn't actually repaint immediately. It basically tells the JPanel that it ought to repaint itself soon. Then you go ahead on the same thread and calculate the AI's move, which will freeze the window because Swing isn't multi-threaded.
First, threads are not re-entrant (I'll explain that in a moment).
thread.run() is not causing the thread to execute in a separate thread, it's just call the run method of the thread (within the current Threads context.
What you need to do is set up a condition loop within your Thread that you can trigger in order to execute the logic you need.
public class ChessThread extends Thread { // I prefer Runnable, that's me
protected static final Object NEXT_MOVE_LOCK = Object();
public ChessThread() {
setDaemon(true); // This will allow the JVM to exit without the need to terminate the thread...
}
public void doNextMove() {
// Notify the "wait" that we want to continue calculating the next move
synchronized (NEXT_MOVE_LOCK) {
NEXT_MOVE_LOCK.notify();
}
}
public void run() {
while (true) {
// Wait for the "next move" request
synchronized (NEXT_MOVE_LOCK) {
try {
NEXT_MOVE_LOCK.wait();
} catch (InterruptedException exp) {
}
}
// Calculate the next move...
}
}
}
Now, Threads are non-reentrant, this means that once the run method has complete, that instance of the Thread can not be restarted.
Hence using thread.start() more then once will not work (can't remember if it throws an exception or not) (hence the reason I prefer Runnable)
So. What you want to do, is start the Thread when your program loads and when you need to, call thread.doNextMove() to cause it calculate the what ever it is you need.
Now, also remember, Swing is not Thread safe. That is, you should NEVER update the UI from any Thread other than the Event Dispatching Thread (or EDT)
You might also want to have a read through Concurrency in Swing
Oh and Concurrency in Java