Ideal way to cancel an executing AsyncTask

AndroidAndroid Asynctask

Android Problem Overview


I am running remote audio-file-fetching and audio file playback operations in a background thread using AsyncTask. A Cancellable progress bar is shown for the time the fetch operation runs.

I want to cancel/abort the AsyncTask run when the user cancels (decides against) the operation. What is the ideal way to handle such a case?

Android Solutions


Solution 1 - Android

Just discovered that AlertDialogs's boolean cancel(...); I've been using everywhere actually does nothing. Great.
So...

public class MyTask extends AsyncTask<Void, Void, Void> {

	private volatile boolean running = true;
	private final ProgressDialog progressDialog;

	public MyTask(Context ctx) {
		progressDialog = gimmeOne(ctx);

		progressDialog.setCancelable(true);
		progressDialog.setOnCancelListener(new OnCancelListener() {
			@Override
			public void onCancel(DialogInterface dialog) {
				// actually could set running = false; right here, but I'll
				// stick to contract.
				cancel(true);
			}
		});

	}

	@Override
	protected void onPreExecute() {
		progressDialog.show();
	}

	@Override
	protected void onCancelled() {
		running = false;
	}

	@Override
	protected Void doInBackground(Void... params) {

		while (running) {
			// does the hard work
		}
		return null;
	}
	
	// ...

}

Solution 2 - Android

If you're doing computations:

  • You have to check isCancelled() periodically.

If you're doing a HTTP request:

  • Save the instance of your HttpGet or HttpPost somewhere (eg. a public field).
  • After calling cancel, call request.abort(). This will cause IOException be thrown inside your doInBackground.

In my case, I had a connector class which I used in various AsyncTasks. To keep it simple, I added a new abortAllRequests method to that class and called this method directly after calling cancel.

Solution 3 - Android

The thing is that AsyncTask.cancel() call only calls the onCancel function in your task. This is where you want to handle the cancel request.

Here is a small task I use to trigger an update method

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;
		}
	 }

Solution 4 - Android

Simple: don't use an AsyncTask. AsyncTask is designed for short operations that end quickly (tens of seconds) and therefore do not need to be canceled. "Audio file playback" does not qualify. You don't even need a background thread for ordinary audio file playback.

Solution 5 - Android

The only way to do it is by checking the value of the isCancelled() method and stopping playback when it returns true.

Solution 6 - Android

This is how I write my AsyncTask
the key point is add Thread.sleep(1);

@Override	protected Integer doInBackground(String... params) {
		
		Log.d(TAG, PRE + "url:" + params[0]);
        Log.d(TAG, PRE + "file name:" + params[1]);
        downloadPath = params[1];
        
        int returnCode = SUCCESS;
		FileOutputStream fos = null;
        try {
        	URL url = new URL(params[0]);
            File file = new File(params[1]);
            fos = new FileOutputStream(file);
            
            long startTime = System.currentTimeMillis();
            URLConnection ucon = url.openConnection();
            InputStream is = ucon.getInputStream();
            BufferedInputStream bis = new BufferedInputStream(is);
            
            byte[] data = new byte[10240]; 
            int nFinishSize = 0;
            while( bis.read(data, 0, 10240) != -1){
            	fos.write(data, 0, 10240);
            	nFinishSize += 10240;
            	**Thread.sleep( 1 ); // this make cancel method work**
            	this.publishProgress(nFinishSize);
            }              
            data = null;    
            Log.d(TAG, "download ready in"
                  + ((System.currentTimeMillis() - startTime) / 1000)
                  + " sec");
                
        } catch (IOException e) {
                Log.d(TAG, PRE + "Error: " + e);
                returnCode = FAIL;
        } catch (Exception e){
        		 e.printStackTrace();       	
        } finally{
			try {
				if(fos != null)
					fos.close();
			} catch (IOException e) {
				Log.d(TAG, PRE + "Error: " + e);
				e.printStackTrace();
			}
        }
       
		return returnCode;
	}

Solution 7 - Android

Our global AsyncTask class variable

LongOperation LongOperationOdeme = new LongOperation();

And KEYCODE_BACK action which interrupt AsyncTask

   @Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_BACK) {
			LongOperationOdeme.cancel(true);
		}
		return super.onKeyDown(keyCode, event);
	}

It works for me.

Solution 8 - Android

I don't like to force interrupt my async tasks with cancel(true) unnecessarily because they may have resources to be freed, such as closing sockets or file streams, writing data to the local database etc. On the other hand, I have faced situations in which the async task refuses to finish itself part of the time, for example sometimes when the main activity is being closed and I request the async task to finish from inside the activity's onPause() method. So it's not a matter of simply calling running = false. I have to go for a mixed solution: both call running = false, then giving the async task a few milliseconds to finish, and then call either cancel(false) or cancel(true).

if (backgroundTask != null) {
    backgroundTask.requestTermination();
    try {
        Thread.sleep((int)(0.5 * 1000));
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    if (backgroundTask.getStatus() != AsyncTask.Status.FINISHED) {
        backgroundTask.cancel(false);
    }
    backgroundTask = null;
}

As a side result, after doInBackground() finishes, sometimes the onCancelled() method is called, and sometimes onPostExecute(). But at least the async task termination is guaranteed.

Solution 9 - Android

With reference to Yanchenko's answer on 29 April '10: Using a 'while(running)' approach is neat when your code under 'doInBackground' has to be executed multiple times during every execution of the AsyncTask. If your code under 'doInBackground' has to be executed only once per execution of the AsyncTask, wrapping all your code under 'doInBackground' in a 'while(running)' loop will not stop the background code (background thread) from running when the AsyncTask itself is cancelled, because the 'while(running)' condition will only be evaluated once all the code inside the while loop has been executed at least once. You should thus either (a.) break up your code under 'doInBackground' into multiple 'while(running)' blocks or (b.) perform numerous 'isCancelled' checks throughout your 'doInBackground' code, as explained under "Cancelling a task" at https://developer.android.com/reference/android/os/AsyncTask.html.

For option (a.) one can thus modify Yanchenko's answer as follows:

public class MyTask extends AsyncTask<Void, Void, Void> {

private volatile boolean running = true;

//...

@Override
protected void onCancelled() {
    running = false;
}

@Override
protected Void doInBackground(Void... params) {

    // does the hard work

    while (running) {
        // part 1 of the hard work
    }

    while (running) {
        // part 2 of the hard work
    }

    // ...

    while (running) {
        // part x of the hard work
    }
    return null;
}

// ...

For option (b.) your code in 'doInBackground' will look something like this:

public class MyTask extends AsyncTask<Void, Void, Void> {

//...

@Override
protected Void doInBackground(Void... params) {

    // part 1 of the hard work
    // ...
    if (isCancelled()) {return null;}

    // part 2 of the hard work
    // ...
    if (isCancelled()) {return null;}

    // ...

    // part x of the hard work
    // ...
    if (isCancelled()) {return null;}
}

// ...

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSamuhView Question on Stackoverflow
Solution 1 - AndroidyanchenkoView Answer on Stackoverflow
Solution 2 - AndroidwrygielView Answer on Stackoverflow
Solution 3 - AndroidDonCrocoView Answer on Stackoverflow
Solution 4 - AndroidCommonsWareView Answer on Stackoverflow
Solution 5 - AndroiddbyrneView Answer on Stackoverflow
Solution 6 - AndroidAndrew ChenView Answer on Stackoverflow
Solution 7 - AndroidGöksel GürenView Answer on Stackoverflow
Solution 8 - AndroidPiovezanView Answer on Stackoverflow
Solution 9 - AndroidAlex Ivan HowardView Answer on Stackoverflow