Does SwingWorker has to be a nested class? - java

I'm wondering if SwingWorker has to be a nested class within my main GUI. I'd rather make it an external class to keep the GUI clear from any of my programs logic.
I tried to make the SwingWorker class external, which works fine for the process, unfortunately I can't access any of my GUI fields from the SwingWorker class.
Whenever I try to access an attribute, such like a label or whatever from within SwingWorker's done() method I get a nullPointer exception.
Any advice would be much appreciated!
First of all thank you very much Jeff! Works fine so far, even though I could not follow you on the second option you presented.
One of my background tasks calculates a certain size (long value), so it would be nice to get that value from my GUI.
You suggested to work with getters and setters but unfortunately I've got no idea on how to implement them in the SwingWorker class.
I tried it like this:
public void setSize(long totalSize) {
this.totalSize = totalSize;
}
public long getTotalSize() {
return totalSize;
}
The setter is invoked at the end of the doInBackground() method. Unfortunately I can't use the get() method from my GUI.
final MySwingWorker w = new MySwingWorker();
Runnable r = new Runnable() {
public void run() {
// do something with w.get()
}
};
w.setRunnable(r);
w.execute();
The object creation of "w" does not work in my case as the constructor requires an object of Runnable.
Am I missing something?
Please go easy on me, it's the first time I work with SwingWorker. :)
Again, thank you very much for your help!

You can make the SwingWorker an external class. However, just like any other class, if it can't see the variables (e.g. the label you want to set), of course it won't be able to set it. One thing you could do is pass the worker a Runnable that it executes when it is complete.
public class MySwingWorker extends SwingWorker {
private final Runnable r;
public MySwingWorker(Runnable r) {
this.r = r;
}
public void doInBackground() {...}
public void done() { r.run(); }
}
Now from the GUI, you might do something like
Runnable updateLabel = new Runnable() {
public void run() {
label.setText("myValue");
}
};
SwingWorker w = new MySwingWorker(updateLabel);
w.execute();
This gets a bit trickier if you want to use the result of the SwingWorker, though it is possible. Rather than passing the Runnable to the swing worker's constructor, you would have a setter method and then it would be something like:
final MySwingWorker w = new MySwingWorker();
Runnable r = new Runnable() {
public void run() {
// do something with w.get()
}
};
w.setRunnable(r);
w.execute();
In either case, the Runnable is functioning similarly to a closure that is executed when the worker is finished.

Related

can't get sequential behavior Java Swing [duplicate]

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();

Confusion regarding Runnable and Thread

I know this is a very tiny thing and would be quite easy for all you programmers out here, but I am stuck. I am not able to understand why this code snippet is printing out "Dog" instead of "Cat".
Runnable r = new Runnable() {
public void run() {
System.out.print("Cat");
}
};
Thread t = new Thread(r) {
public void run() {
System.out.print("Dog");
}
};
t.start();
Calling start() on a Thread object causes the JVM to spawn a new system thread which then proceeds to call the run method. Its default implementation looks something like this :
private Runnable target; // This is what you passed to the constructor
#Override
public void run() {
if (target != null) {
target.run();
}
}
Since you have overriden this very method in your anonymous Thread subclass declaration, this code never gets called, and the Runnable you injected is simply never used.
Also, whenever possible, leave the Thread class alone and put your code in Runnables instead.

Updating JavaFX ProgressIndicator multiple times from a Thread

I am working on the design of a multi-threading app in Javafx and would like to have a TableView with columns for Name and Progress of each Thread. After doing much research I found a similar example of what I am trying to accomplish here:
JavaFX Update progressbar in tableview from Task
(Which points to this: 'https://community.oracle.com/message/10999916')
The problem I am running into, however, is illustrated well in this example; how can you call a 'Task' object multiple times to update a ProgressIndicator?
My understanding from Oracle's documentation is that a Task object "is a one-shot class and cannot be reused". It would seem then that one can only invoke the call() method of a Task object once. I need to update the Task multiple times as it progresses through a Thread class, not call it once and arbitrarily increment through a For loop.
I have read about binding to Listeners and creating Service classes, but I am unsure if those are actual resolutions to this problem. I would therefore like to ask if this is even possible in Javafx, or if perhaps I am overlooking something. In the event someone has accomplished this in the past, it would be tremendously helpful if you might be able to illustrate how through the example provided previously.
Any direction on this would be appreciated, thank you.
-Drew
EDIT 1: I edited my wording as it was inaccurate.
EDIT 2: Here is an example with some pseudo code. Say I had a class with the following code:
public static class TaskEx extends Task<Void>{
#Override
protected Void call(){
updateProgress(.5, 1);
return null
}
public static void callThread() {
TableView<TaskEx> table = new TableView<TaskEx>();
//Some code for data in table.
TableColumn progressColumn = new TableColumn ("Progress");
progressColumn.setCellValueFactory(new PropertyValueFactor("progress");
table.setItems(<data>);
table.getColumns();addAll(progressColumn);
ExecutorService executor = Executors.newFixedThreadPool(<SomeNumber>);
for(TaskEx task : table.getItems(){
Threading.ThreadClass newThread = new Threading.ThreadClass(task);
executor.submit(newThread, <uniqueID>);
}
}
Then say I had a second class for Threading with this logic:
static class ThreadClass extends Thread{
Task progressTask;
public ThreadClass(Task task, Integer id){
progressTask = task;
}
public void run(){
ExecutorService executor = Executors.newFixedThreadPool(<someNumber>);
//This invokes the Task call for the correct progressIndicator in the Tableview.
//It will correctly set the progressIndicator to 50% done.
executor.submit(progressTask);
/* Main logic of the Threading class that involves the 'id' passed in. */
//This will do nothing because you cannot invoke the Task call more than once.
executor.submit(progressTask);
}
}
That is the sort of workflow I need, but I'm unsure how to accomplish this.
It seems like you don't get what we were talking about. You are trying to do your logic in the Thread.run(), and then each thread is creating a Task just to do the update of progress.
What you need is really to shift your logic from Thread.run() to Task.call(). Your thread is really just a thread, and all it does is to run a Runnable object (which is the Task).
public class TaskEx extends Task<Void> {
#Override
protected Void call() {
// Do whatever you need this thread to do
updateProgress(0.5, 1);
// Do the rest
updateProgress(1, 1);
}
}
public static void callThread() {
TableView<TaskEx> table = new TableView<TaskEx>();
ObservableList<TaskEx> data = FXCollections.observableArrayList<>();
data.add(new TaskEx()); // Add the data you need
TableColumn progressColumn = new TableColumn("Progress");
progressColumn.setCellValueFactory(new PropertyValueFactory("progress"));
progressColumn.setCellFactory(column -> {
return new TableCell<TaskEx, Double> {
private final ProgressBar bp = new ProgressBar();
#Override
public void updateItem(Double item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
}
else {
bp.setProgress(item.doubleValue());
setGraphic(bp);
}
}
}
});
table.setItems(data);
table.getColumns().add(progressColumn);
ExecutorService executor = Executors.newFixedThreadPool(data.size());
for (TaskEx task : table.getItems()) {
executor.submit(task);
}
}
This implement removes ThreadClass because there should not be any logic that must be done at a thread sub-class. If you really need to access the thread object as part of your logic, call Thread.getCurrentThread() from your TaskEx.call().
This implement also opens multiple threads doing exactly the same thing (which is quite meaningless). If you need to do a set of different logics, you can either make a set of different Task subclasses, or add a constructor taking in Runnable objects in TaskEx.
E.g.
public class TaskEx extends Task<Void> {
private final Runnable[] logics;
public TaskEx(Runnable[] logics) {
this.logics = logics;
}
#Override
protected Void call() {
for (int i = 0; i < logics.length; i++) {
logics[i].run();
updateProgress(i, logics.length);
}
}
}

Java new Thread using this - Any reason if it is good / bad?

Just a quick question look at the code below, is there any reason why wouldn't do this or is it fine?
public class MyClass implements Runnable, MyClassInterface {
Thread threader;
void start() {
threader = new Thread(this);
threader.start();
}
#Override
public void run() {
Thread current = Thread.getCurrentThread();
while (threader = current) {
..
}
}
}
The original logic was not to expose that fact it runs in a separate thread to the caller
who creates a "MyClass" but then there are doubts if that is a good thing or bad.
Can anyone see any good reason not to do it or is it acceptable. It can be expected that MyClass.start() maybe called a few times.
EDIT: Updated the code to show it is implementing Runnable and one other interface, the interface is used by client code, the actual implementation may run in a separate thread, same thread or any other way. The idea was to abstract that away from the client, as the client is simply an object that "MyClass" will notify and is not aware (currently) of the Runnable interface it implements.
Perhaps that abstraction is not needed and client should have more control?
EDIT: The start() was simply to tell the object it is ready to start receiving notifications rather than start a thread.
Have a look at this: http://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html
In my opinion, it is a bad design, because you are breaking encapsulation by implementing an interface (Runnable) and by providing a public method (run) that are of no use of the consumer of the class.
You can start a thread from the start method without inhering from Runnable:
public class MyClass {
private Thread thread;
public void start() {
thread = new Thread(this::doWork); // Java 8 method reference
thread.start();
}
private void doWork() {
// ...
}
}
If you can't use method references from Java 8, replace this::doWork with:
new Runnable() { public void run() { doWork(); } }

JTextArea not updating dynamically

I have a JTextArea inside a class that I want to update dynamically. Currently it is only displaying the text I append to it after all the processing is done. I have tried to implement the following to fix it:
public NewConsole(){
initComponents();
}
public void write(final String s){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
textarea.append(s);
}
});
}
Console gets instantiated in a parent class as:
protected NewConsole console = new NewConsole();
and to output to it, all the children call:
console.write("Append this..");
EDIT: Here's some more information:
public abstract class Parent{
protected NewConsole console = new NewConsole();
public Parent(){}
protected abstract int doSomething();
}
public class Child extends Parent{
public Child(){
console.write("I want this to update dynamically");
doSomething();
console.write("And this..");
}
public int doSomething(){
//Quite intensive processing here
}
}
The intensive processing done in doSomething is blocking the EDT, preventing UI updates. Use a SwingWorker instead to perform this functionality.
Use execute to start the worker. Move any required calls to console.write to either doInBackground or done.
You might try calling invokeAndWait() in place of invokeLater(), but in fact there is not enough information to be sure of an answer here.
I think of invokeLater() as "put this in your queue of things to do", and invokeAndWait() as "put this in the queue of things to do, and I'll suspend while you do them". I don't know if this change will fix your problem, but it seems like something to try based on what you've told us.

Categories