This question already has answers here:
javafx animation looping
(3 answers)
Closed 5 years ago.
I am trying to build a clock in javafx but the GUI crashes when I try using an infinite loop.
while (true) {
Date time = new Date();
// mins and hour are labels
if (time.getMinutes() < 10) {
mins.setText("0" + Integer.toString(time.getMinutes()));
} else {
mins.setText(Integer.toString(time.getMinutes()));
}
if (time.getHours() < 10) {
hour.setText(0 + Integer.toString(time.getHours()));
} else {
hour.setText(Integer.toString(time.getHours()));
}
}
I heard I can use something called a thread but I didn't really understand how to properly implement it.
Looks like you are using an infinite loop in the UI thread. You should keep track of time in a background thread, but update the UI in UI thread.
To run in background thread, use:
new Thread(new Runnable(){
public void run(){
//your code here.
}
}).start();
To run in UI thread, use:
Platform.runLater(new Runnable(){
public void run(){
//your code here.
}
});
That is perfectly reasonable. Never block the main thread! Use an additional thread in order to achieve your goal.
Task<Void> workingTask = new Task<Void>() {
#Override
public Void call() {
while (true) {
//yourcode
}
}
and use Platform.runLater(()-> { //yourcode}); in order to send small tasks to main javafx thread. For e.g.
Platform.runLater(() -> {
mins.setText(Integer.toString(time.getMinutes()));
});
Related
This question already has answers here:
Setting text on TextView from background thread anomaly
(2 answers)
Closed 3 years ago.
I don't know why non UI thread can update the text view in while loop like this:
Runnable timeCount = new Runnable() {
#Override
public void run() {
int i = 0;
while (true)
{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mTextView.setText(i++ + "");
}
}
};
Thread thread = new Thread(timeCount);
thread.start();
has been called after Activity Resume.
Please help to explain about this!
the ViewRoot will call the checkThread() to check if it's the main thread ,if your thread be called just before the method of checkThread() .it will not to throw the error。
As mentioned in documentation(https://developer.android.com/topic/performance/threads.html#references) Android UI classes are not thread safe.
If you try to update View objects from any other thread than the one created it you may face anomaly. Sometimes it may seem to work however there are no guarantees.
I'm making a program which sends some data to the Matlab and receives output from matlab. To do that, I used matlabcontrol library.
Problem
After I click Java FX application button to submit data, matlabcontrol opens Matlab for further calculations. But when java opens the matlab, Java FX application stuck with wait cursor.Then starts to work again after Matlab finishes the process of calculation.
What I did
public void runner()
{
Platform.runLater(new Runnable()
{
#Override
public void run()
{
firstFunc();
}
});
Platform.runLater(new Runnable()
{
#Override
public void run()
{
secondFunc();
}
});
}
public void firstFunc()
{
// This function controls UI while Matlab does it's calculations
double progress = 0.2;
progressLabel.setText(progress*100+"% Completed");
progressBar.setProgress(progress);
}
public void secondFunc()
{
// This method creates matlab connection and handle matlab
firstClass mainFunc = new firstClass(pathSelected);
mainFunc.func();
}
So I used Platform runLater to run two methods separately. But still my program stuck with wait cursor when Matlab starts to functioning.
I also used threads to run these functions in parallel. But had the same issue. How can I correct this. Any help?
Update
As described in this question, I did use service and task with countdownlatch. But still didn't get what I wanted. In there,
Service<Void> service = new Service<Void>() {
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
//Background work
final CountDownLatch latch = new CountDownLatch(1);
Platform.runLater(new Runnable() {
#Override
public void run() {
try{
//FX Stuff done here
firstFunc();
}finally{
latch.countDown();
}
}
});
latch.await();
//Keep with the background work
// I added matlab calling function here.
secondFunc();
return null;
}
};
}
};
service.start();
latch await and let background work to carry on. But in my case, my FX application shows a progress bar. So it should always update while background task happens. In here, it finishes FX task and moves to background task. I didn't get what I wanted. please help.
I had to use Service, Task and CountDownLatch to accomplish this task as I mentioned in Question Update part.
Also, I had to run another task inside the firstFunc, where I did update the progress. Like in this answer.
basically, I have this code which was initially working with console i/o now I have to connect it to UI. It may be completely wrong, I've tried multiple things although it still ends up with freezing the GUI.
I've tried to redirect console I/O to GUI scrollpane, but the GUI freezes anyway. Probably it has to do something with threads, but I have limited knowledge on it so I need the deeper explanation how to implement it in this current situation.
This is the button on GUI class containing the method that needs to change this GUI.
public class GUI {
...
btnNext.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
controller.startTest(index, idUser);
}
});
}
This is the method startTest from another class which contains instance of Question class.
public int startTest() {
for (int i = 0; i < this.numberofQuestions; i++) {
Question qt = this.q[i];
qt.askQuestion(); <--- This needs to change Label in GUI
if(!qt.userAnswer()) <--- This needs to get string from TextField
decreaseScore(1);
}
return actScore();
}
askQuestion method:
public void askQuestion() {
System.out.println(getQuestion());
/* I've tried to change staticaly declared frame in GUI from there */
}
userAnswer method:
public boolean userAnswer() {
#SuppressWarnings("resource")
Scanner scanner = new Scanner(System.in);
if( Objects.equals(getAnswer(),userInput) ) {
System.out.println("Correct");
return true;
}
System.out.println("False");
return false;
}
Thanks for help.
You're correct in thinking that it related to threads.
When you try executing code that will take a long time to process (eg. downloading a large file) in the swing thread, the swing thread will pause to complete execution and cause the GUI to freeze. This is solved by executing the long running code in a separate thread.
As Sergiy Medvynskyy pointed out in his comment, you need to implement the long running code in the SwingWorker class.
A good way to implement it would be this:
public class TestWorker extends SwingWorker<Integer, String> {
#Override
protected Integer doInBackground() throws Exception {
//This is where you execute the long running
//code
controller.startTest(index, idUser);
publish("Finish");
}
#Override
protected void process(List<String> chunks) {
//Called when the task has finished executing.
//This is where you can update your GUI when
//the task is complete or when you want to
//notify the user of a change.
}
}
Use TestWorker.execute() to start the worker.
This website provides a good example on how to use
the SwingWorker class.
As other answers pointed out, doing heavy work on the GUI thread will freeze the GUI. You can use a SwingWorker for that, but in many cases a simple Thread does the job:
Thread t = new Thread(){
#Override
public void run(){
// do stuff
}
};
t.start();
Or if you use Java 8+:
Thread t = new Thread(() -> {
// do stuff
});
t.start();
I start my GUI like this, which seems correct.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame gui = new JFrame();
gui.setExtendedState(JFrame.MAXIMIZED_BOTH);
gui.setVisible(true);
}
});
At a certain point in the application, the playTurn() method gets fired. The for loops all turns in the list.
for (String turn : controller.getTurns()) {
playTurn(turn);
}
I now load the correct panel with my CardLayout which worked fine. Then I had to write the playTurn() method. So playTurn() gets called. It should do certain things according to some variables. But it should not return until some buttons are disabled. This is what I can't achieve, the program just stops working. I can guess it's in the direction of threads etc.. but can't seem to figure it out. Thanks in advance.
public void playTurn(String turn) {
if (controller.givePlayers().contains(turn)) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
while (!turnFinished) {
if (!button1.isEnabled() && !button1.isEnabled() && !button1.isEnabled() && !button1.isEnabled()) {
turnFinished = true;
}
}
}
});
} else {
deletePlayer(turn);
}
}
Sorry for bad formatting. Couldn't find where.
EDIT:
The GUI stops being responsive. Can't close program either.
I tried using a SwingWorker for the while which does not block the GUI but still playTurn() returns.
I have even tried creating a new thread where I call the method. The doesn't get blocked anymore but the method still returns.
Thread one = new Thread() {
public void run() {
playTurn(turn);
}
};
FIXED: Placing the runnable up higher in the stack;
Your playTurn method runs the code on the EDT, cause of this line javax.swing.SwingUtilities.invokeLater(new Runnable() {, which makes your application GUI unresponsive as GUI-changing code must generally be run on the EDT. Since your buttons won't change from your GUI, once the loop starts, it might just loop forever.
By running the code in another Thread, you won't freeze your GUI. I'm guessing, since you don't provide much informations on the rest of your code, that you might have to change the way you handle things once your loop is done.
Edit from comments : Since you don't want playTurn to return, don't use a thread within it and make sure playTurn is not running on the EDT. Your playTurn method will return after creating and making a new Thread run the code.
You might want to try dong it like this :
Runnable code = new Runnable() {
#Override
public void run() {
for (String turn : controller.getTurns()) {
playTurn(turn);
}
}
};
if (!SwingUtilities.isEventDispatchThread()) {
code.run();
} else {
new Thread(code).start();
}
To make sure you don't run the code on the EDT. That way, playTurn doesn't return until the loop condition is met, the GUI stays responsive.
public void playTurn(String turn) {
if (controller.givePlayers().contains(turn)) {
while (!turnFinished) {
if (!button1.isEnabled() && !button1.isEnabled() && !button1.isEnabled() && !button1.isEnabled()) {
turnFinished = true;
}
}
} else {
deletePlayer(turn);
}
}
Doing this might have you change a few things more.
The idea is to make the call to a new Thread where you don't want it/need it to wait for the code being run in a new Thread to end to continue.
This is a general Java question and not an Android one first off!
I'd like to know how to run code on the main thread, from the context of a secondary thread. For example:
new Thread(new Runnable() {
public void run() {
//work out pi to 1,000 DP (takes a while!)
//print the result on the main thread
}
}).start();
That sort of thing - I realise my example is a little poor since in Java you don't need to be in the main thread to print something out, and that Swing has an event queue also - but the generic situation where you might need to run say a Runnable on the main thread while in the context of a background thread.
EDIT: For comparison - here's how I'd do it in Objective-C:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0UL), ^{
//do background thread stuff
dispatch_async(dispatch_get_main_queue(), ^{
//update UI
});
});
Thanks in advance!
There is no universal way to just send some code to another running thread and say "Hey, you, do this." You would need to put the main thread into a state where it has a mechanism for receiving work and is waiting for work to do.
Here's a simple example of setting up the main thread to wait to receive work from other threads and run it as it arrives. Obviously you would want to add a way to actually end the program and so forth...!
public static final BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
public static void main(String[] args) throws Exception {
new Thread(new Runnable(){
#Override
public void run() {
final int result;
result = 2+3;
queue.add(new Runnable(){
#Override
public void run() {
System.out.println(result);
}
});
}
}).start();
while(true) {
queue.take().run();
}
}
In case you are on Android, using a Handler should do the job?
new Handler(Looper.getMainLooper()).post(new Runnable () {
#Override
public void run () {
...
}
});
An old discussion, but if it is a matter of sending request to the main thread (an not the opposite direction) you can also do it with futures. The basic aim is to execute something in background and, when it is finished, to get the result:
public static void main(String[] args) throws InterruptedException, ExecutionException {
// create the task to execute
System.out.println("Main: Run thread");
FutureTask<Integer> task = new FutureTask<Integer>(
new Callable<Integer>() {
#Override
public Integer call() throws Exception {
// indicate the beginning of the thread
System.out.println("Thread: Start");
// decide a timeout between 1 and 5s
int timeout = 1000 + new Random().nextInt(4000);
// wait the timeout
Thread.sleep(timeout);
// indicate the end of the thread
System.out.println("Thread: Stop after " + timeout + "ms");
// return the result of the background execution
return timeout;
}
});
new Thread(task).start();
// here the thread is running in background
// during this time we do something else
System.out.println("Main: Start to work on other things...");
Thread.sleep(2000);
System.out.println("Main: I have done plenty of stuff, but now I need the result of my function!");
// wait for the thread to finish if necessary and retrieve the result.
Integer result = task.get();
// now we can go ahead and use the result
System.out.println("Main: Thread has returned " + result);
// you can also check task.isDone() before to call task.get() to know
// if it is finished and do somethings else if it is not the case.
}
If your intention is to do several stuff in background and retrieve the results, you can set some queues as said above or you can split the process in several futures (starting all at once or starting a new one when needed, even from another future). If you store each task in a map or a list, initialized in the main thread, you can check the futures that you want at anytime and get their results when they are done.
You may want to use the 'even dispatching thread' where most event driven things happen. If you are using swing then:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Your code here.
}
});
Or create a class that implements Runnable and pass it into invokeLater().
If you're using JavaFX, which I highly recommend, then you can use
Platform.runLater(new Runnable() {
#Override
public void run() {
alert(text);
}
});
from within your non-UI thread, and the runnable will executed from the UI thread on return from your thread.
A little late to the party but I think that my approach is a little bit different.
Modifying Affe's solution a little bit
public static final BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
public static void main(String[] args) {
Thread myThread = new Thread(
() -> {
String name = Thread.currentThread().getName();
System.out.println("initial current thread " + name);
queue.add(() -> System.out.println(Thread.currentThread().getName()));
});
myThread.setName("background thread");
myThread.start();
try {
myThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
while (!queue.isEmpty()) {
try {
queue.take().run();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
output
initial current thread background thread
main