Introduction
Again another resume article about Android. This time I will cover threading. And in the accompanying source code, there is an application for Android allowing you to experiment with the various options available.
Not many assumptions are made on the knowledge required for understanding this article. A basic knowledge of Android programming and knowing the concept of a thread should suffice.
Background
Long Running Tasks on the UI Thread
A usual Android application has only a single thread on which all work done by your application is executed. This thread is known as the UI thread. That also includes anything done in your eventhandlers and any updating of the user interface. The result of this is that if you are doing anything in an eventhandler that can take a long time to execute, the UI of your application will freeze/not be updated during this time. What's even more, Android will show a dialog telling the user that the application is not responding!
buttonDoIt.setOnClickListener(
new Button.OnClickListener(){
@Override public void onClick(View arg0)
{
LongRunningTask();
}
});
In which the method LongRunningTask
looks like:
public void LongRunningTask()
{
for(int i = 0; i < cnt; i++)
{
try {
Thread.sleep(1000);
} catch (Exception e) {
Log.v("Error: ", e.toString());
}
textView.setText("Progress: " + i);
}
}
What you need to do is execute your long running task on another thread than the UI thread. For this, you will need to create an Android thread.
Long Running Tasks on their Own Thread
To execute a long running task in another thread, you create a new thread and implement the long running code in the thread’s run()
method. After this, you start the thread by calling its start()
method.
However, there is a caveat here to. You cannot just update your UI from this other thread because of thread synchronization issues. Android enforces that updating the UI can only be done from a single thread: the UI thread. And therefore, you must delegate the updating back to the UI thread. You do this by inserting a message into the messagequeue of the UI thread. I will explain this a bit further, but for now, here’s the code how you do this.
Handler uiHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
textView.setText("Progress: " + msg.what);
}
};
private void CreateThread() {
Thread t = new Thread() {
public void run() {
for(int i = 0; i < cnt; i++)
{
try {
Thread.sleep(1000);
} catch (Exception e) {
Log.v("Error: ", e.toString());
}
if(feedBackByHandler)
{
textView.setText("Progress: " + i);
}
else
{
uiHandler.sendMessage(uiHandler.obtainMessage(i));
}
}
}
};
t.start();
}
Long Running Tasks on their Own Thread using AsyncTask
In the above item, you had to write a lot of boilerplate code for doing something as simple as performing a long running task and informing the UI of the progress made. Fortunately, there is a simpler way: you can create your own class derived from AsyncTask.
The AsyncTask class provides 4 methods you can override:
doInBackground
: Here you do the actual work. This method cannot access any UI controls and is executed on a separate thread. onProgressUpdate
: Called when publishing progress updates by calling publishProgress
in doInBackground
. You can directly access any UI controls in this method because it is executed on the UI thread. onPreExecute
: Called just before calling doInBackground
. You can directly access any UI controls in this method because it is executed on the UI thread. onPostExecute
: Called just after calling doInBackground
. You can directly access any UI controls in this method because it is executed on the UI thread.
What this does is execute your work on a separate thread, but handle all the messaging needed to update your UI on the UI thread. As a result, the code becomes:
class LongRunningAsyncTask extends AsyncTask<String, Integer, Integer> {
@Override
protected Integer doInBackground(String... dummy) {
int i = 0;
for(; i < cnt; i++)
{
try {
Thread.sleep(1000);
} catch (Exception e) {
Log.v("Error: ", e.toString());
}
if(feedBackInBackground)
{
textView.setText("Progress: " + i);
}
else
{
publishProgress(i);
}
}
return i;
}
@Override
protected void onProgressUpdate(Integer... progress) {
textView.setText("Progress: " + progress[0]);
}
@Override
protected void onPreExecute() {
textView.setText("Started!");
}
@Override
protected void onPostExecute(Integer result) {
textView.setText("Finished with " + result + "!");
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
Button buttonDoIt = (Button)findViewById(R.id.buttonDoIt);
buttonDoIt.setOnClickListener(
new Button.OnClickListener(){
@Override public void onClick(View arg0)
{
feedBackInBackground = chkOnBackground.isChecked();
new LongRunningAsyncTask().execute("");
}
});
}
Threads, Loopers and Handlers
You will surely have an idea of what a thread is, but what is a handler and a looper?
A handler is the Android way of making it possible to handle custom messages in the messageloop of a thread. Okay, what does this mean?
As you already know, a thread is a flow of execution in your application and it can only do one single thing at a time. If you provide it an implementation of the run()
method, it will execute this method and when the method is finished, the thread will end its execution and be no longer available.
In code, you would have something like this:
Thread t = new Thread() {
public void run() {
}
};
t.start();
You can imagine that you would like a thread which is always available to you to do some stuff. You could create a queue in which you insert messages and those messages tell the thread what to do. In the thread, you write a loop which continuously checks this queue for messages and if you find one, you execute the corresponding work. This is called “the messageloop”.
In code, you would have something like this:
Thread t = new Thread() {
public void run() {
for(;;)
{
if(messageQueue.size() != 0)
{
}
}
}
};
t.start();
Threads you create yourself have no such messageloop by default, but the UI thread does. This messageloop is used to process the messages send by the Android operating system to your application. If you click a button, this results in a message pushed on the messagequeue, and the messageloop pulls it from this queue and handles it by calling your onClick
event handler. While it is executing your onClick
handler, it cannot do anything else. The creation of the queue and the managing of messages in the queue is done by a Looper
object. If you want to put your own custom messages in the queue to be processed by this thread, you need a Handler
object: if the Looper
sees your custom message in the messagequeue, it will call the Handler
of the thread and hand it your message. In the handleMessage
of your custom Handler
, you then process this message.
Resuming we have the following:
- Thread: A thread is a flow of execution in your program
- Looper: A looper implements a
for
loop and a queue into which messages can be posted. Inside the for
-loop, it checks for messages in the queue. - Handler: A handler is called by a looper to process the messages in the queue. The handler must know what to do by the id of the message.
- Message: A message identifies some work to perform. You create messages by calling any of the
obtainMessage
methods of the handler.
A Thread
can have only a single Looper
and a single Handler
associated with it. So if you create a Looper
or a Handler
in a thread, Android will check if one exists already and if so, will hand you the existing one, and if not it will create a new object for you and automatically associate the looper or handler with the thread on which you created the object.
Long Running Tasks using Handlers: Blocking the UI Thread
So, the UI Thread already has a Looper associated with it implementing the default application messageloop. If you supply it a handler, then this will get used by the messageloop.
Because a thread can only do a single thing at a time, if you execute a long running operation in your handler, the messageloop cannot handle any other messages, like subsequent clicking of for example a checkbox.
Handler uiHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
for(int i = 0; i < cnt; i++)
{
try {
Thread.sleep(1000);
} catch (Exception e) {
Log.v("Error: ", e.toString());
}
}
textView.setText(textView.getText()+"Did you succeed?");
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
Button buttonDoIt = (Button)findViewById(R.id.buttonDoIt);
buttonDoIt.setOnClickListener(
new Button.OnClickListener(){
@Override public void onClick(View arg0)
{
uiHandler.sendMessage(uiHandler.obtainMessage());
}
});
}
Long Running Tasks using Handlers: Not Blocking the UI Thread
To have a handler that does not block the UI thread, you must associate it with a different thread, thus you must create a new thread, then associate a new looper with it and finally a handler. And it is to this handler you must send your message.
public class HandlerNonBlockingHandlerActivity extends Activity {
Handler threadHandler = null;
Handler uiHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
textView.setText(textView.getText()+"Did you succeed?");
}
};
private void CreateThread() {
Thread t = new Thread() {
public void run() {
Looper.prepare();
threadHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
for(int i = 0; i < cnt; i++)
{
try {
Thread.sleep(1000);
} catch (Exception e) {
Log.v("Error: ", e.toString());
}
}
uiHandler.sendMessage(uiHandler.obtainMessage());
}
};
Looper.loop();
}
};
t.start();
}
@Override
public void onCreate(Bundle savedInstanceState) {
Button buttonDoIt = (Button)findViewById(R.id.buttonDoIt);
buttonDoIt.setOnClickListener(
new Button.OnClickListener(){
@Override public void onClick(View arg0)
{
threadHandler.sendMessage(threadHandler.obtainMessage());
}
});
CreateThread();
}
}
Do Try This At Home: The Code
The code has seven activities demonstrating the concepts explained above.
When you startup the application, you see the following screen:
Each entry in the list demonstrates a concept and provides controls to experiment.
Action On UIThread: LongRunningTaskOnUIThread
This sample demonstrates what you should not do: call the long running method in the onClick
handler.
public class LongRunningTaskOnUIThread extends Activity {
public void LongRunningTask()
{
for(int i = 0; i < cnt; i++)
{
try {
Thread.sleep(1000);
} catch (Exception e) {
Log.v("Error: ", e.toString());
}
textView.setText("Progress: " + i);
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.text_view);
textView=(TextView)findViewById(R.id.textView);
editTextTaskDuration=(EditText)findViewById(R.id.editTextTaskDuration);
editTextTaskDuration.setText("" + cnt);
Button buttonDoIt = (Button)findViewById(R.id.buttonDoIt);
buttonDoIt.setOnClickListener(
new Button.OnClickListener(){
@Override public void onClick(View arg0)
{
String taskDurationAsString = editTextTaskDuration.getText().toString();
cnt = Integer.parseInt(taskDurationAsString);
LongRunningTask();
}
});
}
private TextView textView;
private EditText editTextTaskDuration;
private int cnt = 5;
}
Running the sample shows the following screen:
If you push the button, there are three things to notice:
- Although the code updates a textbox inside the loop, when running the program the text in the textbox does not get updated until after about 5 seconds (or whatever task duration you filled in in the edit box): the time it takes to finish the loop.
- When clicking the checkbox during the first 5 seconds after pressing the button, the checkbox does not alter its state.
- If you set the task duration to a high value (20 did it for me), you’ll get an exception. That is because Android notices you are blocking the UI thread.
These two different things happening have a common cause: your loop is executed on the UI thread and therefore prohibits any processing of other events.
Action On Thread: LongRunningTaskOnOwnThread
This activity demonstrates executing a long running operation on another thread and giving feedback about progression in the UI.
public class LongRunningTaskOnOwnThread extends Activity {
Handler uiHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
textView.setText("Progress: " + msg.what);
}
};
private void CreateThread() {
Thread t = new Thread() {
public void run() {
for(int i = 0; i < cnt; i++)
{
try {
Thread.sleep(1000);
} catch (Exception e) {
Log.v("Error: ", e.toString());
}
if(feedBackByHandler)
{
textView.setText("Progress: " + i);
}
else
{
uiHandler.sendMessage(uiHandler.obtainMessage(i));
}
}
}
};
t.start();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.text_view_withcheckbox);
textView=(TextView)findViewById(R.id.textView);
chkOnHandler = (CheckBox)findViewById(R.id.checkBoxConfig);
editTextTaskDuration=(EditText)findViewById(R.id.editTextTaskDuration);
editTextTaskDuration.setText("" + cnt);
Button buttonDoIt = (Button)findViewById(R.id.buttonDoIt);
buttonDoIt.setOnClickListener(
new Button.OnClickListener(){
@Override public void onClick(View arg0)
{
feedBackByHandler = chkOnHandler.isChecked();
String taskDurationAsString = editTextTaskDuration.getText().toString();
cnt = Integer.parseInt(taskDurationAsString);
CreateThread();
}
});
}
private TextView textView;
private CheckBox chkOnHandler;
private EditText editTextTaskDuration;
private int cnt = 5;
private boolean feedBackByHandler = true;
}
Running the sample shows the following screen:
The checkbox “Update on custom thread” allows to select on which thread the UI should be updated, the UI thread being the only correct one of course. So if you check the option the application will crash, what is to be expected.
Running this code you will notice two things, the opposite of what happened above:
- If you select the correct update option (see above), you will notice you receive the correct feedback in the UI.
- If you try to check the bottom checkbox, you will succeed.
By executing your long running operation on a different thread, you are no longer blocking the UI thread, thus allowing the UI to respond to other events.
Action with Async: LongRunningTaskWithAsyncTask
In the previous activity, we had to write a lot of boiler plate code. This activity does exactly the same but uses an AsyncTask
object, which provides feedback automatically on the UI thread.
public class LongRunningTaskWithAsyncTask extends Activity {
class LongRunningAsyncTask extends AsyncTask<String, Integer, Integer> {
@Override
protected Integer doInBackground(String... dummy) {
int i = 0;
for(; i < cnt; i++)
{
try {
Thread.sleep(1000);
} catch (Exception e) {
Log.v("Error: ", e.toString());
}
if(feedBackInBackground)
{
textView.setText("Progress: " + i);
}
else
{
publishProgress(i);
}
}
return i;
}
@Override
protected void onProgressUpdate(Integer... progress) {
textView.setText("Progress: " + progress[0]);
}
@Override
protected void onPreExecute() {
textView.setText("Started!");
}
@Override
protected void onPostExecute(Integer result) {
textView.setText("Finished with " + result + "!");
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.text_view);
textView=(TextView)findViewById(R.id.text_view_withcheckbox);
chkOnBackground = (CheckBox)findViewById(R.id.checkBoxConfig);
chkOnBackground.setText("Update in doInBackground");
editTextTaskDuration=(EditText)findViewById(R.id.editTextTaskDuration);
editTextTaskDuration.setText("" + cnt);
Button buttonDoIt = (Button)findViewById(R.id.buttonDoIt);
buttonDoIt.setOnClickListener(
new Button.OnClickListener(){
@Override public void onClick(View arg0)
{
feedBackInBackground = chkOnBackground.isChecked();
String taskDurationAsString = editTextTaskDuration.getText().toString();
cnt = Integer.parseInt(taskDurationAsString);
new LongRunningAsyncTask().execute("");
}
});
}
private TextView textView;
private CheckBox chkOnBackground;
private EditText editTextTaskDuration;
private int cnt = 5;
private boolean feedBackInBackground = false;
}
Running the sample shows the following screen:
Blocking Action: HandlerBlockingHandlerActivity
This demonstrates the fact that a handler is associated with the thread on which it is created. It also demonstrates that simply the fact of creating a handler is not sufficient to make sure your long running operations aren’t blocking the UI !
In this case, the handler is created on the UI thread, thus the long running operation is executed on the UI thread again blocking any updates of the UI.
public class HandlerBlockingHandlerActivity extends Activity {
Handler uiHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
for(int i = 0; i < cnt; i++)
{
try {
Thread.sleep(1000);
} catch (Exception e) {
Log.v("Error: ", e.toString());
}
}
textView.setText(textView.getText()+"Did you succeed?");
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.text_view);
textView=(TextView)findViewById(R.id.textView);
editTextTaskDuration=(EditText)findViewById(R.id.editTextTaskDuration);
editTextTaskDuration.setText("" + cnt);
Button buttonDoIt = (Button)findViewById(R.id.buttonDoIt);
buttonDoIt.setOnClickListener(
new Button.OnClickListener(){
@Override public void onClick(View arg0)
{
String taskDurationAsString = editTextTaskDuration.getText().toString();
cnt = Integer.parseInt(taskDurationAsString);
uiHandler.sendMessage(uiHandler.obtainMessage());
}
});
}
private TextView textView;
private EditText editTextTaskDuration;
private int cnt = 5;
}
Running the sample shows the following screen:
Again you have the same two controls: a textbox
in which we’d like to show the progress and a checkbox
for you to check.
In this case, none of the above will succeed because the handler was created on the UI thread.
NonBlocking Action: HandlerNonBlockingHandlerActivity
This is the counterpart of the above:
Here, first a Thread
is created and inside it a Looper
to have a message queue. Then a Handler
is created which is associated to the created thread.
public class HandlerNonBlockingHandlerActivity extends Activity {
Handler threadHandler = null;
Handler uiHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
textView.setText(textView.getText()+"Did you succeed?");
}
};
private void CreateThread() {
Thread t = new Thread() {
public void run() {
Looper.prepare();
threadHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
for(int i = 0; i < cnt; i++)
{
try {
Thread.sleep(1000);
} catch (Exception e) {
Log.v("Error: ", e.toString());
}
}
uiHandler.sendMessage(uiHandler.obtainMessage());
}
};
Looper.loop();
}
};
t.start();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.text_view);
textView=(TextView)findViewById(R.id.textView);
editTextTaskDuration=(EditText)findViewById(R.id.editTextTaskDuration);
editTextTaskDuration.setText("" + cnt);
Button buttonDoIt = (Button)findViewById(R.id.buttonDoIt);
buttonDoIt.setOnClickListener(
new Button.OnClickListener(){
@Override public void onClick(View arg0)
{
String taskDurationAsString = editTextTaskDuration.getText().toString();
cnt = Integer.parseInt(taskDurationAsString);
threadHandler.sendMessage(threadHandler.obtainMessage());
}
});
CreateThread();
}
private TextView textView;
private EditText editTextTaskDuration;
private int cnt = 5;
}
Deepdive into Handler: DeepDiveHandler
Now, let us dive a little deeper into what a Handler
is actually capable of and how to send Message
s.
public class DeepDiveHandler extends Activity {
static final int MessageFromHandler = 0;
static final int MessageAfterLooper = 1;
static final int MessageShowText = 0;
static final int MessageQuitLooper = 1;
Handler threadHandler = null;
Handler uiHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what)
{
case MessageFromHandler:
long uptimeSec = SystemClock.uptimeMillis() / 1000;
long minutes = uptimeSec / 60;
uptimeSec = uptimeSec % 60;
textView.setText("Message=" + minutes + ":" + uptimeSec);
break;
case MessageAfterLooper:
textView.setText("After Looper.Loop()");
}
}
};
private void CreateThread() {
Thread t = new Thread() {
public void run() {
Looper.prepare();
threadHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what)
{
case MessageShowText:
Message uiMsg = uiHandler.obtainMessage();
uiMsg.what = MessageFromHandler;
uiHandler.sendMessage(uiMsg);
break;
case MessageQuitLooper:
this.getLooper().quit();
break;
}
}
};
Looper.loop();
Message uiMsg = uiHandler.obtainMessage();
uiMsg.what = MessageAfterLooper;
uiHandler.sendMessage(uiMsg);
}
};
t.start();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.deepdivehandler_view);
textView=(TextView)findViewById(R.id.textView);
Button buttonStartIt = (Button)findViewById(R.id.buttonStartIt);
buttonStartIt.setOnClickListener(
new Button.OnClickListener(){
@Override public void onClick(View arg0)
{
CreateThread();
}
});
Button buttonDoIt = (Button)findViewById(R.id.buttonDoIt);
buttonDoIt.setOnClickListener(
new Button.OnClickListener(){
@Override public void onClick(View arg0)
{
Message showMsg = threadHandler.obtainMessage();
showMsg.what = MessageShowText;
threadHandler.sendMessage(showMsg);
}
});
Button buttonStopIt = (Button)findViewById(R.id.buttonStopIt);
buttonStopIt.setOnClickListener(
new Button.OnClickListener(){
@Override public void onClick(View arg0)
{
Message quitLooperMsg = threadHandler.obtainMessage();
quitLooperMsg.what = MessageQuitLooper;
threadHandler.sendMessage(quitLooperMsg);
}
});
}
private TextView textView;
}
In the sample screen, you have 3 buttons:
- Start thread: This button creates a new thread with an associated
Looper
and Handler
- Take action: This button sends messages to the
Handler
created by the “Start thread” button - Stop thread: This button also sends a message to the
Handler
created by the “Start thread” button, but with a different payload than the “Take action” button, resulting in the thread to stop running.
Ok, what is happening here?
The “Take action” button sends a message to the Handler
with a what-parameter of MessageShowText
. This results in the Handler
sending a message to the UI handler which then updates the text in the TextView
.
The “Stop thread” button sends a message to the Handler
with a what-parameter of MessageQuitLooper
. This results in a call of the Looper
‘s quit()
-method thus ending the looper and executing any code after the Looper
‘s loop()
-method call.
Deepdive into Async: DeepDiveAsync
Next, we’ll dive a little deeper in the functionality of AsyncTask
.
public class DeepDiveAsync extends Activity {
class DeepDiveAsyncTask extends AsyncTask<String, Integer, Integer> {
@Override
protected Integer doInBackground(String... dummy) {
int i = 0;
try {
for(; i < m_duration; i++)
{
Thread.sleep(1000);
if(m_checkForCancellation) {
if(isCancelled()) {
return i;
}
}
publishProgress(i);
}
} catch (Exception e) {
Log.v("Error: ", e.toString());
}
return i;
}
@Override
protected void onProgressUpdate(Integer... progress) {
textView.setText("Progress: " + progress[0]);
}
@Override
protected void onPreExecute() {
textView.setText("Started!");
}
@Override
protected void onPostExecute(Integer result) {
textView.setText("Finished with " + result + "!");
}
@Override
protected void onCancelled() {
textView.setText("Cancelled!");
};
public int getDuration()
{
return m_duration;
}
public void setDuration(int duration)
{
m_duration = duration;
}
public boolean getCheckForCancellation()
{
return m_checkForCancellation;
}
public void setCheckForCancellation(boolean checkForCancellation)
{
m_checkForCancellation = checkForCancellation;
}
private int m_duration = 5;
private boolean m_checkForCancellation = false;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.deepdiveasync_view);
textView=(TextView)findViewById(R.id.textView);
editTextTaskDuration=(EditText)findViewById(R.id.editTextTaskDuration);
checkBoxCheckForCancelation=(CheckBox)findViewById(R.id.checkBoxCheckForCancelation);
deepDiveAsyncTask = new DeepDiveAsyncTask();
editTextTaskDuration.setText(Integer.toString(deepDiveAsyncTask.getDuration()));
checkBoxCheckForCancelation.setChecked(deepDiveAsyncTask.getCheckForCancellation());
Button buttonDoIt = (Button)findViewById(R.id.buttonDoIt);
buttonDoIt.setOnClickListener(
new Button.OnClickListener(){
@Override public void onClick(View arg0)
{
String taskDurationAsString = editTextTaskDuration.getText().toString();
int taskDuration = Integer.parseInt(taskDurationAsString);
deepDiveAsyncTask.setDuration(taskDuration);
deepDiveAsyncTask.setCheckForCancellation(checkBoxCheckForCancelation.isChecked());
deepDiveAsyncTask.execute("");
}
});
Button buttonCancelItWithInterrupt = (Button)findViewById(R.id.buttonCancelItWithInterrupt);
buttonCancelItWithInterrupt.setOnClickListener(
new Button.OnClickListener(){
@Override public void onClick(View arg0)
{
deepDiveAsyncTask.cancel(true);
}
});
Button buttonCancelItNoInterrupt = (Button)findViewById(R.id.buttonCancelItNoInterrupt);
buttonCancelItNoInterrupt.setOnClickListener(
new Button.OnClickListener(){
@Override public void onClick(View arg0)
{
deepDiveAsyncTask.cancel(false);
}
});
}
private TextView textView;
private EditText editTextTaskDuration;
private CheckBox checkBoxCheckForCancelation;
private DeepDiveAsyncTask deepDiveAsyncTask;
}
In the sample application, we have 3 buttons:
- Take action: This button creates the
AsyncTask
using the parameters provided in the Task duration EditText
and the Check for cancellation CheckBox
- Cancel action with interrupt: This button calls the
AsyncTask
‘s cancel(boolean mayInterruptIfRunning)
with a parameter with value true
allowing Android to interrupt the thread running the task. - Cancel action no interrupt: This button calls the
AsyncTask
‘s cancel(boolean mayInterruptIfRunning)
with a parameter with value false
not allowing Android to interrupt the thread running the task.
What is happening?
The “Cancel action with interrupt” button calls the AsyncTask
‘s cancel
-method with a parameter of value true
. This way, we let Android interrupt the action immediately which we can see when pressing this button: the method onCancelled()
of the AsyncTask
is called immediately showing the message “Cancelled!”
The “Cancel action no interrupt” button calls the exact same method but with a parameter of value false
. The result of this is that if you now call the method isCancelled()
in your doInBackground
method implementation, it will return true
and YOU can end the method. Read that sentence again: yes YOU are responsible for calling the isCancelled()
method and taking the necessary steps to stop the execution. In the provided test application, the checkbox “Check for cancellation” allows you to bypass this check. If you do that, you will notice that your task is NOT cancelled.
Filed under: Android, CodeProject Tagged: android, codeproject, threading