I have following piece of code:
public class SomeActivity extends Activity {
Context context;
List<MenuItem> menuItems;
public void importList(View v) {
menuItems = new ArrayList<MenuItem>();
ProgressDialog dialog = ProgressDialog.show(this.context, "TITLE", "MSG");
MyAsyncTask task = new MyAsyncTask(context); // Context is here because I tried to create ProgressDialog inside pre/postExecute, but it doesn't work either
task.execute();
try {
// menuItems = task.get();
} catch(Exception e) {
// : (
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
// ...
this.context = this;
}
}
When I comment the line, where i get values from AsyncTask ("menuItems = task.get()") everythings work ok. But when I uncomment it, ProgressDialog appears AFTER the task is finished, and value returned. Why is that?
I think that it has sth to do with these contexts (that's why I included onCreate method) but I don't have any idea how to fix it. Obviously, I want ProgressDialog to display BEFORE task is finished, not after.
Not sure if relevant - MyAsyncTask is doing http request and some json parsing.
I think that it has sth to do with these contexts
Not at all. Also, when sending Context from an Activity you don't need to create a variable. Simply use this or ActivityName.this.
MyAsyncTask task = new MyAsyncTask(this);
But when I uncomment it, ProgressDialog appears AFTER the task is finished, and value returned. Why is that?
Calling get() blocks the UI, this is why you don't see the progress until it is done. You don't need to call this. The point of onProgressUpdate() is so the background thread (doInBackground()) can call it to update the UI since doInBackground() doesn't run on the UI.
Remove that line from your code and never look at it again. If there is a reason that you think you need it then please explain and we can help you find a better way.
Edit
See this SO answer on updating when the task is finished
Related
I am using AsyncTask so that the function I want executes immediately and does not wait till the end to be executed..
but for some reason I don't why it executes in at the end of all process!
I looked at other solutions and found that Thread should be executed at the end but for AsyncTask it should be executed whenever it is called..
here is my code
private void LogMeIn()
{
string CheckValue;
// Here I call the AsyncTask
new GCM().execute(null,null,null);
//gcmRegID is a public variable and should has GCM value assigned to it by now, but I it is empty as GCM() has not been executed yet
//This is always return empty string
CheckValue = gcmRegID;
}
This is the AsyncTask that wait till the end to be executed
//This is the AsyncTask
private class GCM extends AsyncTask<String, String, String> {
private String resp;
private Context context;
#Override
protected String doInBackground(String... params) {
GCMHelper gcmRegistrationHelper = new GCMHelper (
getApplicationContext());
try {
gcmRegID = gcmRegistrationHelper.GCMRegister("123456789");
} catch (Exception e) {
e.printStackTrace();
}
return gcmRegID;
}
}
I tried to put the call for GCMRegister in onPreExecute but i get an error that it has to be in the main thread
it is like i am going in circles....
the call has to be in the main thread and the main thread will be executed at the end of the function...
it is like no way to get the GCM code in the middle!!!!
How can I make this AsyncTask executes when it called??
Thanks
Without seeing more of your code it's hard for me to tell but I would take a look at where you are calling LogMeIn(). Because your AsyncTask and call to execute are nested in the LogMeIn() function, it won't be called until LogMeIn() is first called.
AsyncTask goes through the following 4 steps in order after calling execute():
onPreExecute()
doInBackground(Params...)
onProgressUpdate(Progress...)
onPostExecute(Result)
These can be added to your GCM class and used however you like. Note that you don't call them directly. AsyncTask does it automatically after calling .execute().
Only the tasks specified in doInBackground(Params...) are executed on a background thread. The rest are all done on the UI(or main) thread. I would suggest putting either a toast or a log inside onPreExecute() and in onPostExecute() to debug where/when GCM().execute is actually being called and then to tell you when the background task is complete. This will give you a better idea of what is going on.
Make sure you aren't trying to update the UI in doInBackground().
Would love to help more but we would need to see more of your code.
#Override
public void onPreExecute() {
super.onPreExecute();
Log.d("GCM", "onPreExecute: called");
}
#Override
public void onPostExecute(String resultOfDoInBackground) {
super.onPostExecute(resultOfDoInBackground);
Log.d("GCM", "onPostExecute: called");
}
AsyncTask keeps a queue of tasks and a thread pool,the thread pool execute the tasks one by one,so if you have too more tasks ,you will find it not execute your tasks immediately.
And in one process ,all your AsyncTask share one thread pool.In this case,you should make one task queue by yourself,you can just use HandleThread to execute a timely task.
Try placing your new GCM().execute(null,null,null); in the protected void onCreate(Bundle savedInstanceState) method. this way it will be called once the app is ran. This way you will have your GCM id before you get to the LogMEIn method.
Try this -
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ) {
new GCM().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,params);
} else {
new GCM().execute();
}
I want to know whether a practice I am using is correct or not? Please have a look!
I am using an AsyncTask to call a Handler to animate (increment gradually over time) the progress in a ProgressBar.
I could only use Handler to animate it but I needed to animate multiple ProgressBars using the same Handler. I did not find it possible to pass a paramerer "ProgressBar" to a Handler, nor did declaring a global variable (maybe array?) looked clean.
So I pass the ProgressBar into an AsyncTask as a parameter, and then within the AsyncTask, I have defined the Handler which manipulates the passed ProgressBar using the method of AsyncTask called publishProgress(int). So Each call to AsyncTask is effectively equal to serving the Handler a unique passed ProgressBar.
I am including a stripped down code which shows the structure:
public class animateProgress extends AsyncTask<ArrayList<ArrayList>, Integer, Boolean> {
ProgressBar pb;
int incrementProgress;
#Override
protected Boolean doInBackground(ArrayList<ArrayList>... arrayLists) {
ArrayList<ArrayList> gotArrayList = arrayLists[0]; //get passed arraylist
ArrayList<ProgressBar> alP = gotArrayList.get(1);
pb = alP.get(0); // REFERENCE TO PROGRESSBAR IS ACQUIRED HERE.
scoreCounter.postDelayed(scoreCounterUpdate, 0);
return null;
}
private Handler scoreCounter = new Handler();
private Runnable scoreCounterUpdate = new Runnable() {
public void run() {
if (MY_LOGIC_THAT_PROGRESS_SHOULD_INCREMENT) {
publishProgress(incrementProgress);
}
if (MY_LOGIC_THAT_WE_SHOULD_EXIT) {
scoreCounter.removeCallbacks(scoreCounterUpdate);
} else {
scoreCounter.postDelayed(this, 50);
}
}
};
#Override
protected void onProgressUpdate(Integer... values) {
pb.incrementProgressBy(values[0]);
super.onProgressUpdate(values);
}
}
The code works perfectly but my concern is that I think I should either not use AsyncTask or not use Handler.
Also, I think that doInBackground() should theoretically finish as soon as I call scoreCounter.postDelayed(), and so should the whole AsyncTask. But in reality, the publishProgress() called by handler many seconds later still works. This is confusing.
So as the title probably suggests - I've done a lot of research on the topic, but I am still confused and unable of achieving what I want.
In very simplified scenario, I have a LoginActivity in which is method boolean validateUserInput(String mail, String password) and I want to do the check input in the separate thread. I suppose I will extend it in the future to do the log-in itself as well (http request). Naturally I would like to get boolean value if the operation was successful or not - and in the process of operation I want to show progressbar dialog.
Make a thread, run the code, return its result, show the progress bar in a meantime, piece of cake right?
Should I use asynctask or runnable? How do I do this so I do not block the UI thread?
This is code I tried to use in LoginActivity:
new Thread(new Runnable() {
#Override
public void run() {
mUserInputValidated = validateUserInput(inputEmail.getText().toString(), inputPassword.getText().toString());
}
}).start();
if(mUserInputValidated)
{
attemptUserLogin(inputEmail.getText().toString(), inputPassword.getText().toString());
}
I also tried asynctask approach, but ended up with various errors since I started progress dialog in onPreExecute() and ended it in onPostExecute(), using reference like LoginActivity.this where was the problem with memory leak which I was also unable to fix?
I assume this is pretty usual scenarios, since almost every app use it, so - what are common approaches? How do I fix my code?
You have to use asynctask this will take the work off from main-thread and place it on background thread once the work is done
This is a sample that shows how to do it
private class LongOperation extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.interrupted();
}
}
return "Executed";
}
#Override
protected void onPostExecute(String result) {
TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed"); // txt.setText(result);
// might want to change "executed" for the returned string passed
// into onPostExecute() but that is upto you
}
#Override
protected void onPreExecute() {}
#Override
protected void onProgressUpdate(Void... values) {}
}
Reference
This is the flow of my app:
1) The user takes a picture or video
2) The media is saved to internal storage
3) The path is assigned to a custom object
4) The UI is updated to indicate that the user can continue
The UI updates only if the custom object has either an imagepath or a videopath. I've just started using AsyncTask to save to internal storage in a background thread so the app will not hang while saving large files, but I'm having some problems.
What I want to do: Display a ProgressDialog until doInBackground() finishes, then assign the path to my Object, and then continue on the main thread to update the UI.
Right now, the main thread will continue while AsyncTask is still working, and the UI will not update correctly since the path has not yet been assigned to the Object.
I've read about AsyncTask#get(), but I'm not sure how to implement it with the ProgressDialog. I tried, and the main thread didn't seem to wait for the results before continuing.
I'd really appreciate any help. Thank you!
My AsyncTask:
private class SaveMediaTask extends AsyncTask<Integer, Void, String>
{
private ProgressDialog progressDialog;
private int mediaType;
public SaveMediaTask()
{
progressDialog = new ProgressDialog(getActivity(), ProgressDialog.THEME_HOLO_LIGHT);
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
this.progressDialog.setTitle("Processing");
}
#Override
protected void onPreExecute()
{
super.onPreExecute();
this.progressDialog.show();
}
protected String doInBackground(Integer... mediaType)
{
//save to internal storage and return the path
return path;
}
protected void onPostExecute(String path)
{
//by the time this runs, the UI has already tried to update itself on the main thread,
//and found that myObject does not yet have a path. Once this runs, it is too late.
myObject.setPath(path);
if (progressDialog.isShowing())
{
progressDialog.dismiss();
}
}
}
How I call it, immediately after the user leaves the camera:
new SaveMediaTask().execute(MEDIA_TYPE_IMAGE);
//WAIT HERE AND DISPLAY PROGRESSDIALOG UNTIL TASK IS DONE
//update UI
Your onPostExecute should notify your activity, that it can continue. So basically:
// Start the task from your activity
new SaveMediaTask().execute(MEDIA_TYPE_IMAGE);
// Method that will be called when task is completed
public void taskComplete() {
// update UI
}
...
protected void onPostExecute(String path) {
myObject.setPath(path);
if (progressDialog.isShowing()) {
progressDialog.dismiss();
}
((YourActivity)getActivity()).taskComplete();
}
the main thread didn't seem to wait for the results before
continuing.
the main thread wouldn't wait. This isn't how AsyncTask work. AsyncTask runs in background along with your main thread.
then continue on the main thread to update the UI.
You don't need to continue on the main thread to update the UI once AsyncTask task is done, you could simply execute that post doInBackground task in onPostExecute() after dismissing the progressDialog, i.e progressDialog.dismiss(); because onPostExecute runs in the UI thread.
Also, the good way is to start progressDialog inside onPreExecute() method and dismiss it in onPostExecute without checking if the progessDialog is still showing or not because onPostExecute() would only run if the doInBackground() method is finished doing its job.
What I want to do: Display a ProgressDialog until doInBackground()
finishes, then assign the path to my Object, and then continue on the
main thread to update the UI.
Start ProgressDialog in onPreExecute,
Assign the path to your object in doInBackground
Dismiss ProgressDialog in onPostExecute,
Continue your main thread work in onPostExecute since it runs in UI thread.
You can also update your UI thread while doInBackground is still running by invoking publishProgress(). Each call to this method will trigger the execution of onProgressUpdate() on the UI thread.
Tip: Its a good idea to dismiss progressDialog inside onCancelled() method beside dismissing it in onPostExecute() in case if you ever cancel your task inside doInBackground
You can move your code marked with //update UI to the end of your onPostExecute method. onPostExecute is always called on UI thread and is a good place to update UI to reflect the AsyncTask work results.
I am developing an Android application and I need show remind dialog from time to time in according to variable from shared preferences. If user does not want to see reminder message, he can disable this option also in my PreferenceActivity. I am using AsyncTask class to show remind message to user from time to time . User can press options button at every moment to go to PreferenceActivity to enable/disable reminder option and set pause time value between reminds, and the go back to activity. So I am checking all values in onResume method. And if users does not want to see remind messages I need to finish current working AsyncTask, or if user changed value between reminds, I need to restart current AsyncTack with new pause value. But I see an unpredictable behavior of my AsyncTack: sometimes it stops, sometimes not and continue working and showing message)))), sometimes it works and sometimes - not))))). Here is a piece of code:
This is my AsyncTack class
private class ReadReminderTask extends AsyncTask<Long, Void, Void> {
private long mRemindTime;
private volatile boolean running = true;
public ReadReminderTask(){
}
#Override
public Void doInBackground(Long... params){
mRemindTime = params[0];
while (running){
try{
Thread.sleep(mRemindTime);
publishProgress();
}
catch (Exception e){
e.printStackTrace();
}
}
return null;
}
#Override
protected void onProgressUpdate(Void... progress) {
super.onProgressUpdate(progress);
// showReminder is a method where I show remind message
showReminder();
}
#Override
protected void onCancelled() {
running = false;
}
}
This is onResume method of my Activity where I need to show reminder message:
if(!settings.getBoolean("needToRemind", false)) {
mReadReminderTask.cancel(true);
} else if(settings.getBoolean("needToRemind", false)) {
mReadReminderTask = new ReadReminderTask();
mReadReminderTask.execute(settings.getLong("timeRemind", 1));
}
Can anyone help me with my problem?
That's really not such a great way to do that. The "unpredictable behavior" happens because you leave the task running even after your Activity is stopped. The call to cancel in your onResume is not only too late, it actually is not doing anything at all.
Have a look at Handler.postDelayed. You want something like this:
In your onCreate
hdlr = new Handler();
reminder = new Runnable() { public void run() { showReminder(); } }
EDITED TO ADD ESSENTIAL 2ND PARAM TO postDelayed
... in your onResume:
hdlr.postDelayed(reminder, mRemindTime);
... and, in your onPause:
hdlr.removeCallbacks(reminder);
Please note that AsyncTask.cancel() does not really cancel running task. Instead it only sets a flag to notify that cancel has been requested. It is up to AsyncTask implementation if and when to take this flag into account.
Your current code does not check for cancellation after Thread.sleep returns. Correct implementation would be something like:
public Void doInBackground(Long... params){
mRemindTime = params[0];
while (!isCancelled()){
try{
Thread.sleep(mRemindTime);
if(!isCancelled())
publishProgress();
}
catch (Exception e){
e.printStackTrace();
}
}
return null;
}
Also attribute 'running' and onCancelled() method in your asynctask are not needed. You can use method 'isCancelled()' instead.
It's generally a bad solution. AsyncTasks are not great in handling config changes.
Depending on how long the delays are, you can use AlarmManager.setInexactRepeating in order to show these dialogs. You can create new PendingIntent using PendingIntent.getService or PendingIntent.getBroadcast depending on how you'd like to implement showing dialog – (from Service you will be able to show only system-like dialogs).
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intentStart = new Intent();
intentStart.setAction(SHOW_DIALOG_ACTION);
PendingIntent pendingIntentStart = PendingIntent.getBroadcast(context, 0, intentStart, 0);
And register a BroadcastReceiver which will handle showing the dialog.
If the delays are not long, you can create a custom ThreadPoolExecutor which will do what you want.
I don't think AsyncTask should be used for that. Thread.sleep(mRemindTime) is also very ugly! If you need a timer use CountDownTimer.