Lesson 89. AsyncTask. Cancel - cancel a task in progress

Lesson 89. AsyncTask. Cancel – cancel a task in progress


In this lesson:

– cancel the task in progress

Sometimes there is a need to cancel an already completed task. There is a cancel method in AsyncTask. It accepts a boolean parameter that indicates whether the system can interrupt the flow.

But in general, it is advisable not to wait for the system to complete the flow, but to act on its own. In doInBackground, we must periodically call the isCancelled method. As soon as we execute the cancel method for AsyncTask, isCancelled will return true. This means that we have to complete the doInBackground method.

That is a method cancel – it’s us we put a labelThat the task must be canceled. method isCancelled – we ourselves are this label read and take action to complete the task.

method cancel returns a boolean. We will get falseIf the task already completed or canceled.

Consider the example.

Let’s create a project:

Project name: P0891_AsyncTaskCancel
Build Target: Android 2.3.3
Application name: AsyncTaskCancel
Package name: ru.startandroid.develop.p0891asynctaskcancel
Create Activity: MainActivity

strings.xml:



	AsyncTaskCancel
	Start
	Cancel

main.xml:



	
	
	
	

Buttons start and cancel tasks, and TextView to output text.

MainActivity.java:

package ru.startandroid.develop.p0891asynctaskcancel;

import java.util.concurrent.TimeUnit;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends Activity {

  final String LOG_TAG = "myLogs";

  MyTask mt;
  TextView tvInfo;

  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    tvInfo = (TextView) findViewById(R.id.tvInfo);
  }

  public void onclick(View v) {
    switch (v.getId()) {
    case R.id.btnStart:
      mt = new MyTask();
      mt.execute();
      break;
    case R.id.btnCancel:
      cancelTask();
      break;
    default:
      break;
    }
  }

  private void cancelTask() {
    if (mt == null) return;
    Log.d(LOG_TAG, "cancel result: " + mt.cancel(false));
  }

  class MyTask extends AsyncTask {
    @Override
    protected void onPreExecute() {
      super.onPreExecute();
      tvInfo.setText("Begin");
      Log.d(LOG_TAG, "Begin");
    }

    @Override
    protected Void doInBackground(Void... params) {
      try {
        for (int i = 0; i < 5; i++) {
          TimeUnit.SECONDS.sleep(1);
          Log.d(LOG_TAG, "isCancelled: " + isCancelled());
        }
      } catch (InterruptedException e) {
        Log.d(LOG_TAG, "Interrupted");
        e.printStackTrace();
      }
      return null;
    }

    @Override
    protected void onPostExecute(Void result) {
      super.onPostExecute(result);
      tvInfo.setText("End");
      Log.d(LOG_TAG, "End");
    }
    
    @Override
    protected void onCancelled() {
      super.onCancelled();
      tvInfo.setText("Cancel");
      Log.d(LOG_TAG, "Cancel");

    }
  }
}

When the button is pressed Cancel the cancelTask ​​method is executed, in which we execute cancel (with false) for AsyncTask.

IN doInBackground in the loop we pause and log the result of the isCancelled method.

The onCancelled method is called by the system instead of onPostExecute if the job was canceled.

We will save everything and launch the application.

press Start, And after a couple of seconds, we press Cancel.

We look at the logs:

08: 17: 51.956: D / myLogs (487): Begin
08: 17: 52.993: D / myLogs (487): isCancelled: false
08: 17: 53.998: D / myLogs (487): isCancelled: false
08: 17: 54.543: D / myLogs (487): cancel result: true
08: 17: 54.552: D / myLogs (487): Cancel
08: 17: 55.042: D / myLogs (487): isCancelled: true
08: 17: 56.061: D / myLogs (487): isCancelled: true
08: 17: 57.111: D / myLogs (487): isCancelled: true

We see that in the first two cycles of the task, the isCancelled method returned false. Then we clicked Cancel (cancel result: true). The onCancelled ((Cancel). And the doInBackground method continued its work and completed the cycle to the end. But the onPostExecute method, which is usually called at the end of a task, was not recruited at all because we canceled the task (cancel method).

That is, although we canceled and completed, but the task continued to work. We need to complete the task ourselves. For this we read isCancelled and if he does true, then complete doInBackground method. That is, in our case we need to rewrite the doInBackground method:

    protected Void doInBackground(Void... params) {
      try {
        for (int i = 0; i < 5; i++) {
          TimeUnit.SECONDS.sleep(1);
          if (isCancelled()) return null;
          Log.d(LOG_TAG, "isCancelled: " + isCancelled());
        }
      } catch (InterruptedException e) {
        Log.d(LOG_TAG, "Interrupted");
        e.printStackTrace();
      }
      return null;
    }

We just added a check isCancelled. If it returns true, then we go (return). Of course, more complex tasks may require more sophisticated exit logic.

Now if we click Cancel in the process of completing the task, doInBackground will stop working as soon as it can:

08: 40: 12.439: D / myLogs (440): Begin
08: 40: 13.498: D / myLogs (440): isCancelled: false
08: 40: 14.558: D / myLogs (440): isCancelled: false
08: 40: 15.118: D / myLogs (440): cancel result: true
08: 40: 15.138: D / myLogs (440): Cancel

Delete or comment on the line just added:

if (isCancelled()) return null;

in the doInBackground method. We will now not require explicit verification of the cancellation of the assignment. We will check that the method will cancel if we pass it to true.

Let's rewrite cancelTask ​​():

  private void cancelTask() {
    if (mt == null) return;
    Log.d(LOG_TAG, "cancel result: " + mt.cancel(true));
  }

We pass true to the cancel method. That is, he will try to stop the flow himself.

Save, launch the application. press Start, And after a couple of seconds, we press Cancel. We look at the logs:

08: 58: 35.949: D / myLogs (545): Begin
08: 58: 37.023: D / myLogs (545): isCancelled: false
08: 58: 38.052: D / myLogs (545): isCancelled: false
08: 58: 38.688: D / myLogs (545): cancel result: true
08: 58: 38.698: D / myLogs (545): Interrupted
08: 58: 38.710: D / myLogs (545): Cancel

We see that the doInBackground method shut down because the sleep method generated an InterruptedException (Interrupted). That is, when we use sleep, the flow stop works. But not a fact that works in other cases. Therefore, I repeat again: do not rely especially on cancel (true), but use validation isCancelled or method onCancelled to complete your task. Or check to see if cancel (true) works on your terms.

Well, for the test, try pressing Cancel when the task is already completed or canceled. In this case, the cancel method will return false.

P.S. I tested it all on Android 2.3.3. In the forum, in the thread of this lesson, it was noticed that the behavior of canceling the task is slightly different in version 4 of Android.

In the next lesson:

- read the statuses of the task




Discuss in the forum [23 replies]

Leave a Comment