I have several different AsyncTasks that need to perform the same shared basic operation and show progress while doing so. Rather than copying a lot of code several times (with progress showing in each) is there a way to share the code by passing in the other class? The AsyncTasks are very different and you can't just pass in the Abstract, which has a protected publishProgress() method anyway. Looked at a bunch of examples and tried to a lot of different variations without success.
So here are the details...lets say we have two different tasks doing different stuff, but sharing the need to hit a server, download a number of files and then put them in a DB (and then do other stuff):
public class ExampleTaskFragment extends ListFragment {
private ProgressDialog mProgress;
private void hideProgress() {
mProgress.dismiss();
}
private void showProgress(String message) {
mProgress = ProgressDialog.show(getActivity(), null, message, true, false);
}
protected void updateProgressMessage(String message) {
mProgress.setMessage(message);
}
public class TaskA extends AsyncTask<Object, String, ArrayList<A>> {
public void progressUpdate(String... values) {
publishProgress(values);
}
#Override
protected void onCancelled() {
hideProgress();
}
#Override
protected void onPreExecute() {
showProgress("Stuff before shared...");
}
#Override
protected ArrayList<A> doInBackground(Object... params) {
// DoStuffPublish is the common stuff both tasks share
DoStuffPublish dostuff = new DoStuffPublish(this);
return doMoreStuffA();
}
#Override
protected void onPostExecute(ArrayList<A> result) {
updateProgressMessage("After shared stuff...");
hideProgress();
}
ArrayList<A> doMoreStuffA() {
return new ArrayList<A>();
}
}
public class TaskB extends AsyncTask<Object, String, ArrayList<B>> {
public void progressUpdate(String... values) {
publishProgress(values);
}
#Override
protected void onCancelled() {
hideProgress();
}
#Override
protected void onPreExecute() {
showProgress("Stuff before shared...");
}
#Override
protected ArrayList<B> doInBackground(Object... params) {
// DoStuffPublish is the common stuff both tasks share
DoStuffPublish dostuff = new DoStuffPublish(this);
return doMoreStuffB();
}
#Override
protected void onPostExecute(ArrayList<B> result) {
updateProgressMessage("After shared stuff...");
hideProgress();
}
ArrayList<B> doMoreStuffB() {
return new ArrayList<B>();
}
}
}
public class DoStuffPublish {
private AsyncTask task;
public DoStuffPublish(AsyncTask task) {
super();
this.task = task;
doSharedStuff();
}
protected void publishProgress(String message) {
if (task instanceof TaskA) {
((TaskA)task).progressUpdate(message);
}
else if (task instanceof TaskB) {
((TaskB)task).progressUpdate(message);
}
}
public void doSharedStuff() {
publishProgress("Doing Shared stuff...");
// Do lots of shared things here...
publishProgress("Done doing shared stuff...");
}
}
This code has been cobbled together - so don't get too hung up on the syntax. The concept it there...I want to have a shared class I can call in 2 or more tasks that can share that code and still publish updates. I have tried putting the ProgressDialog in the tasks and outside without success. When I have done so I get "Only the original thread that created a view hierarchy can touch it's views". And in the debugger you can see the two different threads - the original main UI thread and the thread created in the Form doInBackground(Object... params).
I fear this may not be possible...
robinj - had it correct, just needed to read his/her answer a little better. The trick was to code it like this:`public class ExampleTaskFragment extends ListFragment {
private ProgressDialog mProgress;
private void hideProgress() {
mProgress.dismiss();
}
private void showProgress(String message) {
mProgress = ProgressDialog.show(getActivity(), null, message, true, false);
}
protected void updateProgressMessage(String message) {
mProgress.setMessage(message);
}
public class TaskA extends AsyncTask<Object, String, ArrayList<A>> {
public void progressUpdate(String... values) {
publishProgress(values);
}
#Override
protected void onProgressUpdate(String... values) {
updateProgressMessage(values[0]);
}
#Override
protected void onCancelled() {
hideProgress();
}
#Override
protected void onPreExecute() {
showProgress("Stuff before shared...");
}
#Override
protected ArrayList<A> doInBackground(Object... params) {
// DoStuffPublish is the common stuff both tasks share
DoStuffPublish dostuff = new DoStuffPublish(this);
return doMoreStuffA();
}
#Override
protected void onPostExecute(ArrayList<A> result) {
updateProgressMessage("After shared stuff...");
hideProgress();
}
ArrayList<A> doMoreStuffA() {
return new ArrayList<A>();
}
}
public class TaskB extends AsyncTask<Object, String, ArrayList<B>> {
public void progressUpdate(String... values) {
publishProgress(values);
}
#Override
protected void onProgressUpdate(String... values) {
updateProgressMessage(values[0]);
}
#Override
protected void onCancelled() {
hideProgress();
}
#Override
protected void onPreExecute() {
showProgress("Stuff before shared...");
}
#Override
protected ArrayList<B> doInBackground(Object... params) {
// DoStuffPublish is the common stuff both tasks share
DoStuffPublish dostuff = new DoStuffPublish(this);
return doMoreStuffB();
}
#Override
protected void onPostExecute(ArrayList<B> result) {
updateProgressMessage("After shared stuff...");
hideProgress();
}
ArrayList<B> doMoreStuffB() {
return new ArrayList<B>();
}
}
}
public class DoStuffPublish {
private AsyncTask task;
public DoStuffPublish(AsyncTask task) {
super();
this.task = task;
doSharedStuff();
}
protected void publishProgress(String message) {
if (task instanceof TaskA) {
((TaskA)task).progressUpdate(message);
}
else if (task instanceof TaskB) {
((TaskB)task).progressUpdate(message);
}
}
public void doSharedStuff() {
publishProgress("Doing Shared stuff...");
// Do lots of shared things here...
publishProgress("Done doing shared stuff...");
}
}
`
Note the
#Override
protected void onProgressUpdate(String... values) {
updateProgressMessage(values[0]);
}
Previously I had been trying to directly set the mProgress value, but it was in the wrong thread? Anyway thanks to robinj for the help! If you put an answer in I will thumbs it up...
Related
MainActivity execute external asynctask class
Here my code
public class MainActivity extends AppCompatActivity implements View.OnClickListener
...
public void onFirstBtnClick()
{
AysncClass ac = new AyncClass();
ac.execute();
}
and external asynctask
public class AysncClass extends AsyncTask<String, String, Integer>
#Override
protected Integer doInBackground(String... strings) {
Method1(strings[0], Integer.parseInt(strings[1]));
return null;
}
public Method1(Strins s, int i)
{
onProgressUpdate("first start");
publishProgress();
// do more work
onProgressUpdate("second start");
publishProgress();
}
public void Method2()
{
onProgressUpdate("Method2 here");
publishProgress();
}
...
#Override
protected void onProgressUpdate(final String... values) {
super.onProgressUpdate(values);
// what can i do here?
}
}
I use runOnUiThread like this in onProgressUpdate
((MainActivity)context).runOnUiThread(new Runnable()
{
#Override
public void run()
{
((MainActivity)context).tvRead.append(values[0]);
}
});*
but It occur 'java.lang.ArrayIndexOutOfBoundsException: length=0; index=0' even though values[0] is not null.
Also I use interface
this.context.WriteText(values[0]);
It occur same error
And I do this...
((MainActivity)context).tvRead.append(values[0]);
It occur 'java.lang.RuntimeException: An error occured while executing doInBackground()' and 'CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.'
...How can I resolve?
Do not call onProgressUpdate(). Instead just call publishProgress("The String").
By calling publishProgress without a parameter you will have no values in onProgressUpdate. That's why you get an IndexOutOfBondException.
Don't call onProgressUpdate on your own.
Call publishProgress("My Message");
In onProgressUpdate, don't use runOnUIThread.
OnProgressUpdate runs on the Ui thread only.
Instead of this
onProgressUpdate("Method2 here");
publishProgress();
Do this
publishProgress("Method2 here");
On ProgressUpdate will be in UIThread, no need again specify in UI thread. From DoInbackGround you have to make call publishProgress method for invoking OnProgressUpdate.
Use an interface as communicator between your Activity and AsyncTask
Create Interface
interface MyInterface {
void callActivityUi(String progress); // use float maybe
}
Initialize AsyncTask like this:
AysncClass as = new AysncClass(new MyInterface() {
#Override
void callActivityUi (String progress) {
// you will receive data here
}
});
Create a constructor in your AysncClass
private MyInterface myInterface;
public AysncClass (MyInterface myInterface) {
this.myInterface = myInterface;
}
Call Activity in onProgressUpdate(String... values)
myInterface.callActivityUi(values[0]);
Based on your code, you can use an interface to post the progress to the activity. I modified your code like this:
AsyncTask class
public class AysncClass extends AsyncTask<String, String, Integer> {
public interface SomeListener {
public void onSomething(Object mObject);
}
private SomeListener sl;
public AysncClass(SomeListener sl) {
this.sl = sl;
}
#Override
protected Integer doInBackground(String... strings) {
Method1(strings[0], Integer.parseInt(strings[1]));
return null;
}
public Method1(Strins s, int i) {
onProgressUpdate("first start");
publishProgress();
// do more work
onProgressUpdate("second start");
publishProgress();
}
public void Method2() {
onProgressUpdate("Method2 here");
publishProgress();
}
...
#Override
protected void onProgressUpdate(final String... values) {
super.onProgressUpdate(values);
// what can i do here?
sl.onSomething(mObject);
}
}
MainActivity class
public class MainActivity extends AppCompatActivity implements View.OnClickListener,
AysncClass.SomeListener {
...
public void onFirstBtnClick() {
AysncClass ac = new AyncClass(this);
ac.execute();
}
#Override
public void onSomething(Object mObject) {
//Do your UI work here
}
}
I have a specific scenario and I need your help.
I'm trying to build an App in Android that involves network communication.
I am using AsyncTask for the http POST requests.
I have another class called Proxy (not a good one.. will be changed) which holds different kinds of functionalities (registerUser, setUserName, getUserPermission...)
And Of course, I have an Activity.
My Activity holds an instance of Proxy class.
My goal, is to push a button in the activity, it will call a method from Proxy class, which in its turn calls the AsyncTask's execute() method that actually run the http POST.
I was wondering how to get the data from AsyncTask's onPostExecute to my activity.
What I have in mind is to have an interface in AsyncTask, which will be implemented in Proxy class, and another interface in Proxy class which will be implemented in my Activity class.
Roll the data all the way to my Activity.
I want to hear your thoughts about whether this is the way to go, or another approach is preffered.
Thanks a lot for your help.
Adding some code
public class RegisterActivity extends FragmentActivity implements Proxy.OnProxyHttpPostResponseListener {
private Proxy proxy;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register);
this.proxy = new Proxy();
this.proxy.setHttpPostResponseListener(this);
}
#Override
public void onProxyHttpPostResponse(String response) {
//Do something when http post returns
}
}
public class Proxy {
public interface OnProxyHttpPostResponseListener {
void onProxyHttpPostResponse(String response);
}
private OnProxyHttpPostResponseListener httpPostResponseListener;
public void setHttpPostResponseListener(OnProxyHttpPostResponseListener listener) {
this.httpPostResponseListener = listener;
}
private class HttpPostAsync extends AsyncTask<Pair<String, ArrayList<Pair<String, String>>>, Void, String> {
#Override
protected String doInBackground(Pair<String, ArrayList<Pair<String, String>>>... params) {
return this.httpPost(params[0].first, params[0].second);
}
protected void onPostExecute(String response) {
httpPostResponseListener.onProxyHttpPostResponse(response);
}
}
If you're just needing HTTP POST functionality then an AsyncTask might not be the best choice. AsyncTask really shines if you need to get progress updates as the task is executing (with onProgressUpdate(Progress... progress)). If you'd like to use AsyncTask nonetheless, iroiroys' reply should help.
A bit more simply, you could just use a Handler thread straight up. Something like this:
public class HandlerExampleActivity extends Activity {
private Button postButton;
private Button getButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_example);
backgroundThread = new BackgroundThread();
backgroundThread.start();
postButton = (Button) findViewById(R.id.button_post);
postbutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
backgroundThread.post("DATA_HERE");
}
});
getButton = (Button) findViewById(R.id.button_get);
getbutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
backgroundThread.get("URL_HERE");
}
});
}
#Override
protected void onDestroy() {
super.onDestroy();
backgroundThread.exit();
}
private class BackgroundThread extends Thread {
private Handler backgroundHandler;
public void run() {
Looper.prepare();
backgroundHandler = new Handler();
Looper.loop();
}
public void post(DataType data) {
backgroundHandler.post(new Runnable() {
#Override
public void run() {
// pull data and do the POST
uiMsg = uiHandler.obtainMessage(POST_COMPLETE, whatever_data_passing_back, 0, null);
uiHandler.sendMessage(uiMsg);
}
});
}
public void get(URL data) {
backgroundHandler.post(new Runnable() {
#Override
public void run() {
// GET data
uiMsg = uiHandler.obtainMessage(GET_COMPLETE, whatever_data_passing_back, 0, null);
uiHandler.sendMessage(uiMsg);
}
});
}
public void exit() {
backgroundHandler.getLooper().quit();
}
}
private final Handler uiHandler = new Handler() {
public void handleMessage(Message msg) {
switch(msg.what) {
case POST_COMPLETE:
// handle it
break;
case GET_COMPLETE:
// handle it
break
case MESSAGE_BACK_TO_UI_THREAD:
// do something
break;
case OPERATION_FAIL:
// oh no!
break;
case OPERATION_SUCCESS:
// yay!
break;
}
}
};
}
I suggest you try Handler and Handler.Callback.
Below I made it simple example..
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Message;
public class MainActivity extends Activity implements Callback {
Handler handler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler = new Handler(this);
Proxy proxy = new Proxy(handler);
proxy.foo();
}
private class Proxy {
Handler handler;
public Proxy(Handler handler) {
this.handler = handler;
}
private void foo() {
new myAsync().execute();
}
private class myAsync extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
Message msg = handler.obtainMessage();
msg.obj = result;
handler.sendMessage(msg);
}
}
}
#Override
public boolean handleMessage(Message msg) {
// Handle Message here!
return false;
}
}
I have AsyncTask class and I call it in my main class. I need to override the onPostExecute function and call inside ftpDisconnect(). But it does not work properly.
TempClass dj = new TempClass(serialnum) {
#Override
protected void onProgressUpdate(Integer... values) {
pr_bar.setProgress(values[0]);
super.onProgressUpdate(values);
}
#Override
protected void onPostExecute(String result) {
//dj.ftpDisconnect(); //need to make this happen
super.onPostExecute(result);
}
};
dj.execute();
My TempClass:
public class TempClass extends AsyncTask<String, Integer, String> {
public TempClass(String serialnum) {
this.serialnum = serialnum;
}
#Override
protected String doInBackground(String... params) {
//do stuff
return null;
}
public boolean ftpDisconnect() {
try {
mFTPClient.disconnect();
return true;
} catch (Exception e) {
}
return false;
}
}
You can't access that instance of dj without making it final. You should be able to call ftpDisconnect from inside the class:
#Override
protected void onPostExecute(String result) {
ftpDisconnect();
super.onPostExecute(result);
}
My asynctask not going in onProgressUpdate. why? I'm going to create progress bar in asynctask. Below is my code to show uploading image by using progress bar.
public static class RegisterDataEngineForPost extends AsyncTask<MultipartEntity, integer, String>{
ProgressBar ProgressBarUpload;
public void setmCurrentPhotoPath(ProgressBarUploadb) {
ProgressBarUpload = ProgressBarUploadb;
}
#Override
protected void onProgressUpdate(integer... values) {
}
#Override
protected void onPreExecute() {
ProgressBarUpload.setProgress(0);
}
#Override
protected String doInBackground(MultipartEntity... params) {
}
#Override
protected void onPostExecute(String result) {
}
}
}
Use http://developer.android.com/reference/android/os/AsyncTask.html#publishProgress(Progress...)
manually, Android will not know your progress out of nothing, you need to determine it yourself.
You have to call publishProgress(progress_value) to update the progress like that
public static class RegisterDataEngineForPost extends AsyncTask<MultipartEntity, integer, String>{
ProgressBar ProgressBarUpload;
public void setmCurrentPhotoPath(ProgressBarUploadb) {
ProgressBarUpload = ProgressBarUploadb;
}
#Override
protected void onProgressUpdate(integer... values) {
}
#Override
protected void onPreExecute() {
ProgressBarUpload.setProgress(0);
}
#Override
protected String doInBackground(MultipartEntity... params) {
//do some task and update the progress
publishProgress(progress_value);
}
#Override
protected void onPostExecute(String result) {
}
}
}
I don't understand why I'm getting this error. I'm using AsyncTask to run some processes in the background.
I have:
protected void onPreExecute()
{
connectionProgressDialog = new ProgressDialog(SetPreference.this);
connectionProgressDialog.setCancelable(true);
connectionProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
connectionProgressDialog.setMessage("Connecting to site...");
connectionProgressDialog.show();
downloadSpinnerProgressDialog = new ProgressDialog(SetPreference.this);
downloadSpinnerProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
downloadSpinnerProgressDialog.setMessage("Downloading wallpaper...");
}
When I get into doInBackground() depending on a condition I:
[...]
connectionProgressDialog.dismiss();
downloadSpinnerProgressDialog.show();
[...]
Whenever I try downloadSpinnerProgressDialog.show() I receive the error.
Any ideas guys?
The method show() must be called from the User-Interface (UI) thread, while doInBackground() runs on different thread which is the main reason why AsyncTask was designed.
You have to call show() either in onProgressUpdate() or in onPostExecute().
For example:
class ExampleTask extends AsyncTask<String, String, String> {
// Your onPreExecute method.
#Override
protected String doInBackground(String... params) {
// Your code.
if (condition_is_true) {
this.publishProgress("Show the dialog");
}
return "Result";
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
connectionProgressDialog.dismiss();
downloadSpinnerProgressDialog.show();
}
}
I had a similar issue but from reading this question I figured I could run on UI thread:
YourActivity.this.runOnUiThread(new Runnable() {
public void run() {
alertDialog.show();
}
});
Seems to do the trick for me.
I had a hard time making this work too, the solution for me was to use both hyui and konstantin answers,
class ExampleTask extends AsyncTask<String, String, String> {
// Your onPreExecute method.
#Override
protected String doInBackground(String... params) {
// Your code.
if (condition_is_true) {
this.publishProgress("Show the dialog");
}
return "Result";
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
YourActivity.this.runOnUiThread(new Runnable() {
public void run() {
alertDialog.show();
}
});
}
}
final Handler handler = new Handler() {
#Override
public void handleMessage(final Message msgs) {
//write your code hear which give error
}
}
new Thread(new Runnable() {
#Override
public void run() {
handler.sendEmptyMessage(1);
//this will call handleMessage function and hendal all error
}
}).start();