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.
Related
I have a class (lets say SocketClass) which extends AsyncTask (I am using Sockets, that's why I am using AsyncTask). I am calling that class on a different class which runs on the main thread.
SocketClass socketClass = new SocketClass(input);
socketClass.execute();
System.out.println(socketClass.getOutput());
This is my SocketClass
public class SocketClass extends AsyncTask < String, Void, Void > {
int output;
int input;
public Offload(int input) {
this.input = input;
}
public int getOutput() {
return output;
}
public void doSomething() {
// sockets related code
// set value to the output variable
}
#Override
protected Void doInBackground(String...voids) {
doSomething();
return null;
}
}
When I run the application System.out.println(socketClass.getOutput()); will get executed before a value fetch to the output variable. Is it possible to execute System.out.println(socketClass.getOutput()); only after fetching a value to output variable in doSomething() method? There are some solutions in Stackoverflow such as Solution1 and Solution2, but I am afraid to use these because I dont know whether it will affect badly to the application since we want to hold some process which are in the main thread
You can call AsyncTask.get() to get the result back after doInBackground completes.
new SocketClass().execute().get();
NOTE: this will cause the Main thread to hang while it waits.
I made the following sample program to check if onPostExecute will work after doInBackground.
For this case, my understanding is that doPostExecute works after the doInBackground using the result of doInBackground which is String ab.
But after the doInBackground, it goes to AsyncTask(#Nullable Looper callbackLooper) which is in AsyncTask.java.
I am not sure why it does not go to onPostExecute even though doInBackground processed normally with appropriate form to be forwarded to onPostExecute.
Are there any missing things that need to be taken care of?
public void search(String word){
AsyncSearch as = new AsyncSearch();
as.execute(word);
}
private class AsyncSearch extends AsyncTask<String, Void, String> {
protected String doInBackground(String... word) {
try {
String string = aaa(word[0]);
return string;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
protected void onPostExecute(String string){
System.out.println(string);
//and forward the string to the other method
}
public String aaa(String word) throws IOException {
String ab = word + "test";
return ab;
}
}
At this point, after the doInBackground(), the following in AsyncTask was called somehow.
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
And the following error came up on the console.
I am not sure if that relates to the above phenomenon.
Would you mind giving me some help?
Thank you.
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
}
}
}
Tons of JProgressBar questions on here I know, but through all the answers and I can't seem to diagnose my issue. I am processing a file with some address verification software. I click the Process button and I need my JProgressBar to update with each file processed.
Here is the button:
private JButton getJButton0() {
...
jButton0.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
jButton0ActionActionPerformed(event);
t.start();
}
...
Per everybody's recommendation, I used the setValue() method within a thread
Thread t = new Thread(){
public void run() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
jProgressBar0.setValue(BulkProcessor.getPercentComplete());
}
});
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
...
BulkProcessor.getPercentComplete() is a method I'm calling from another class which represents the percentage complete. I have tested this method and it updates correctly. The issue is that the progress bar will not update until the files are finished processing, and then it will jump to 100%. I apologize if this is a repeat question, but I have done some serious digging on this site with no luck. Any help much appreciated.
Edit:
Per recommended duplicate, I tried this:
public void update(){
new SwingWorker<Void,Void>() {
protected Void doInBackground() throws Exception {
jProgressBar0.setValue(BulkProcessor.getPercentComplete());
return null;
};
}.execute();
}
And then tried calling this update() method under the actionPerformed() (switched t.start() with update()). I am still having the same issue.
Edit
Based on user1676075's recommendation, however same issue:
public static void update(){
new SwingWorker<Void,Integer>() {
protected Void doInBackground() throws Exception {
do
{
percentComplete = BulkProcessor.getPercentComplete();
publish(percentComplete);
Thread.sleep(100);
} while(percentComplete < 100);
return null;
}
#Override
protected
void process(List<Integer> progress)
{
jProgressBar0.setValue(progress.get(0));
}
}.execute();
}
Edit
Here is the code from my BulkProcessor class
private String getOutputLine( String searchString, String inputLine )
throws QasException
{
..(code for processing lines)..
countRecord++;
percentComplete = (int) Math.round((countRecord/totalRecord)*100);
totalRecord is updated in the main class of my BulkProcessor class
public static void main( String input, String output ){
count.clear();
try{
String inputFile = input;
String outputFile = output;
LineNumberReader lnr = new LineNumberReader(new FileReader(new File(input)));
lnr.skip(Long.MAX_VALUE);
totalRecord = lnr.getLineNumber() + 1; //line count in file
BulkProcessor bulk = new BulkProcessor(inputFile, outputFile, ConfigManager.DFLT_NAME);
bulk.process();
}catch(Exception e ){
e.printStackTrace();
}
}
Looks like you're mixing usages. See the SwingWorker documentation, example at the top: http://docs.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html.
Ideally you'd update your BulkProcessor in the doInBackground method of the SwingWorker, and that would call setProgress, and the jProgressBar would be listening for those progress updates as in the example.
If that won't work for you, which it seems like it won't just based on the above, start a SwingWorker from the button press event. Implement the SwingWorker methods kinda like this (pseudocode):
new SwingWorker<Void,Integer>()
{
doInBackground()
{
do
{
percentComplete = BulkProcessor.getPercentComplete();
publish(percentCompete);
Thread.sleep(100);
} while (percentComplete < 100);
}
#Override
process(List<Integer> progress)
{
jProgressBar0.setValue(progress.get(0));
}
}.execute();
You'll need to add error-handling and checks for complete and failure cases, but that should get you started and to where you want to be. doInBackground runs in a background thread so won't block anything, and process() runs on the swing worker thread so will post the updates.
The mistake you probably went on is calling the t.start(); after thejButton0ActionPerformed(event); which makes that after the action is performed the thread will start. Therefore the value of the progress bar is not updated as intended.
You need to start the thread in jButton0ActionPerformed(event); and then update the value in it.
Just a hunch, but...
percentComplete = (int) Math.round((countRecord/totalRecord)*100);
Are you sure this is not integer arithmetic? I don't know the type of totalRecord, so I can't say for sure.
I'd guess everything works fine, and just the progress is 0 all the time, until complete where it magically is 100. This is because an int divided by an int will not have fraction values (ie. 99/100 == 0, 100/100 == 1). This fits perfectly with the symptoms you are experiencing.
Try replacing the line above with:
percentComplete = (int) Math.round((countRecord/(double) totalRecord)*100);
to see it I'm right. :-)
Have you tried to use the PropertyChangeListener-interface?
The calculations will be done by the Swingworker-thread and the main-gui will implement this interface. Some example-code
#Override
public void actionPerformed(ActionEvent e) {
this.myButton.setEnabled(false);
MyWorkerThread thread = new MyWorkerThread(); //Data-processing
thread.addPropertyChangeListener(this.mainguiframe); //Separation of concern
thread.execute();
}
Using the "setProgress"-method of the swing-worker-thread the main-gui-thread will be notified if something has happend.
#Override
public void propertyChange(PropertyChangeEvent property) {
Integer currentValue = new Integer(0);
currentValue = (Integer) property.getNewValue();
this.progressBar.setValue(currentValue.intValue());
}
Swing is not thread-safe. This is not the best solution but perhaps it can help you. Please comment if there is somethin horrible wrong.