AsyncTask Android example
AsyncTask Android example
Question
I was reading about AsyncTask
, and I tried the simple program below. But it does not seem to work. How can I make it work?
public class AsyncTaskActivity extends Activity {
Button btn;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btn = (Button) findViewById(R.id.button1);
btn.setOnClickListener((OnClickListener) this);
}
public void onClick(View view){
new LongOperation().execute("");
}
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) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed");
return null;
}
@Override
protected void onPostExecute(String result) {
}
@Override
protected void onPreExecute() {
}
@Override
protected void onProgressUpdate(Void... values) {
}
}
}
I am just trying to change the label after 5 seconds in the background process.
This is my main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="false"
android:max="10"
android:padding="10dip">
</ProgressBar>
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start Progress" >
</Button>
<TextView android:id="@+id/output"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Replace"/>
</LinearLayout>
Accepted Answer
Ok, you are trying to access the GUI via another thread. This, in the main, is not good practice.
The AsyncTask executes everything in doInBackground()
inside of another thread, which does not have access to the GUI where your views are.
preExecute()
and postExecute()
offer you access to the GUI before and after the heavy lifting occurs in this new thread, and you can even pass the result of the long operation to postExecute()
to then show any results of processing.
See these lines where you are later updating your TextView:
TextView txt = findViewById(R.id.output);
txt.setText("Executed");
Put them in onPostExecute()
.
You will then see your TextView text updated after the doInBackground
completes.
I noticed that your onClick listener does not check to see which View has been selected. I find the easiest way to do this is via switch statements. I have a complete class edited below with all suggestions to save confusion.
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.Settings.System;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.view.View.OnClickListener;
public class AsyncTaskActivity extends Activity implements OnClickListener {
Button btn;
AsyncTask<?, ?, ?> runningTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btn = findViewById(R.id.button1);
// Because we implement OnClickListener, we only
// have to pass "this" (much easier)
btn.setOnClickListener(this);
}
@Override
public void onClick(View view) {
// Detect the view that was "clicked"
switch (view.getId()) {
case R.id.button1:
if (runningTask != null)
runningTask.cancel(true);
runningTask = new LongOperation();
runningTask.execute();
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
// Cancel running task(s) to avoid memory leaks
if (runningTask != null)
runningTask.cancel(true);
}
private final class LongOperation extends AsyncTask<Void, Void, String> {
@Override
protected String doInBackground(Void... params) {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// We were cancelled; stop sleeping!
}
}
return "Executed";
}
@Override
protected void onPostExecute(String result) {
TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed"); // txt.setText(result);
// You might want to change "executed" for the returned string
// passed into onPostExecute(), but that is up to you
}
}
}
Read more… Read less…
I'm sure it is executing properly, but you're trying to change the UI elements in the background thread and that won't do.
Revise your call and AsyncTask as follows:
Calling Class
Note: I personally suggest using onPostExecute()
wherever you execute your AsyncTask thread and not in the class that extends AsyncTask itself. I think it makes the code easier to read especially if you need the AsyncTask in multiple places handling the results slightly different.
new LongThread() {
@Override public void onPostExecute(String result) {
TextView txt = (TextView) findViewById(R.id.output);
txt.setText(result);
}
}.execute("");
LongThread class (extends AsyncTask):
@Override
protected String doInBackground(String... params) {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "Executed";
}
I have created a simple example for using AsyncTask of Android. It starts with onPreExecute(), doInBackground(), publishProgress()
and finally onProgressUpdate()
.
In this, doInBackground() works as a background thread, while other works in the UI Thread. You can't access an UI element in doInBackground(). The sequence is the same as I have mentioned.
However, if you need to update any widget from doInBackground
, you can publishProgress
from doInBackground
which will call onProgressUpdate
to update your UI widget.
class TestAsync extends AsyncTask<Void, Integer, String> {
String TAG = getClass().getSimpleName();
protected void onPreExecute() {
super.onPreExecute();
Log.d(TAG + " PreExceute","On pre Exceute......");
}
protected String doInBackground(Void...arg0) {
Log.d(TAG + " DoINBackGround", "On doInBackground...");
for (int i=0; i<10; i++){
Integer in = new Integer(i);
publishProgress(i);
}
return "You are at PostExecute";
}
protected void onProgressUpdate(Integer...a) {
super.onProgressUpdate(a);
Log.d(TAG + " onProgressUpdate", "You are in progress update ... " + a[0]);
}
protected void onPostExecute(String result) {
super.onPostExecute(result);
Log.d(TAG + " onPostExecute", "" + result);
}
}
Call it like this in your activity:
new TestAsync().execute();
Move these two lines:
TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed");
out of your AsyncTask's doInBackground
method and put them in the onPostExecute
method. Your AsyncTask
should look something like this:
private class LongOperation extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String... params) {
try {
Thread.sleep(5000); // no need for a loop
} catch (InterruptedException e) {
Log.e("LongOperation", "Interrupted", e);
return "Interrupted";
}
return "Executed";
}
@Override
protected void onPostExecute(String result) {
TextView txt = (TextView) findViewById(R.id.output);
txt.setText(result);
}
}
Background / Theory
AsyncTask allows you to run a task on a background thread, while publishing results to the UI thread.
The user should always able to interact with the app so it is important to avoid blocking the main (UI) thread with tasks such as downloading content from the web.
This is why we use an
AsyncTask
.It offers a straightforward interface by wrapping the UI thread message queue and handler that allow you to send and process runnable objects and messages from other threads.
Implementation
AsyncTask is a generic class. (It takes parameterized types in its constructor.)
It uses these three generic types:
Params
- the type of the parameters sent to the task upon execution.
Progress
- the type of the progress units published during the background computation.
Result
- the type of the result of the background computation.
Not all types are always used by an asynchronous task. To mark a type as unused, simply use the type Void:
private class MyTask extends AsyncTask<Void, Void, Void> { ... }
These three parameters correspond to three primary functions you can override in AsyncTask
:
doInBackground(Params...)
onProgressUpdate(Progress...)
onPostExecute(Result)
To execute AsyncTask
- Call
execute()
with parameters to be sent to the background task.
What Happens
On main/UI thread,
onPreExecute()
is called.- To initialize something in this thread. (E.g. show a progress bar on the user interface.)
On a background thread,
doInBackground(Params...)
is called.- (
Params
were passed viaexecute
.) - Where the long-running task should happen.
Must override at least
doInBackground()
to use AsyncTask.Call
publishProgress(Progress...)
to update the user interface with a display of progress (e.g. UI animation or log text printed) while the background computation is still executing.- Causes
onProgressUpdate()
to be called.
- Causes
- (
On the background thread a result is returned from
doInBackground()
.- (This triggers the next step.)
On main/UI thread,
onPostExecute()
is called with the returned result.
Examples
In both examples the "blocking task" is a download from the web.
- Example A downloads an image and displays it in an ImageView, while
- Example B downloads some files.
Example A
The doInBackground()
method downloads the image and stores it in an object of type BitMap. The onPostExecute()
method takes the bitmap and places it in the ImageView.
class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
ImageView bitImage;
public DownloadImageTask(ImageView bitImage) {
this.bitImage = bitImage;
}
protected Bitmap doInBackground(String... urls) {
String urldisplay = urls[0];
Bitmap mBmp = null;
try {
InputStream in = new java.net.URL(urldisplay).openStream();
mBmp = BitmapFactory.decodeStream(in);
} catch (Exception e) {
Log.e("Error", e.getMessage());
e.printStackTrace();
}
return mBmp;
}
protected void onPostExecute(Bitmap result) {
bitImage.setImageBitmap(result);
}
}
Example B
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
Example B execution
new DownloadFilesTask().execute(url1, url2, url3);
How to memorize the parameters used in AsyncTask?
Don't
If you are new to AsyncTask then it is very common to get confused while writing an AsyncTask. The main culprits are the parameters used in the AsyncTask, i.e., AsyncTask<A, B, C>
. Based on the A, B, C (arguments) signature of the methods differs which makes things even more confusing.
Keep it simple!
The key is don't memorize. If you can visualize what your task really needs to do then writing the AsyncTask with the correct signature at the first attempt would be a piece of cake. Just figure out what your Input
, Progress
, and Output
are, and you will be good to go.
So what is an AsyncTask?
AsyncTask is a background task that runs in the background thread. It takes an Input
, performs Progress
and gives an Output
.
I.e.,
AsyncTask<Input, Progress, Output>
.
For example:
What is the relationship with methods?
Between
AsyncTask
anddoInBackground()
doInBackground()
andonPostExecute(),
onProgressUpdate()` are also related
How to write that in the code?
DownloadTask extends AsyncTask<String, Integer, String>{
// Always same signature
@Override
public void onPreExecute()
{}
@Override
public String doInbackGround(String... parameters)
{
// Download code
int downloadPerc = // Calculate that
publish(downloadPerc);
return "Download Success";
}
@Override
public void onPostExecute(String result)
{
super.onPostExecute(result);
}
@Override
public void onProgressUpdate(Integer... parameters)
{
// Show in spinner, and access UI elements
}
}
How will you run this Task?
new DownLoadTask().execute("Paradise.mp3");