7.1: Create an AsyncTask
Contents:
- What you should already KNOW
- What you will LEARN
- What you will DO
- App overview
- Task 1: Set up the SimpleAsyncTask project
- Task 2: Create the AsyncTask subclass
- Task 3: Implement the final steps
- Coding challenge
- Summary
- Related concept
- Learn more
A thread is an independent path of execution in a running program. When an Android program is launched, the Android Runtime system creates a thread called the "Main" thread. As your program runs, each line of code is executed in a serial fashion, line by line. This main thread is how your application interacts with components from the Android UI Toolkit, and it's why the main thread is sometimes called the "UI thread". However, sometimes an application needs to perform resource-intensive work, such as downloading files, database queries, playing media, or computing complex analytics. This type of intensive work can block the UI thread if all of the code executes serially on a single thread. When the app is performing resources intensive work, the app does not respond to the user or draw on the screen because it is waiting for that work to be done. This can yield poor performance, which negatively affects the user experience. Users may get frustrated and uninstall your Android app if the performance of the app is slow.
To keep the user experience (UX) running smoothly and responding quickly to user gestures, the Android Framework provides a helper class called AsyncTask which processes work off of the UI thread. An AsyncTask is an abstract Java class that provides one way to move this intensive processing onto a separate thread, thereby allowing the UI thread to remain very responsive. Since the separate thread is not synchronized with the calling thread, it is called an asynchronous thread. An AsyncTask also contains a callback that allows you to display the results of the computation back in the UI thread.
In this practical, you will learn how to add a background task to your Android app using an AsyncTask.
What you should already KNOW
You should be able to:
- Create an Activity.
- Add a TextView to the layout for the activity.
- Programmatically get the id for the TextView and set its content.
- Use Button views and their onClick functionality.
What you will LEARN
During this practical, you will learn to:
- Add an AsyncTask to your app in order to run a task in the background, off of the UI thread.
- Identify and understand the benefits and drawbacks of using AsyncTask for background tasks.
What you will DO
During this practical, you will:
- Create a simple application that executes a background task using an AsyncTask.
- Run the app and see what happens when you rotate the screen.
App overview
You will build an app that has one TextView and one button. When the user clicks the button, the app sleeps for a random amount of time, and then displays a message in the TextView when it wakes up.
Here's what the finished app will look like:
Task 1. Set up the SimpleAsyncTask project
The SimpleAsyncTask UI is straightforward. It contains a button that launches the AsyncTask, and a TextView that displays the status of the application.
1.1 Create the layout
- Create a new project called SimpleAsyncTask using the Empty Activity template. (Accept the defaults for the other options.)
Open the activity_main.xml layout file.
- Change the root view to LinearLayout.
- In the "Hello World" TextView element, remove the
layout_constraint
attributes, if they are present. - Add the following essential UI elements to the layout:
View Attributes Values LinearLayout android:orientation vertical TextView android:text android:id
I am ready to start work! @+id/textView1
Button android:text android:onClick
Start Task startTask
Note: You can set the layout height and width of each view to whatever you want, as long the views remain on the screen independent of the screen size (usingwrap_content
ensures that this is the case).
The onClick attribute for the button will be highlighted in yellow, since the
startTask()
method is not yet implemented in the MainActivity. Place your cursor in the highlighted text, press Alt + Enter (Option + Enter on a Mac) and choose Create 'startTask(View) in 'MainActivity' to create the method stub in MainActivity.
Depending on your version of Android Studio, the activity_main.xml layout file will look something like the following:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ready_to_start"
android:id = "@+id/textView1"
android:textSize="24sp"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/start_task"
android:id="@+id/button"
android:layout_marginTop="56dp"
android:onClick="startTask" />
</LinearLayout>
Task 2. Create the AsyncTask subclass
Since AsyncTask is an abstract class, you need to subclass it in order to use it. In this example the AsyncTask will execute a very simple background task: it just sleeps for a random amount of time. In a real app, the background task could perform all sorts of work, from querying a database, to connecting to the Internet, to calculating the next Go move so that you can beat the current Go champion.
An AsyncTask has the following methods for performing work off of the main thread:
onPreExecute()
: This method runs on the UI thread, and is used for setting up your task (like showing a progress bar).doInBackground()
: This is where you implement the code to execute the work that is to be performed on the separate thread.onProgressUpdate()
: This is invoked on the UI thread and used for updating progress in the UI (such as filling up a progress bar)onPostExecute()
: Again on the UI thread, this is used for updating the results to the UI once the AsyncTask has finished loading.Note: A background or worker thread is any thread which is not the main or UI thread.
When you create an AsyncTask, you may need to give it information about the work which it is to perform, whether and how to report its progress, and in what form to return the result.
In this exercise you will use an AsyncTask subclass to define work that will run in a different thread than the UI thread, which will avoid any performance issues.
When you create an AsyncTask, you can configure it using these parameters:
- Params: The data type of the parameters sent to the task upon executing the
doInBackground()
override method. - Progress: The data type of the progress units published using the
onProgressUpdated()
override method. - Result: The data type of the result delivered by the
onPostExecute()
override method.
For example, an AsyncTask with the following class declaration would take a String
as a parameter in doInBackground()
(to use in a query, for example), an Integer for onProgressUpdate()
(percentage of job complete), and a Bitmap for the the result in onPostExecute()
(the query result):
public class MyAsyncTask extends AsyncTask <String, Integer, Bitmap>{}
2.1 Subclass the AsyncTask
In your first AsyncTask implementation, the AsyncTask subclass will be very simple. It does not require a query parameter or publish its progress. You will only be using the doInBackground()
and onPostExecute()
methods.
- Create a new Java class called SimpleAsyncTask that extends AsyncTask and takes three generic type parameters:
- Void for the params, since this AsyncTask does not require any inputs.
- Void for the progress type, since the progress is not published.
- A String as the result type, since you will update the TextView with a string when the AsyncTask has completed execution.
public class SimpleAsyncTask extends AsyncTask <Void, Void, String>{}
Note: The class declaration will be underlined in red, since the required methodThe AsyncTask will need to update the TextView once it has completed sleeping. The constructor will then need to include the TextView, so that it can be updated indoInBackground()
has not yet been implemented.onPostExecute()
.
- Define a member variable mTextView.
- Implement a constructor for AsyncTask that takes a TextView and sets mTextView to the one passed in TextView:
public SimpleAsyncTask(TextView tv) { mTextView = tv; }
2.2 Implement doInBackground()
- Add the required
doInBackground()
method. Place your cursor on the highlighted class declaration, press Alt + Enter (Option + Enter on a Mac) and select Implement methods. ChoosedoInBackground()
and click OK:@Override protected String doInBackground(Void... voids) { return null; }
Implement doInBackground() to:
- Generate a random integer between 0 and 10
- Multiply that number by 200
- Put the current thread to sleep. (Use
Thread.sleep()
) in a try/catch block. Return the String "Awake at last after xx milliseconds" (where xx is the number of milliseconds the app slept)
@Override protected String doInBackground(Void... voids) { // Generate a random number between 0 and 10 Random r = new Random(); int n = r.nextInt(11); // Make the task take long enough that we have // time to rotate the phone while it is running int s = n * 200; // Sleep for the random amount of time try { Thread.sleep(s); } catch (InterruptedException e) { e.printStackTrace(); } // Return a String result return "Awake at last after sleeping for " + s + " milliseconds!"; }
2.3 Implement onPostExecute()
When the doInBackground()
method completes, the return value is automatically passed to the onPostExecute()
callback.
- Implement
onPostExecute()
to take aString
argument (this is what you defined in the third parameter of AsyncTask and what yourdoInBackground()
method returned) and display that string in the TextView:protected void onPostExecute(String result) { mTextView.setText(result); }
Note: You can update the UI inonPostExecute()
because it is run on the main (UI) thread. You cannot callmTextView.setText()
indoInBackground()
, because that method is executed on a separate thread.
Task 3. Implement the final steps
3.1 Implement the method that starts the AsyncTask
Your app now has an AsyncTask that performs work in the background (or it would if you didn't call sleep()
as the simulated work.) You can now implement the method that gets called when the Start Task button is clicked, to trigger the background task.
- In the MainActivity.java file, add a member variable to store the TextView.
private TextView mTextView;
- In the
onCreate()
method, initializemTextView
to the TextView in the UI. - Add code to the
startTask()
method to create an instance ofSimpleAsyncTask
, passing the TextViewmTextView
to the constructor. Call
execute()
on thatSimpleAsyncTask
instance.Note: Theexecute()
method is where you pass in the parameters (separated by commas) that are then passed intodoInBackground()
by the system. Since this AsyncTask has no parameters, you will leave it blank.Update the TextView to show the text "Napping…"
public void startTask (View view) { // Put a message in the text view mTextView.setText("Napping... "); // Start the AsyncTask. // The AsyncTask has a callback that will update the text view. new SimpleAsyncTask(mTextView).execute(); }
Solution code for MainActivity:
package android.example.com.simpleasynctask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
// The TextView where we will show results
TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initialize mTextView
mTextView = (TextView) findViewById(R.id.textView1);
}
public void startTask (View view) {
// Put a message in the text view
mTextView.setText("Napping... ");
// Start the AsyncTask.
// The AsyncTask has a callback that will update the text view.
new SimpleAsyncTask(mTextView).execute();
}
}
3.2 Implement onSaveInstanceState()
- Run the app and click the Start Task button. How long does the app nap?
Click the Start Task button again, and while the app is napping, rotate the device. If the background task completes before you can rotate the phone, try again. Alternatively, you can update the code and make it sleep for a longer time period.
Note: You'll notice that when the device is rotated, the TextView resets to its initial content and the AsyncTask doesn't seem able to update the TextView.There are several things going on here:
- When you rotate the device, the system restarts the app, calling
onDestroy()
and thenonCreate()
, which restarts the activity lifecycle. Since the AsyncTasks are no longer connected to the lifecycle of your app, and cannot reconnect to the activity. - The AsyncTasks will continue running to completion in the background, consuming system resources, but never showing the results in the UI, which gets reset in
onCreate()
. It will never be able to update the TextView that was passed to it, since that particular TextView has also been destroyed. Eventually, the system run out of resources, and will fail. - Even without the AsyncTask, the rotation of the device resets all of the UI elements to their default state, which for the TextView implies a particular string that you set in the activity_main.xml file.
For these reasons, AsyncTasks are not well suited to tasks which may be interrupted by the destruction of the Activity. In use cases where this is critical you can use a different type of class called a Loader, which you will implement in a later practical.
In order to prevent the TextView from resetting to the initial string, you need to save its state. You've already learned how to maintain the state of views in a previous practical, using the SavedInstanceState class.
You will now implement
onSaveInstanceState()
to preserve the content of your TextView when the activity is spontaneously destroyed.Note: Not all uses of AsyncTask require you to handle the state of the views on rotation. This app uses a TextView to display the results of the app, so preserving the state is useful. In other cases, such as uploading a file, you may not need any persistent information in the UI, so retaining the state is not critical.- When you rotate the device, the system restarts the app, calling
Override the
onSaveInstanceState()
method in MainActivity to preserve the text inside the TextView when the activity is destroyed:outState.putString(TEXT_STATE, mTextView.getText().toString());
- Retrieve the value of the TextView when the activity is restored in the
onCreate()
method.// Restore TextView if there is a savedInstanceState if(savedInstanceState!=null){ mTextView.setText(savedInstanceState.getString(TEXT_STATE)); }
Solution code for MainActivity:
package android.example.com.simpleasynctask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;
/**
* The SimpleAsyncTask app contains a button that launches an AsyncTask
* which sleeps in the asynchronous thread for a random amount of time.
*/
public class MainActivity extends AppCompatActivity {
//Key for saving the state of the TextView
private static final String TEXT_STATE = "currentText";
// The TextView where we will show results
private TextView mTextView = null;
/**
* Initializes the activity.
* @param savedInstanceState The current state data
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initialize mTextView
mTextView = (TextView) findViewById(R.id.textView1);
// Restore TextView if there is a savedInstanceState
if(savedInstanceState!=null){
mTextView.setText(savedInstanceState.getString(TEXT_STATE));
}
}
/**`
* Handles the onCLick for the "Start Task" button. Launches the AsyncTask
* which performs work off of the UI thread.
*
* @param view The view (Button) that was clicked.
*/
public void startTask (View view) {
// Put a message in the text view
mTextView.setText(R.string.napping);
// Start the AsyncTask.
// The AsyncTask has a callback that will update the textview.
new SimpleAsyncTask(mTextView).execute();
}
/**
* Saves the contents of the TextView to restore on configuration change.
* @param outState The bundle in which the state of the activity is saved when it is spontaneously destroyed.
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Save the state of the TextView
outState.putString(TEXT_STATE, mTextView.getText().toString());
}
}
Solution code
Android Studio project: SimpleAsyncTask
Coding challenge
Challenge: AsyncTask provides another very useful override method: onProgressUpdate()
, which allows you to update the UI while the AsyncTask is running. Use this method to update the UI with the current sleep time. Look to the AsyncTask documentation to see how onProgressUpdate()
is properly implemented. Remember that in the class definition of your AsyncTask, you will need to specify the data type to be used in the onProgressUpdate()
method.
Summary
- Avoid resource-intensive work in the UI thread which may make your UI sluggish or erratic.
- Any code that does not involve drawing the UI or responding to the user input should be moved from the UI thread to another, separate thread.
- An AsyncTask is an abstract Java class that moves intensive processing onto a separate thread.
- AsyncTask must be subclassed to be used.
- AsyncTask has 4 useful methods:
onPreExecute()
,doInBackground()
,onPostExecute()
andonProgressUpdate()
.
doInBackground()
is the only method that is run on a separate worker thread.- You should not call UI methods in your AsyncTask method.
- The other methods of AsyncTask run in the UI thread and allow calling methods of UI components.
- Rotating an Android device destroys and recreates an Activity. This can disassociate the UI from the background thread, which will continue to run.
Related concept
The related concept documentation is in Android Developer Fundamentals: Concepts.
Learn more
Android developer documentation:
- Processes and Threads
- Processing bitmaps off the UI thread using AsyncTask
- AsyncTask
Other resources:
Videos:
- Threading Performance 101 by Performance Guru Colt McAnlis. Learn more about the main thread and why it's bad to run long-running tasks on the main thread.
- Good AsyncTask Hunting by Colt McAnlis. Learn more about AsyncTasks.