1. Threading and Asynchronous Processing

Offloading intensive operations provides a smoother, more stable experience to the user. An application that is not responsive within 100 to 200 milliseconds creates the feeling of a slow application. Processing off the main UI thread may help speed up your applications. 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 will 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, so time-intensive operations such as networking should not block the main UI thread. Some common blocking operations include:

Image Any lengthy or complex calculation or operation

Image Querying a data set of indeterminate size

Image Parsing a data set

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

Image Iterating over a data structure of indeterminate size

Image Accessing network resources

Image Accessing location-based services

Image Accessing a content provider interface

Image Accessing a local database

Image Accessing a local file

Image 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 an application is not responding in a reasonable time and shuts that application down. Typically, these events happen when an application takes longer than 5 seconds to respond or complete a task or when an intent broadcast takes longer than 10 seconds to complete.

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. To ensure that your asynchronous code operates off the main UI thread properly, make sure to test your application on real 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:

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

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

Image 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 this application is provided for download on the book’s website.


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 each new version of 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:

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

Image The doInBackground() method runs in the background on a separate thread and is where all the real work is done.

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

Image 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 or to update the UI in other ways.

Image 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’s performance and smoothness. If you modify the previous code to take an identifier for 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 to parallel execute, call executeOnExecutor(AsyncTask.SERIAL_EXECUTOR). Although the documentation lists the plan to return execute to serial behavior after API Level 11, you can confirm that the behavior is serial only 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 an operation similar 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.


Image Tip

Today’s Android devices contain multiple processor cores. To take advantage of running your application code across multiple processors to execute multiple operations in parallel, you could define a ThreadPoolExecutor to manage a pool of Thread objects to speed up your application’s code even further. To determine the number of available processors on a device, use the following code:

Click here to view code image

Runtime.getRuntime().availableProcessors();


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 from which the Activity or Fragment wants to display data.

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 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.

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

StrictMode 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 StrictMode in their own applications to detect when they are, for instance, performing network or disk operations on the main thread. In API Level 11, StrictMode was expanded with system-wide settings. These settings disallow some operations on the main thread and instead throw exceptions. To enable StrictMode 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 StrictMode entirely. This is not recommended for production applications.

On Android 4.0 and later devices, a Developer options setting screen is available to turn the screen flashing on and off with StrictMode; 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. So that users don’t perceive your applications as being slow, all of these tasks should be moved from the main UI thread of the application using some sort of asynchronous method, whether that be 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.

Quiz Questions

1. True or false: An ANR event means that the Android Notification Record has completed.

2. What AsyncTask method runs on the UI thread before background processing begins?

3. If you do not want parallel execution with your AsyncTask, what method, including parameters, should you use?

4. What class should you use when you want to load data asynchronously for an Activity or Fragment?

5. True or false: StrictCode is a method developers can use to detect operations that should not be performed on the main thread.

Exercises

1. Using the online documentation, create a list of the various things you should do to reinforce responsiveness in your applications.

2. Using the online documentation, explain the “important hierarchy” that Android uses for determining which processes are kept and which are destroyed.

3. Using the online documentation, determine at least one strategy you should use when your application needs to perform operations that take a long time to complete, and write a simple application demonstrating your knowledge of the strategy.

References and More Information

Android Training: “Keeping Your App Responsive”:

http://d.android.com/training/articles/perf-anr.html

Android API Guides: “Processes and Threads”:

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

Android Reference documentation on the Thread class:

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

Android Reference documentation on the ThreadPoolExecutor class:

http://d.android.com/reference/java/util/concurrent/ThreadPoolExecutor.html

Android Reference documentation on the AsyncTask class:

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

Android API Guides: “Loaders”:

http://d.android.com/guide/components/loaders.html

Android Reference 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.227.102.34