AsyncTask

Very often, your application has to deal with the sequence that was shown in Listing 5–2:

  • Event is received in UI thread
  • Operation is to be executed in non-UI thread in response to event
  • UI needs to be updated with result of operation

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.

Listing 5–5. Using AsyncTask

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:

  • onPreExecute()
  • onProgressUpdate(Progress… values)
  • onPostExecute(Result result)
  • onCancelled()
  • onCancelled(Result result) (API introduced in Android 3.0)

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.

Listing 5–6. Downloading Multiple Files

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.60.232