I know this question has been asked multiple times, but I have an apparently clean code that just doesn't work, giving me no Exceptions or anything.
I have a trivial one-button Activity (MainActivity). The button in it calls an AsyncTask in order to send an email in the background. I tried to do what I guess it's a pretty common thing: show a ProgressDialog when the task starts and dismiss it when it ends. To do so I put the above-mentioned code into AsyncTask.onPreExecute() and AsyncTask.onPostExecute().
I thought the problem was in the Context provided to the dialog constructor, but I created a simple constructor for my AsyncTask to which I pass the application context. I added a simple Toast to debug, but it doesn't show up neither...
Here's the code for the button onClick method:
public void onClick(View v) {
new Sender(this).execute("args");
}
And here's the code for the task:
private class Sender extends AsyncTask<String, Void, Void> {
private ProgressDialog progDialog;
private Context context;
public Sender(Context context) {
this.context = context;
}
#SuppressWarnings("unused")
protected void onPreExecute(Void... params) {
Toast.makeText(this.context, "Sending...", Toast.LENGTH_SHORT).show();
progDialog = ProgressDialog.show(this.context, "Sending", "Picture is being sent...", true);
}
protected Void doInBackground(String... mailSubj) {
// some code that works
return null;
}
#SuppressWarnings("unused")
protected void onPostExecute(Void... v) {
progDialog.dismiss();
Toast.makeText(MainActivity.this, "Mail sent", Toast.LENGTH_SHORT).show();
}
}
Where did I go wrong?
Your progress dialog not show up because you don't override onPreExecute(). Add #Override annotation to your onPreExecute(), see what happens. onPreExecute takes no arguments.
I found the mistake and the solution is straight forward: adding a Void list of arguments to onPreExecute() and onPostExecute() caused Java to not override the methods from AsyncTask. The first needs no arguments and the latter needs a single Result (in this case Void) argument.
This question can be closed.
Related
I have an AsyncTask which does a lot of JSON calculations.
public class InitializationTask extends AsyncTask<Void, Void, InitializationResult> {
private ProcessController processController = new ProcessController();
private ProgressDialog progressDialog;
private MainActivity mainActivity;
public InitializationTask(MainActivity mainActivity) {
this.mainActivity = mainActivity;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog = new ProgressDialog(mainActivity);
progressDialog.setMessage("Die Daten werden aufbereitet.\nBitte warten...");
progressDialog.setIndeterminate(true); //means that the "loading amount" is not measured.
progressDialog.setCancelable(false);
progressDialog.show();
};
#Override
protected InitializationResult doInBackground(Void... params) {
return processController.initializeData();
}
#Override
protected void onPostExecute(InitializationResult result) {
super.onPostExecute(result);
progressDialog.dismiss();
if (result.isValid()) {
mainActivity.finalizeSetup();
}
else {
AlertDialog.Builder dialog = new AlertDialog.Builder(mainActivity);
dialog.setTitle("Initialisierungsfehler");
dialog.setMessage(result.getReason());
dialog.setPositiveButton("Ok",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
mainActivity.finish();
}
});
dialog.show();
}
}
}
processController.initializeData() runs for about 20 seconds. All this works. It even works when I install the application, and pressing home button while application is initializing. The AsyncTask is working in the background. When I restart the application from Android device again after the AsyncTask has been finished, the application still works.
But the application cannot handle this use case: When I deploy the application (or start it when no data is initialized), so that it really takes about 20sec to initialize the data and when I hit home to close the application while initialization (the AsyncTask) runs in the background and start the application again, it leads to unexpected behavior as RuntimExceptions and so on. It seems that the device wants to load the application twice, but none of them can start successfully. How to deal with that?
I thought about checking if there is a running AsyncTask in MainActivity to avoid starting it again:
private InitializationTask initializationTask = new InitializationTask(this);
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
if (!AsyncTask.Status.RUNNING.equals(initializationTask.getStatus())) {
initializationTask.execute((Void[])null);
}
initializeMap();
}
Unfortunately this does do nothing. Moreover I'm not sure if such use case is possible at all, because when I start the same application twice, they cannot "share" an AsyncTask. Should I somehow avoid starting the application twice or something? I'm not sure what options do I have on this? Any ideas?
I usually do it a bit more bluntly. I set my AsyncTask reference to null when I'm not using it. When the onClick fires, I check if it's not null, which means I started it. If it is null, I create and execute a new AsyncTask right there. It is plenty fast and it's clean enough. One bonus of this approach is that an AsyncTask can only be executed once anyway, so it fits in well with that. At the end of onPostExecute, you can set the reference back to null again if you intend to stay in the same Activity.
While you're perfecting your AsyncTask flow, make sure that it survives orientation changes as well.
I found the solution: It's actually not an AsyncTask issue. The problem was that my parse method of JSONParser (that does the most of the work) which is invoked in processController.initializeData(), was not synchronized.
When a button is clicked I'm calling the async class in a function and I need to show progressDialog until it runs the displaylist function. But it shows up only after the function finished running and closes immediately. Please help me what am I doing wrong here.
public class FilterAsyncTask extends AsyncTask<Void, Void, Void> {
ProgressDialog dispProgress;
#Override
protected void onPreExecute()
{
dispProgress = ProgressDialog.show(Filter.this, "Please wait...",
"Loading...", true, true);
}
protected Void doInBackground(Void... params) {
return null;
}
protected void onPostExecute(Void result) {
super.onPostExecute(result);
MerchantsActivity.displayList();
dispProgress.cancel();
finish();
}
}
Your AsyncTask will complete immediately because you do exactly nothing in doInBackground()! That's where your long-running background non-UI code is supposed to go...
I would recommend you not to use the static ProgressDialog#show method. Rather donew ProgressDialog() and initialize it accordingly and finally call show(). I have never used the static method and do not know how it works, but I have used the other option. Furthermore the static method seems to have no available documentation.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
AsyncTask block UI threat and show progressbar with delay
I want to show a progressDialog while retrieving JSON from any server. So I had used AsyncTask as a solution (not sure any different way out).
Everything is fine, the ProgressDialog works properly until I call .get() method using AsyncTask instance. I suppose it's blocking UI somehow. Here is my AsyncTask:
public class myAsync extends AsyncTask<String, String, List> {
String message; // for dialog message
ProgressDialog progress;
Intent myIntent;
Context ctx;
public myAsync(String message, Context ctx) {
this.message = message;
this.ctx = ctx;
progress = new ProgressDialog(ctx);
}
#Override
protected void onPreExecute() {
progress.setMessage(message);
progress.setIndeterminate(true);
progress.setCancelable(false);
progress.show();
}
#Override
protected List doInBackground(String... params) {
//returns any list after the task
return anyList;
}
#Override
protected void onPostExecute(List result) {
if(progress.isShowing())
progress.dismiss();
}
}
And here is myActivity which is calls AsyncTask:
myAsync asyncTask = new myAsync("Loading...", this);
asyncTask.execute("Any string", "Other string");
asyncTask.get(); // If I comment out this line, ProgressDialog works
After execute, when I tried to log the result from doInBackground and onPostExecute both there is no problem. But if I want to get with .get() the result ProgressDialog is not shown or shown so little time (maybe 0.2 seconds)
What's the problem?
Yes, get() waits if necessary for the computation to complete, and then retrieves its result. This means, that you are blocking your UI thread, waiting for the result.
Solution: Don't call get
Usually, you will call a function (callback) in the postExecute.
Calling .get() changes your AsyncTask into an effective "SyncTask" as it causes the current thread (which would be the UI thread) to wait until the AsyncTask has finished its processing. Since you are now blocking the UI thread the call to the ProgressDialog's .show() method never gets a chance to allow the dialog to draw itself the screen.
Removing the call will allow it to run properly in the background.
If you need to do processing after the task has completed I suggest you either put it inside the onPostExecute method itself or use a callback to the Activity from onPostExecute.
If I understand your question correctly, you need to update the progress of your AsyncTask in a ProgressDialog and this isn't currently working. So a couple of things to note: I'm not sure what you're trying to achieve with .get() but I'll assume you want to display the progress.
I've modified your program below to update the UI thread with your AsyncTask's progress. Everytime you need to update the progress, update that prog variable in the doInBackground method.
public class myAsync extends AsyncTask<String, Integer, List> {
String message; // for dialog message
ProgressDialog progress;
Intent myIntent;
Context ctx;
public myAsync(String message, Context ctx) {
this.message = message;
this.ctx = ctx;
progress = new ProgressDialog(ctx);
}
#Override
protected void onPreExecute() {
// Runs on the UI thread
progress.setMessage(message);
progress.setIndeterminate(true);
progress.setCancelable(false);
progress.show();
}
#Override
protected List doInBackground(String... params) {
// Runs in the background thread
// publish your progress here!!
int prog = 5; // This number will represent your "progress"
publishProgress(prog);
return anyList;
}
protected void onProgressUpdate(Integer... progress) {
// Runs in the UI thread
// This method will fire (on the UI thread) EVERYTIME publishProgress
// is called.
Log.d(TAG, "Progress is: " +progress);
}
#Override
protected void onPostExecute(List result) {
// Runs in the UI thread
for (int i=0; i<result.size(); i++) {
Log.d(TAG, "List item: " + result.get(i));
}
if(progress.isShowing())
progress.dismiss();
}
}
Try using runOnUiThread like this:
runOnUiThread(new Runnable(){
public void run() {
dialog.show();
}});
Running something on a AsyncTask means that its running away from the UIthread so usually you cant run ui operations from inside Async methods without handlers and stuff which I usually stay away from. I also handle such a solution by creating a progressDialog as a variable in my class above my oncreate so its visible to the whole class. I then call the progressdialog right before my asynctask and then since its visible to the whole class I call .dissmiss() in the onPostExecute
I'm using following code to fill a custom ListPreference dialog. Since the fill procedure takes a lot of time i want to show a progress dialog during the fill procedure.
My problem is that filler.execute() does not block onPrepareDialogBuilder and functions goes till the end before values are filled causing an exception... Any idea?
#Override
protected void onPrepareDialogBuilder(Builder builder) {
// Load data
if (this.getEntries()==null) {
FillerTask filler = new FillerTask();
filler.execute();
}
Log.d(TAG, "Filler finished");
super.onPrepareDialogBuilder(builder);
}
Here is Filltertask code, basically he looks for every activity with a MAIN Intent filling a list:
private class FillerTask extends AsyncTask<Void, Void, String[][]> {
private ProgressDialog dialog;
#Override
protected void onPreExecute() {
Log.d(TAG, "Dismiss dialog");
dialog = ProgressDialog.show(MyListPreference.this.getContext(), "", "Doing stuff...", true);
}
#Override
protected String[][] doInBackground(Void... params) {
return fill();
}
public String[][] fill() {
Log.d(TAG, "Fill started");
CREATE LISTS...
// Done
Log.d(TAG, "Fill done");
String[][] result = new String[][] {entryNames, entryValues};
return result;
}
#Override
protected void onPostExecute(String[][] result) {
Log.d(TAG, "Post execute");
MyListPreference.this.setEntries(result[0]);
MyListPreference.this.setEntryValues(result[1]);
dialog.dismiss();
}
}
My problem is that filler.execute() does not block onPrepareDialogBuilder and functions goes till the end before values are filled causing an exception... Any idea?
That is the entire point behind an AsyncTask. The "Async" in AsyncTask means asynchronous.
Use your AsyncTask to get your data. Then, in onPostExecute(), display the dialog.
Found the solution, best way to do this is override the onClick method and let the AsyncTask postExecute call the "super()", so click is not passed until content is loaded and during load progress bar is correctly displayed.
asyntask doesn't lock main thread, it just drops a message to message queue of main thread
I have a toast in a slave thread which needs to tell a user wen a connection is established. To do this I know I need to use Async to make the toast happen, but I'm not sure where or how to implements the extended async. If I understand it, I think I can just create a MyAsync with the and just onProgressUpdate() the toast?
#Override
public void onProgressUpdate(String... args) {
Toast.makeText(context, args, Toast.LENGTH_SHORT).show();
}
Thanks for your time
~Aedon
Yep, you should be able to just extend the ASyncTask and change the template variables to what you need. The Toast class is a static class so it can be called from any thread without worrying about conflicts.
I don't see any issues with your code above except you wouldn't want to be calling new Toast messages very often since they stack. So if you were to continuous call the .show() function it would stack them and continue to show new Toast messages every LENGTH_SHORT interval until it caught up.
As for an example of an ASyncTask, here you go:
private class MyAsync extends AsyncTask<<What to pass in to doInBackground>, <What to pass in to onProgressUpdate>, <What type onPostExecute receives>> {
protected T (result type to onPostExecute) doInBackground(T... urls) {
//Do big calculations in here
}
protected void onProgressUpdate(T... progress) {
//Update
}
protected void onPostExecute(T result) {
//Done
}
}