I dont know what I am doing wrong here. I am iterating over a list in my doInBackground method but the loop always exits at the first iteration.
class ScreenerDataLoader extends SwingWorker<Void,Integer> {
protected Void doInBackground() throws Exception {
List<TickerStatistics> tickers = rc.getAll24HrPriceStatistics(); //2000 items
progressBar.setMaximum(tickers.size());
for(int i=0; i< tickers.size();i++){
System.out.println(i); //logs 0 then done is fired
markets.add(new Market(tickers.get(i).getSymbol(),timeframe));
publish(markets.size());
}
return null;
}
protected void process(List<Integer> chunks) {
progressBar.setValue(chunks.get(chunks.size()-1));
}
protected void done() {
System.out.println("done");
}
}
Always call the get() method of the SwingWorker when it has finished, even if no result is expected. This can be added to the done() method.
If the doInBackground() method terminated with some Exception, the get() method will throw an ExecutionException with more details. If get() is not called, the Exception will go unnoticed.
The documentation of get() has a hint:
Throws:
. . .
ExecutionException - if the computation threw an exception
Related
I am trying to implement the SwingWorker Thread in updating my GUI. I understand that the process() method is executed by the EDT, so if i need to update the GUI, I should place the update code within that method.
However, when i try to override the process() method, I get the error, Method does not implement a method from the supertype.
Please am I Missing something or the Process() method no longer exist?
class SwingWorkerThread extends SwingWorker<String, String> {
#Override
protected String doInBackground() throws Exception {
String Pub = "A";
for (int i = 0; i < 20; i++) {
Pub = String.valueOf(i);
publish(Pub);
}
return Pub;
}
#Override
protected String process(String h) {
System.out.println(Pub);
MainFrame.TextArea.setText(Pub);
return null;
}
#Override
protected void done() {
String status;
status = get();
try {
System.out.println("Done");
} catch (Exception ex) {
System.out.println("Error: " + ex);
}
}
}
There is no String process(String) method on SwingWorker. There is a void process(List<V>), which is probably what you want.
(That still won't fix the fact that Pub is a local variable and not visible in that method.)
SwingWorker's processmethod has different arguments and a different return type:
protected void process(List<V> chunks)
Only methods of a subclass with the same signature (that is: name plus number and type of its parameters) and return type can override superclass methods, cf. https://docs.oracle.com/javase/tutorial/java/IandI/override.html.
I have someclass which do large network operations and it do take some time to complete,and hence i put it in AsyncTask .I have to do this process 'n' times so in the main thread using a for loop i called this asynctask n times.will it throw any error since there is an interrupt in completing the for loop.?
// inside main thread
for(i=0;i<n;i++)
{
new asynctask().execute(new someclass());
}
Running mutliple AsyncTask is not recommended, but if it is few times, then it will work but all async task will run serially not in parallel. But if you want async tasks to run parallelly then you can call it's executeOnExecutor(..) method where you have to pass THREAD_POOL_EXECUTOR as parameter. You can search on google you can find many links. Here is an example for your help.
don't call AsyncTask n times just put your for loop in onPostExecute() and do any task up to n times
private class AsyncTaskRunner extends AsyncTask<String, String, String> {
#Override
protected String doInBackground(String... params) {
} catch (Exception e) {
e.printStackTrace();
}
return resp;
}
#Override
protected void onPostExecute(String result) {
// execution of result of Long time consuming operation
for(i=0;i<n;i++)
{
//do your operation here
}
}
#Override
protected void onPreExecute() {
// Things to be done before execution of long running operation. For
// example showing ProgessDialog
}
#Override
protected void onProgressUpdate(String... text) {
// Things to be done while execution of long running operation is in
// progress. For example updating ProgessDialog
}
}
}
I'm using a SwingWorker as a way to wrap non swing tasks/actions with a before and after methods always running in the Event Dispatch Thread (in other words: I invoke before then I invoke an abstract method in the doInBackground and then I invoke after in the done method). This started causing a lot of trouble lately since whatever happens in doInBackground has to be synchronized. I noticed that the problem goes away if I call run instead of execute, but from what I understand the run method works the same as in Thread, it just starts the execution in the exact same thread it has been called from instead of creating a new one, and as such the doInBackground executes on the EDT if I call run from the EDT. Is my thinking correct? How can I synchronize the doInBackground method with the thread calling execute on the worker?
I used the following code but it seems to be creating a thread lock:
private Object LOCK = new Object();
public final void method() {
someObject.before();
SwingWorker<Void, Void> worker1 = new SwingWorker<Void, Void>() {
protected Void doInBackground() throws Exception {
methodExt();
return null;
}
protected void done() {
someObject.after();
synchronized (LOCK) {
LOCK.notifyAll();
}
}
};
worker1.execute();
synchronized (LOCK) {
try {
LOCK.wait();
} catch (InterruptedException exception) {
exception.printStackTrace();
}
}
}
You should never block on the EDT. The whole point of SwingWorker is that it allows you to trigger a task to run on a background thread and schedule additional code to run on the EDT after the background job is complete without blocking the EDT in the meantime. So if you want to run methodA() on the EDT, then methodB() in the background, then methodC() back on the EDT after B has completed, the answer is to call methodC() from the done() method of the SwingWorker.
Instead of trying to do
doSomething();
method(); // which blocks waiting for the BG task to complete
doSomethingElse();
you could modify method() to
public final void method(final Runnable callback) {
someObject.before();
SwingWorker<Void, Void> worker1 = new SwingWorker<Void, Void>() {
protected Void doInBackground() throws Exception {
methodExt();
return null;
}
protected void done() {
someObject.after();
if(callback != null) callback.run();
}
};
worker1.execute();
}
and then call it as
doSomething();
method(new Runnable() {
public void run() {
doSomethingElse();
}
});
Now method returns immediately and does not block the EDT, but doSomethingElse still happens on the EDT after the background task is complete.
So I got into SwingWorkers to handle my Text manipulating with different Classes and Threads. As shown below, my Swingworker gets a filepath and scans the text, passing the lines to a String. With getData() I return the scanned String to my main Class. But this does not work until I run the method scanFile()in the Constructor of my Worker Class. So my Question is: Why does my SwingWorker Class not run the doInBackground() properly?
public class ScanWorker extends SwingWorker<Void, Void> {
private File file;
private String text;
ScanWorker(File file) {
this.file = file;
}
#Override
protected Void doInBackground() throws Exception {
scanFile();
return null;
}
public String getData() {
return text;
}
private void scanFile() {
String line = "";
try {
Scanner scan = new Scanner(file);
while(scan.hasNextLine()) {
line = scan.nextLine();
if(!scan.hasNextLine()) {
text += line;
} else {
text += line + "\n";
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
You should not be adding your own getData() method for the return value, because this isn't thread-safe. It's precisely what SwingWorker tries to avoid by providing its own mechanism for transferring the result back to the Swing thread. The result type is the first type parameter of the SwingWorker, so instead of SwingWorker<Void,Void>, you should have SwingWorker<String,Void>.
Change protected Void doInBackground() to protected String doInBackground() (since that is your result type).
Remove the text field, and instead return the result String from doInBackground().
After you return the result from the doInBackground() method on the worker thread, the done() method will be called on the Swing thread. You're missing that method. From that method you can call get() to retrieve the result. Add the method as follows:
#Override
protected void done() {
String result;
try {
result = get();
} catch (Exception e) {
throw new RuntimeException(e);
}
// Now do whatever you want with the loaded data:
...
}
SwingWorker doesn't do anything until you start it by calling its execute() method. Make sure you're doing that!
P.S. You shouldn't build up the text in a String variable directly, as that recopies the entire string every time you append a line to it, which gives atrocious performance. Use a StringBuilder for this sort of thing, calling toString() on it only at the end.
Or, since there is no point splitting the file to lines if you only want to join them back together again, you can read the full file in one go:
#Override
protected String doInBackground() throws Exception {
return new String(
java.nio.file.Files.readAllBytes(file.toPath()),
java.nio.charset.StandardCharsets.UTF_8);
}
If you put scanFile() in the constructor, then you executed that method on the current thread, defeating the purpose of SwingWorker.
Your pull-based approach where you getData() from the worker, probably immediatly after you execute it, is wrong: you must instead override done() and do any work related to the produced results. Alternatively you may use the publish and process approach where you receive pieces of data in the EDT as they are being produced by the worker thread.
Say I have a class call MyTask
Every time I new a object for MyTask, it will create a thread
boolean mContinueThread = true;
public MyTask (Activity activity) {
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
while (mContinueThread) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
});
thread.start();
return null;
};
}.execute();
}
At first I new myTask1, myTask2, myTask3 then add to the List
List<MyTask> myTasks = new ArrayList<MyTask>;
myTasks.add(myTask1);
myTasks.add(myTask2);
myTasks.add(myTask3);
Now there should be 3 threads run on the background, then I renew the by
myTasks = new ArrayList<MyTask>;
And looks like those threads in myTask1, myTask2, myTask3 are still running, the finalize never get called. I know I can set mContinueThread as true for each MyTask objects before I renew the list, but I would like to know is there any way (callback?) I can know those MyTask objects are not no longer in the list and then set mContinueThread as false?
public MyTask (Activity activity) {
...
#Override
protected void finalize() throws Throwable {
mContinueThread = false;
super.finalize();
}
It seems redundant to have an async task which just starts a Thread.You can achieve the desired outcome, by puting the contance of the thread directly into you AsyncTask$doInBackground()
You can call the call the AsyncTask$cancel(boolean mayInterrupt) method, this will rise an InterruptedException, the only thing left to do, is adding a return statement within the catch:
#Override
protected Void doInBackground(Void... params) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// cancel was called
return null;
}
return null;
};
Cancel the task like that:
myTasks.get(i).cancel(true);
Don't forget to pass true or it won't work
you have to have a variable for your AsyncTask first, so that you can call:
myTasks.get(0).getMyAsyncTask().cancel(boolean);
As you can see here, it is not that easy to cancel an AsyncTask. After you call cancel(boolean), it will: (from docs:
invoking this method will cause subsequent call to isCancelled() to
return true. onCancelled(Object) will be invoked after doInBackground
instead of onPostxecute. To ensure that a task is cancelled, you
should always check the return value of isCancelled from
doInBackground.
So, you call cancel(booelan) onto your reference variable of your AsyncTask and in doInBackground, you always check for isCancelled() in order to stop some processes in your doInBackground method.
You should handle your doInBackground method manually in order to stop all the executions there. So if you have while(true){ do_something }, then it should become while(true){ if(!isCancelled()) { do_something } }