Can an AsyncTask be cancelled while completing onPostExecute()?
Or is it that once doInBackground() is completed the task can no longer be cancelled?
From what I understand the task is only running in the doInBackground() state, onPostExecute() means task has completed and can't be cancelled.
cancel() attempts to cancel execution of this task. This attempt will fail if the task has already completed, already been cancelled, or could not be cancelled for some other reason. If successful, and this task has not started when cancel is called, this task should never run. If the task has already started, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task.
Calling this method will result in onCancelled(Object) being invoked on the UI thread after doInBackground(Object[]) returns. Calling this method guarantees that onPostExecute(Object) is never invoked. After invoking this method, you should check the value returned by isCancelled() periodically from doInBackground(Object[]) to finish the task as early as possible.
Override onCanceled() method, please see the sample code below:
private class UpdateTask extends AsyncTask<Void, Void, Void> {
private boolean running = true;
#Override
protected void onCancelled() {
running = false;
}
#Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
onUpdate();
}
#Override
protected Void doInBackground(Void... params) {
while(running) {
publishProgress();
}
return null;
}
}
Related
My app uses an AsyncTask to download files while displaying a ProgressDialog (I'm aware that it's deprecated) with a "Cancel" button.
According to this you should check isCancelled() in doInBackground periodically because mytask.cancel(true) won't interrupt doInBackground on its own.
I simply cancelled the task without checking at first and noticed that it still stops doInBackground: Depending on how long I let it download before pressing the "Cancel" button, I've seen different sizes in the resulting file - from just a few kb to a couple of mb - the final size would have been around 9mb.
How is this possible? Do you actually not have to call isCancelled() anymore?
My AsyncTask:
private class DownloadTask extends AsyncTask<String, String, String> {
protected void onPreExecute() {
progressdialog.setMessage("Preparing Download...");
progressdialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressdialog.setProgressNumberFormat(null);
progressdialog.setProgressPercentFormat(null);
progressdialog.setIndeterminate(true);
progressdialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
progressdialog.dismiss();
mytask.cancel(true);
}
});
progressdialog.show();
}
protected String doInBackground(String... bla) {
String error = download();
return error;
}
protected void onProgressUpdate(String... s) {
//....
}
protected void onPostExecute(String s) {
progressdialog.dismiss();
//....
}
According to this you should check isCancelled() in doInBackground
periodically because mytask.cancel(true) won't interrupt
doInBackground on its own.
Actually it is not true.
According to documentation:
After invoking this method, you should check the value returned by
isCancelled() periodically from doInBackground(Object[]) to finish the
task as early as possible.
It means you can additionally check for isCancelled() to stop AsyncTask earlier if it is started.
mytask.cancel(true) will stop execution anyway.
Let`s see under the hood what is going on
When you call mytask.cancel(true):
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);
return mFuture.cancel(mayInterruptIfRunning);
}
Where mFuture is FutureTask that holds runnable inside
Then mFuture.cancel is called:
public boolean cancel(boolean mayInterruptIfRunning) {
if (state != NEW)
return false;
if (mayInterruptIfRunning) {
if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, INTERRUPTING))
return false;
Thread t = runner;
if (t != null)
t.interrupt();
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED); // final state
}
else if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, CANCELLED))
return false;
finishCompletion();
return true;
}
Where runner is just
private volatile Thread runner;
Since its just thread, lets see what interrupt does in your case:
If this thread is blocked in an I/O operation upon an interruptible
channel then the channel will be closed, the thread's interrupt status
will be set, and the thread will receive a ClosedByInterruptException.
So if your download() method uses InterruptibleChannel interrupt will work.
In other words looks like you have never had to call isCancelled() to interrupt AsyncTask =) since Thread.interrupt can stop io blocking operation in your case.
I have created an Android Application, in that I want to cancel AsyncTask onPause state of Fragment.
I tried using AsyncTask.cancel(true); but it gives null pointer exception.
#Override
public void onPause()
{
super.onPause();
AsyncTask.cancel(true);
}
Thanks.
task.cancel() will do it. Be sure to include frequent checks to isCancelled() in your onBackground() as well as in onPostExecute() to avoid accessing/updating UI which is no longer there.
public void onActivityPause() {
task.cancel();
}
asyncTask.cancel(true); will change a boolean value only and don't stop your thread.
so you need to "ask" if the async task not cancelled.
example:
#Override
protected Void doInBackground(Void... params) {
while(!isCancelled()){
// do something
}
return null;
}
I created an async task to call my server to get data from DB.
I need to process the result returned from http server call.
From my activity i calling the async task in many places. so i cant use member variable to access the result. is there any way to do?
public Result CallServer(String params)
{
try
{
new MainAynscTask().execute(params);
}
catch(Exception ex)
{
ex.printStackTrace();
}
return aResultM;//Need to get back the result
}
private class MainAynscTask extends AsyncTask<String, Void, Result> {
#Override
protected Result doInBackground(String... ParamsP) {
//calling server codes
return aResultL;
}
#Override
protected void onPostExecute(Result result) {
super.onPostExecute(result);
//how i will pass this result where i called this task?
}
Try to call the get() method of AsyncTask after you call the execute() method. This works for me
http://developer.android.com/reference/android/os/AsyncTask.html#get()
public Result CallServer(String params)
{
try
{
MainAynscTask task = new MainAynscTask();
task.execute(params);
Result aResultM = task.get(); //Add this
}
catch(Exception ex)
{
ex.printStackTrace();
}
return aResultM;//Need to get back the result
}
...
...
There are two ways i can suggest -
onPostExecute(Result) in AsyncTask. See http://developer.android.com/reference/android/os/AsyncTask.html#onPostExecute(Result)
Send a broadcast with the result as an extra.
AsyncTask is an asynchronous task so it does NOT make sense to return the result to the caller. Rather handle the result in onPostExecute() like setting the value to TextView etc. Or send a broadcast so that some other listener can handle the result.
Here's how I got around this:
1) Create an interface class that defines a signature for a method to execute on completion:
public interface AsyncIfc {
public void onComplete();
}
2) Set a property on your AsyncTask class to hold the delegate method:
public AsyncIfc completionCode;
3) Trigger the delegate from onPostExecute() in the AsyncTask:
completionCode.onComplete();
4) From your calling logic, set the delegate property to an anonymous method:
task.completionCode = new AsyncIfc() {
#Override
public void onComplete() {
// Any logic you want to happen after execution
}
};
When an asynchronous task is executed, the task goes through 4 steps:
onPreExecute(), invoked on the UI thread before the task is executed. This step is normally used to setup the task, for instance by showing a progress bar in the user interface.
doInBackground(Params...), invoked on the background thread immediately after onPreExecute() finishes executing. This step is used to perform background computation that can take a long time. The parameters of the asynchronous task are passed to this step. The result of the computation must be returned by this step and will be passed back to the last step. This step can also use publishProgress(Progress...) to publish one or more units of progress. These values are published on the UI thread, in the onProgressUpdate(Progress...) step.
onProgressUpdate(Progress...), invoked on the UI thread after a call to publishProgress(Progress...). The timing of the execution is undefined. This method is used to display any form of progress in the user interface while the background computation is still executing. For instance, it can be used to animate a progress bar or show logs in a text field.
onPostExecute(Result), invoked on the UI thread after the background computation finishes. The result of the background computation is passed to this step as a parameter.
Use a handler
In your activity
Handler mHandler = new Handler() {
#Override public void handleMessage(Message msg) {
String s=(String)msg.obj;
tv.setText(s);
}
};
//result is soap object in this case.
protected void onPostExecute(SoapObject result) {
pd.dismiss();
if(result != null) {
Message msg=new Message();
msg.obj=result.getProperty(0).toString();
mHandler.sendMessage(msg);
}
I tried AsyncTask, Thread and Handler but i don't get it.
The method readXML() takes about 1-2 minutes and i only need a way to cancel this operation. All the solutions I've found were for short time operations (set flag, check flag and break).
Edit
protected class InitTask extends AsyncTask<Context, Integer, String> {
#Override
protected String doInBackground( Context... params ){
try{
preparing = true;
readXML();
preparing = false;
} catch( Exception e ){
Log.i("test", e.getMessage() );
}
return "COMPLETE!";
}
#Override
protected void onCancelled(){
super.onCancelled();
}
}
// ....
_initTask = new InitTask();
_initTask.execute(this);
// ....
_initTask.cancel(true);
Your problem is that onCancelled is only invoked after doInBackground returns. So you need to check for isCancelled from within your readXML operation. See extract from docs (from http://developer.android.com/reference/android/os/AsyncTask.html) below...
A task can be cancelled at any time by invoking cancel(boolean). Invoking this method will cause
subsequent calls to isCancelled() to
return true. After invoking this
method, onCancelled(Object), instead
of onPostExecute(Object) will be
invoked after doInBackground(Object[])
returns. To ensure that a task is
cancelled as quickly as possible, you
should always check the return value
of isCancelled() periodically from
doInBackground(Object[]), if possible
(inside a loop for instance.)
This question already has answers here:
Android SDK AsyncTask doInBackground not running (subclass)
(9 answers)
Closed 9 years ago.
I'm having a problem with the AsyncTask class. It seems like my task stops working after creating 4 or 5 tasks.
Im having 2 activities. MainActivity which only holds a button that starts a second activity called ImageActivity.
ImageActivity is very simple. it got an onCreate that sets the layout, and then it starts a new AsyncTask that loads an image from the internet. This works fine the first few times. But than it suddenly stops working. The onPreExecute method is run every time, but not the doInBackground method. I have tried to simplify the doInBackground with a sleeping loop, and the same thing happens. I cant understand this behavour since the asynctask is both canceled and set to null in the onDestroy method. So every time i start a new ImageActivity, i also create a fresh AsyncTask.
I recreate the ImageActivity and the task by hitting the back button, and than clicking the button on the MainActivity.
Any ideas anyone? I'm really struggling with this one.
UPDATE: Code that starts the ImageActivity (inside a button onClickListener)
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
intent.setClassName(this, ImageActivity.class.getName());
startActivity(intent);
The code above starts this activity
public class ImageActivity extends Activity {
private AsyncTask<Void, Void, Void> task;
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.main);
task = new AsyncTask<Void, Void, Void>() {
#Override
protected void onPreExecute()
{
Log.d(TAG, "onPreExecute()");
}
#Override
protected Void doInBackground(Void... params)
{
Log.d(TAG, "doInBackground() -- Here is the download");
// downloadBitmap("http://mydomain.com/image.jpg")
return null;
}
#Override
protected void onPostExecute(Void res)
{
Log.d(TAG, "onPostExecute()");
if(isCancelled()){
return;
}
}
}.execute();
}
#Override
protected void onDestroy()
{
super.onDestroy();
task.cancel(true);
}
}
UPDATE:
I have tested using a combination of traditional Threads and runOnUiThread method, and it seems to work better. Now the thread runs every time.
Removing the AsyncTask and using a traditional Thread instead of combining it with runOnUiThread seems to work. But I still have not found the reason why the AsyncTask is so "unstable".
Here is the code that works for me:
public class ImageActivity extends Activity {
private Thread worker;
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.main);
worker = new Thread(new Runnable(){
private void updateUI(final List<Object> list)
{
if(worker.isInterrupted()){
return;
}
runOnUiThread(new Runnable(){
#Override
public void run()
{
// Update view and remove loading spinner etc...
}
});
}
private List<Object> download()
{
// Simulate download
SystemClock.sleep(1000);
return new ArrayList<Object>();
}
#Override
public void run()
{
Log.d(TAG, "Thread run()");
updateUI(download());
}
});
worker.start(); }
#Override
protected void onDestroy()
{
super.onDestroy();
worker.interrupt();
}
}
I ran into similar problem. You can't have multiple Asynctasks running in parallel up until SDK 11. Check here for more info
I just ran into this problem as well. If you use AsyncTask.execute, your task is run on a serial queue (from the Android 4.3 source):
When first introduced, AsyncTasks were executed serially on a single
background thread. Starting with DONUT, this was changed to a pool of
threads allowing multiple tasks to operate in parallel. Starting with
HONEYCOMB, tasks are executed on a single thread to avoid common
application errors caused by parallel execution.
This is consistent with behavior that I saw. I had an AsyncTask popped up a dialog in doInBackground and blocked until the dialog was closed. The dialog needed its own AsyncTask to complete. The dialog's AsyncTask.doInBackground method never executed because the original AsyncTask was still blocked.
The solution is to execute the second AsyncTask in a separate Executor.
Use traceview to investigate -- or obtain a thread dump. My guess is that one of your AsyncTask threads are hanging on downloading.
AsyncTask has a small thread pool, so if one of your tasks hangs, it could end up blocking your thread pool.
Here's a quick test you can run -- on 4.3, I see that I have only 5 concurrent threads I can run. When one thread exits, other threads start up.
private void testAsyncTasks() {
for (int i = 1; i <= 10; i++) {
final int tid = i;
new AsyncTask<Integer, Void, Void>() {
protected void onPreExecute() {
Log.d("ASYNCTASK", "Pre execute for task : " + tid);
};
#Override
protected Void doInBackground(Integer... args) {
int taskid = args[0];
long started = SystemClock.elapsedRealtime();
Log.d("ASYNCTASK", "Executing task: " + taskid + " at " + started);
for (int j = 1; j <= 20; j++) {
Log.d("ASYNCTASK", " task " + taskid + ", time=" + (SystemClock.elapsedRealtime() - started));
SystemClock.sleep(1000);
}
return null;
}
protected void onPostExecute(Void result) {
Log.d("ASYNCTASK", "Post execute for task : " + tid);
};
}.execute(i);
}
}
You shouldn't have to worry about housekeeping thread in Android as it is managed by the system.
Please also post the image download method. Have you also tried to not cancel the thread in the onDestroy() method? How are you returning the the image to your UI thread?
The problem I believe is with the heavy image download task. Even if you cancel the async task the image download will continue to execute and the async task does not finish until the download is complete. You might want to check the isCancelled() method on AyncTask while the download is going on and kill the download if the task is cancelled.
For reference, heres the documentation on cancel() method :
Attempts to cancel execution of this task. This attempt will fail if the task has already completed, already been cancelled, or could not be cancelled for some other reason. If successful, and this task has not started when cancel is called, this task should never run. If the task has already started, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task.
Calling this method will result in onCancelled(Object) being invoked on the UI thread after doInBackground(Object[]) returns. Calling this method guarantees that onPostExecute(Object) is never invoked. After invoking this method, you should check the value returned by isCancelled() periodically from doInBackground(Object[]) to finish the task as early as possible.
I had this too, no real reason for not starting. I've noticed that after restarting adb it worked again. Not sure why this is, but it did work for me