Very often, your application has to deal with the sequence that was shown in Listing 5–2:
To simplify this common pattern, Android defines the AsyncTask
class in Android 1.5 and above. The AsyncTask
class allows your application to easily perform a background operation and publish the result in the UI thread. Threads, Runnables, and other related objects are hidden from you for simplicity. Listing 5–5 shows how you would implement the sequence from Listing 5–2 using the AsyncTask
class.
public void onClick (View v) {
// AsyncTask<Params, Progress, Result> anonymous class
new AsyncTask<Integer, Void, BigInteger>() {
@Override
protected BigInteger doInBackground(Integer... params) {
return Fibonacci.recursiveFasterPrimitiveAndBigInteger(params[0]);
}
@Override
protected void onPostExecute(BigInteger result) {
mTextView.setText(result.toString());
}
}.execute(100000);
}
Since doInBackground()
is an abstract method, it has to be implemented. While you don't have to override onPostExecute()
, it is likely you will since one of the main purposes of AsyncTask
is to let you publish the result to the UI thread. The following AsyncTask
protected methods are all called from the UI thread:
The onProgressUpdate()
method is called when publishProgress()
is called from within doInBackground()
. This method allows you to do things like update the UI as the background operations are progressing. A typical example would be to update a progress bar as a file is being downloaded in the background. Listing 5–6 shows how multiple files can be downloaded.
AsyncTask<String, Object, Void> task = new AsyncTask<String, Object, Void>() {
private ByteArrayBuffer downloadFile(String urlString, byte[] buffer) {
try {
URL url = new URL(urlString);
URLConnection connection = url.openConnection();
InputStream is = connection.getInputStream();
//Log.i(TAG, "InputStream: " + is.getClass().getName()); // if you are
curious
//is = new BufferedInputStream(is); // optional line, try with and without
ByteArrayBuffer baf = new ByteArrayBuffer(640 * 1024);
int len;
while ((len = is.read(buffer)) != -1) {
baf.append(buffer, 0, len);
}
return baf;
} catch (MalformedURLException e) {
return null;
} catch (IOException e) {
return null;
}
}
@Override
protected Void doInBackground(String... params) {
if (params != null && params.length > 0) {
byte[] buffer = new byte[4 * 1024]; // try different sizes (1 for example
will give lower performance)
for (String url : params) {
long time = System.currentTimeMillis();
ByteArrayBuffer baf = downloadFile(url, buffer);
time = System.currentTimeMillis() - time;
publishProgress(url, baf, time);
}
} else {
publishProgress(null, null);
}
return null; // we don't care about any result but we still have to return
something
}
@Override
protected void onProgressUpdate(Object... values) {
// values[0] is the URL (String), values[1] is the buffer (ByteArrayBuffer),
values[2] is the duration
String url = (String) values[0];
ByteArrayBuffer buffer = (ByteArrayBuffer) values[1];
if (buffer != null) {
long time = (Long) values[2];
Log.i(TAG, "Downloaded " + url + " (" + buffer.length() + " bytes) in " +
time + " milliseconds");
} else {
Log.w(TAG, "Could not download " + url);
}
// update UI accordingly, etc
}
};
String url1 = "http://www.google.com/index.html";
String url2 = "http://d.android.com/reference/android/os/AsyncTask.html";
task.execute(url1, url2);
//task.execute("http://d.android.com/resources/articles/painless-threading.html"); // try that to get exception
The example in Listing 5–6 simply downloads the files in memory (a ByteArrayBuffer
object). If you want to save the file to permanent storage, you should also perform that operation in a thread other than the UI thread. In addition, the example showed files downloaded one after the other. Depending on your application's needs, it may be better to download several files in parallel.
NOTE: An AsyncTask
object must be created in the UI thread and can be executed only once.
When the doInBackground()
task is actually scheduled depends on the Android version. Before Android 1.6, tasks were executed serially, so only one background thread was needed. Starting with Android 1.6, the single background thread was replaced by a pool of threads allowing multiple tasks to be executed in parallel to allow for better performance. However, executing several tasks in parallel can cause serious problems when synchronization is not implemented properly or when tasks are executed or completed in a certain order (which may not be the order the developer anticipated). Consequently, the Android team plans to revert back to a single background thread model by default after Honeycomb. To continue to allow applications to execute tasks in parallel, a new executeOnExecutor()
API was added in Honeycomb, providing time for application developers to update their applications accordingly. This new API can be used together with AsyncTask.SERIAL_EXECUTOR
for serial execution or AsyncTask.THREAD_POOL_EXECUTOR
for parallel execution.
The planned future change shows that parallel execution requires a careful design and thorough tests. The Android team may have underestimated the potential problems or overestimated the applications' abilities to deal with them when switching to a pool of threads in Android 1.6, triggering the decision to revert back to a single thread model after Honeycomb. Applications' overall quality will improve while more experienced developers will still have the flexibility to execute tasks in parallel for better performance.
The AsyncTask
class can simplify your code when dealing with background tasks and user-interface updates, however it is not meant to fully replace the more basic classes Android defines to communicate between threads.
18.118.12.50