AsyncTask / UI Thread

Background Tasks – Part 3

Previously we looked at using standard threads to keep things off the UI thread and also covered various mechanisms for getting execution back on to the UI thread for when we need to update View objects. However our code was beginning to get quite messy with us having to define lots of Runnables in order to switch execution between threads. In this article we’ll look at AsyncTask which provides a much cleaner mechanism for updating Views.

AsyncTask provides us with a life-cycle framework for background processes. When we subclass AsyncTask we override one or more methods which get called at different points of the task’s life-cycle, and some methods are invoked from the UI thread, with others being invoked from the background thread.

The method that you must override is doInBackground() which is invoked on the background thread and is where you perform the actual work. There are three other optional methods each of which is invoked from the UI thread:

  • onPreExecute – this is called before doInBackground() and is where you can initialise things before the background task begins.
  • onProgressUpdate – this is called while doInBackground() is executing and is where you can make changes to Views indicating some king of progress.
  • onPostExecute – this is called after doInBackground() and is where you can update things after the background task completes.

As a general rule of thumb, if you find that you only need to override doInBackground() and none of the other methods, then maybe you can use a simple Thread instead.

AsyncTask is a generic class and when you override it you must define three object types to determine the type of objects passed in to the task, to represent progress, and returned from the task. Let’s consider the example of a task which is going to perform some kind of download from the network, update a ProgressBar based upon an integer value, and return a string value:

[java] class MyAsyncTask extends AsyncTask
{
private final Activity activity;

private ProgressBar progress;
private int count = 0;

public MyAsyncTask( Activity activity )
{
this.activity = activity;
}

@Override
protected void onPreExecute()
{
progress = (ProgressBar)
activity.findViewById( R.id.progress );
}

@Override
protected String doInBackground( URL… params )
{
String ret = null;
count = params.length;
for( int i = 0; i < count; i++ ) { publishProgress( i ); // Do something which // populates "ret" } return ret; } @Override protected void onProgressUpdate( Integer... values ) { progress.setMax( count ); progress.setProgress( values[0] ); } @Override protected void onPostExecute( String result ) { Toast.makeText( activity, result, Toast.LENGTH_SHORT ).show(); } } [/java] The generic types define the method signatures and, for example, ensures that the return type of doInBackground() matches the single argument of onPostExecute().

By implementing the methods in AsyncTask we no longer have to worry about manually switching between the UI and background threads as this is done for us automatically.

To actually invoke this we simply create an instance of MyAsyncTask and execute it with one or more URL arguments:

[java] public class MyActivity extends Activity
{
@Override
public void onCreate( Bundle savedInstanceState )
{
super.onCreate( savedInstanceState );
setContentView( R.layout.main );

// Get some urls

new MyAsyncTask( this ).execute( url1, url2, url3 );
}
}
[/java]

One of the problems with AsyncTask is that unless the developer fully understands which methods are executed on which thread, it is very easy to introduce problems. For example, I have seen a newbie put a network call in onPreExecute() and perform parsing of the response in doInBackground(). It looks as though there isn’t a problem because the network call is implemented within an AsyncTask implementation, but the network call is still performed on the UI thread.

Another issue with the above example is that the AsyncTask implementation holds a reference to the calling Activity which will not be released until the AsyncTask completes. This could potentially lead to a Context leak (as discussed here).

In Honeycomb Loaders were introduced which offer yet another mechanism for background processing which creates a much cleaner divide between what is executed on the UI thread and what is executed on the background thread. Also, it does not require us to hold Context references in order to do anything meaningful after execution has completed. In the next part of this series we’ll have a look at Loaders.

The source code for this article can be found here.

© 2012, Mark Allison. All rights reserved.

Copyright © 2012 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.

3 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.