Image 1. Threading and Asynchronous Processing

Image

Offloading intensive operations provides a smoother, more stable experience to the user. The Android SDK provides two easy ways to manage offload processing from the main UI thread: the AsyncTask class and the standard Java Thread class. An Activity or Fragment often needs to load data upon launch, which can be done asynchronously using a Loader class. In this chapter, you learn how to make your applications more responsive by knowing when and how to move intensive operations off the main UI thread to be handled asynchronously.

The Importance of Processing Asynchronously

Users demand responsive applications, time-intensive operations such as networking should not block the main UI thread. Some common blocking operations include:

• Any lengthy or complex calculation or operation

• Querying a data set of indeterminate size

• Parsing a data set

• Processing multimedia files, such as images, video, or audio

• Iterating over a data structure of indeterminate size

• Accessing network resources

• Accessing location-based services

• Accessing a content provider interface

• Accessing a local database

• Accessing a local file

• Accessing any service that uses any of the previous services

If your application is not responsive enough, it might be plagued with Application Not Responding (ANR) events. ANR events occur when the Android operating system decides that your application is not responding in a reasonable time and shuts that application down. Typically, these events happen when your application takes longer than 5 seconds to respond or complete a task.

On API Level 11 and later, moving certain operations off the main UI thread is mandatory. For example, networking code must be completed asynchronously. Your code violates system-wide StrictMode policies otherwise. Make sure to test on devices.

Offloading intensive operations from the main UI thread helps avoid the dreaded ANR event and provides a smoother, more stable experience to the user. However, you must still perform all UI operations on the main thread so some communication between these tasks may be desired. Even certain nonintensive operations are important to offload, such as reading or writing to the file system. While these are normally fast, occasionally a read or write might block for various reasons, including contention on the file or the file system itself. Mobile devices often use flash-based storage that uses wear-reduction algorithms that can significantly delay disk writes on occasion.

The Android SDK provides several ways to manage offload processing from the main UI thread:

• Use the AsyncTask helper class to easily complete tasks asynchronously and communicate back to the main UI thread.

• Use the standard Thread class to complete your processing, as you would in any Java application.

• Use the Loader class to facilitate the loading of data for use in an Activity or Fragment while still starting up quickly.

We discuss all three of these in this chapter.


Image Tip

Many of the code examples provided in this chapter are taken from the SimpleAsync application. The source code for the SimpleAsync application is provided for download on the book’s websites.


Image Working with the AsyncTask Class

The AsyncTask class (android.os.AsyncTask) is a special class for Android development that encapsulates background processing and helps facilitate communication to the UI thread while managing the lifecycle of the background task within the context of the Activity lifecycle.

The AsyncTask class is an abstract helper class for managing background operations that eventually are posted back to the UI thread. It creates a simpler interface for asynchronous operations than manually creating a Thread class. Internally, AsyncTask improves with the Android SDK, and in later versions, it can manage multiple tasks simultaneously using multiple physical cores and an internal thread pool.

Instead of creating threads for background processing and using messages and message handlers for updating the UI, you can create a subclass of AsyncTask and implement the appropriate callback methods. The important callbacks are:

• The onPreExecute() method runs on the UI thread before background processing begins.

• The doInBackground() method runs in the background and is where all the real work is done.

• The publishProgress() method, called from the doInBackground() method, periodically informs the UI thread about the background process progress. This method sends information to the UI process. Use this opportunity to send updated progress for a progress bar that the user can see.

• The onProgressUpdate() method runs on the UI thread whenever the doInBackground() method calls publishProgress(). This method receives information from the background process. Use this opportunity to update a ProgressBar control that the user can see.

• The onPostExecute() method runs on the UI thread once the background processing is completed.

When launched with the execute() method, the AsyncTask class handles processing in a background thread without blocking the UI thread.

Let’s look at a simple example. Here we have an Activity class that simply displays a TextView control on the screen. In its onCreate() method, it launches an asynchronous task called CounterTask, which slowly counts to 100. Every time it makes some progress (defined here as 5 percent), it updates the TextView control in the UI. Here is the complete implementation:

public class SimpleAsyncActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        CountingTask tsk = new CountingTask();
        tsk.execute();
    }

    private class CountingTask extends AsyncTask<Void, Integer, Integer> {

        CountingTask() {}

        @Override
        protected Integer doInBackground(Void... unused) {

            int i = 0;
            while (i < 100) {
                SystemClock.sleep(250);
                i++;

                if (i % 5 == 0) {
                    // update UI with progress every 5%
                    publishProgress(i);
                }
            }
            return i;
        }

        protected void onProgressUpdate(Integer... progress) {
            TextView tv = (TextView) findViewById(R.id.counter);
            tv.setText(progress[0] + "% Complete!");
        }

        protected void onPostExecute(Integer result) {
            TextView tv = (TextView) findViewById(R.id.counter);
            tv.setText("Count Complete! Counted to " + result.toString());
        }
    }
}

There are two ways to start the task. The first, and default, is to simply instantiate the task and call the execute() method. Each task instantiation can be executed only once.

CountingTask tsk = new CountingTask();
tsk.execute();

On devices running at least API Level 11, tasks can be executed in parallel. On devices with multiple cores, this can allow execution to complete faster and, in the process, potentially increase your application performance and smoothness. If you modify the previous code to take an identifier for what TextView to update with the counter, you can execute several in parallel. Each task still has to be instantiated separately.

CountingTask tsk = new CountingTask();
tsk.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, id1);
CountingTask tsk2 = new CountingTask();
tsk2.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, id2);


Image Warning

On API Level 11, the execute() method behaves as if it were using a thread pool. If you do not want parallel execute, call executeOnExecute(AsyncTask.SERIAL_EXECUTOR). Although the documentation lists the plan to return execute to serial behavior after API Level 11, you can only confirm that the behavior is serial on API Level 14.


Working with the Thread Class

If you need to control a thread yourself, use the Thread class (java.lang.Thread). Porting existing code might be simpler using the Thread class directly, instead of AsyncTask. The Activity class that owns the thread is responsible for managing the lifecycle of the thread. Generally speaking, the Activity includes a member variable of type Handler. Then, when the Thread is instantiated and started, the post() method of the Handler is used to communicate with the main UI thread. You can also communicate to the main UI thread using the runOnUiThread() method of the Activity class and the post() and postDelayed() methods of the View class. For example, here is a simple Activity class that performs a similar operation to the AsyncTask example shown earlier in this chapter.

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

        final TextView tv = (TextView) findViewById(R.id.counter);
        new Thread(new Runnable() {
            public void run() {

                int i = 0;

                while (i < 100) {
                    SystemClock.sleep(250);
                    i++;

                    final int curCount = i;
                    if (curCount % 5 == 0) {
                        // update UI with progress every 5%
                        tv.post(new Runnable() {
                            public void run() {
                                tv.setText(curCount + "% Complete!");
                            }
                        });
                    }
                }

                tv.post(new Runnable() {
                    public void run() {
                        tv.setText("Count Complete!");
                    }
                });
            }
        }).start();
    }
}

Here we create a new Thread object on the fly in the onCreate() method of the Activity class. Again, we count to 100 using the post() method to update the TextView with our progress in a thread-safe manner.

Working with Loaders

Android 3.0 (API Level 11) introduced the concept of a Loader class, which helps asynchronously load data for an Activity or Fragment from a data source such as a content provider or the network. When configured properly, a Loader also monitors the data source for changes, updating the Activity or Fragment as necessary, which helps avoid unnecessary queries. The most common reason to use a Loader involves pulling data from a content provider. To use a Loader, take the following steps:

1. Use your Activity or Fragment class’s LoaderManager to initialize a Loader.

2. Provide an implementation of the LoaderManager.LoaderCallbacks.

3. The onCreateLoader() method is used to return a new Loader instance, typically a CursorLoader that queries a content provider that the Activity or Fragment wants to display data from.

4. The onLoadFinished() method signals that all data has been loaded and is ready for use. Typically, your screen contains some sort of control, such as a ListView, that leverages the CursorAdapter associated with the CursorLoader, so you want to swap the old and new Cursor objects in the adapter at this time.

5. The onLoaderReset() method is used to signify that that the data is unavailable, and thus, the Cursor used by the adapter is no longer valid. Typically, you need to swap out the Cursor again at this time, because it is no longer valid.

Although the Loader class was added in API Level 11, it is part of the Android Support Package, so it can be used as far back as Android 1.6.


Image Tip

You can find an example of a CursorLoader in Chapter 4, “Building Android Content Providers.” There are also numerous examples in the Android SDK samples.


Understanding StrictMode

Strict mode is a method developers can use to detect operations that should not be performed on the main thread. Beginning in API Level 9, developers can enable strict mode in their own applications to detect when they were, for instance, performing network or disk operations on the main thread. In API Level 11, strict mode was expanded with system-wide settings. These settings disallow some operations on the main thread and instead throw exceptions. To enable strict mode in your own applications to behave like API Level 11 or later, use the following code:

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder()
        .detectAll().penaltyDeath().build();

StrictMode.setThreadPolicy(policy);

If you’re not writing a production application and want to run some quick code without wiring up a full thread, you can disable the crashing and simply flash the screen instead (on API Level 11) or log the mistakes. You can also call permitAll() to skip strict mode entirely. This is not recommended for production applications.

On Android 4.0 and later devices, a Developer options setting screen is available to turn on and off the screen flashing with strict mode; however, it doesn’t detect quite as many mistakes. It can be enlightening to turn it on, though.

Summary

Android applications perform many intensive operations on a regular basis, such as accessing resources on disk, services, content providers, databases, and the network. Other operations that can block the main thread include long processing and calculations, and even simple tasks that are performed on a large set of data. All of these tasks should be moved from the main UI thread of the application using some sort of asynchronous method, whether it uses the Thread class, an AsyncTask implementation, a Loader, or a background service, which we talk about in the next chapter. Developers can use StrictMode to help identify areas of their applications that could be more responsive.

References and More Information

Android Dev Guide: “Designing for Responsiveness”:

http://d.android.com/guide/practices/design/responsiveness.html

Android Dev Guide: “Processes and Threads”:

http://d.android.com/guide/topics/fundamentals/processes-and-threads.html

Android SDK documentation on the Thread class:

http://d.android.com/reference/java/lang/Thread.html

Android SDK documentation on the AsyncTask class:

http://d.android.com/reference/android/os/AsyncTask.html

Android Dev Guide: “Loaders”:

http://d.android.com/guide/topics/fundamentals/loaders.html

Android SDK documentation on the StrictMode class:

http://d.android.com/reference/android/os/StrictMode.html

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

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