Home | History | Annotate | Download | only in articles
      1 page.title=Painless Threading
      2 parent.title=Articles
      3 parent.link=../browser.html?tag=article
      4 @jd:body
      5 
      6 <p>This article discusses the threading model used by Android applications and how applications can ensure best UI performance by spawning worker threads to handle long-running operations, rather than handling them in the main thread. The article also explains the API that your application can use to interact with Android UI toolkit components running on the main thread and spawn managed worker threads.  </p>
      7 
      8 <h3>The UI thread</h3>
      9 
     10 <p>When an application is launched, the system creates a thread called
     11 "main" for the application. The main thread, also called the <em>UI
     12 thread</em>, is very important because it is in charge of dispatching the
     13 events to the appropriate widgets, including drawing events.
     14 It is also the thread where your application interacts with running 
     15 components of the Android UI toolkit. </p>
     16 
     17 <p>For instance, if you touch the a button on screen, the UI thread dispatches
     18 the touch event to the widget, which in turn sets its pressed state and
     19 posts an invalidate request to the event queue. The UI thread dequeues
     20 the request and notifies the widget to redraw itself.</p>
     21 
     22 <p>This single-thread model can yield poor performance unless your application 
     23 is implemented properly. Specifically, if everything is happening in a single 
     24 thread, performing long operations such as network access or database
     25 queries on the UI thread will block the whole user interface. No event
     26 can be dispatched, including drawing events, while the long operation
     27 is underway. From the user's perspective, the application appears hung.
     28 Even worse, if the UI thread is blocked for more than a few seconds
     29 (about 5 seconds currently) the user is presented with the infamous "<a href="http://developer.android.com/guide/practices/design/responsiveness.html">application not responding</a>" (ANR) dialog.</p>
     30 
     31 <p>If you want to see how bad this can look, write a simple application 
     32 with a button that invokes <code>Thread.sleep(2000)</code> in its 
     33 <a href="http://developer.android.com/reference/android/view/View.OnClickListener.html">OnClickListener</a>.
     34 The button will remain in its pressed state for about 2 seconds before
     35 going back to its normal state. When this happens, it is very easy for
     36 the user to <em>perceive</em> the application as slow.</p>
     37 
     38 <p>To summarize, it's vital to the responsiveness of your application's UI to
     39 keep the UI thread unblocked. If you have long operations to perform, you should
     40 make sure to do them in extra threads (<em>background</em> or <em>worker</em>
     41 threads). </p>
     42 
     43 <p>Here's an example of a click listener downloading an image over the 
     44 network and displaying it in an <a href="http://developer.android.com/reference/android/widget/ImageView.html">ImageView</a>:</p>
     45 
     46 <pre class="prettyprint">public void onClick(View v) {
     47   new Thread(new Runnable() {
     48     public void run() {
     49       Bitmap b = loadImageFromNetwork();
     50       mImageView.setImageBitmap(b);
     51     }
     52   }).start();
     53 }</pre>
     54 
     55 <p>At first, this code seems to be a good solution to your problem, as it does
     56 not block the UI thread. Unfortunately, it violates the single-threaded model
     57 for the UI: the Android UI toolkit is <em>not thread-safe</em> and must always
     58 be manipulated on the UI thread. In this piece of code above, the
     59 <code>ImageView</code> is manipulated on a worker thread, which can cause really
     60 weird problems. Tracking down and fixing such bugs can be difficult and
     61 time-consuming.</p>
     62 
     63 <p>Android offers several ways to access the UI
     64 thread from other threads. You may already be familiar with some of
     65 them but here is a comprehensive list:</p>
     66 
     67 <ul>
     68 <li>{@link android.app.Activity#runOnUiThread(java.lang.Runnable) Activity.runOnUiThread(Runnable)}</li>
     69 <li>{@link android.view.View#post(java.lang.Runnable) View.post(Runnable)}</li>
     70 <li>{@link android.view.View#postDelayed(java.lang.Runnable, long) View.postDelayed(Runnable, long)}</li>
     71 <li>{@link android.os.Handler}</li>
     72 </ul>
     73 
     74 <p>You can use any of these classes and methods to correct the previous code example:</p>
     75 
     76 <pre class="prettyprint">public void onClick(View v) {
     77   new Thread(new Runnable() {
     78     public void run() {
     79       final Bitmap b = loadImageFromNetwork();
     80       mImageView.post(new Runnable() {
     81         public void run() {
     82           mImageView.setImageBitmap(b);
     83         }
     84       });
     85     }
     86   }).start();
     87 }</pre>
     88 
     89 <p>Unfortunately,
     90 these classes and methods could also tend to make your code more complicated
     91 and more difficult to read. It becomes even worse when your implement
     92 complex operations that require frequent UI updates. </p>
     93 
     94 <p>To remedy this problem, Android 1.5 and later platforms offer a utility class
     95 called {@link android.os.AsyncTask}, that simplifies the creation of
     96 long-running tasks that need to communicate with the user interface.</p>
     97 
     98 <p>An <code>AsyncTask</code> equivalent is also available for applications that
     99 will run on Android 1.0 and 1.1. The name of the class is <a
    100 href="http://code.google.com/p/shelves/source/browse/trunk/Shelves/src/org/
    101 curiouscreature/android/shelves/util/UserTask.java">UserTask</a>. It offers the
    102 exact same API and all you have to do is copy its source code in your
    103 application.</p>
    104 
    105 <p>The goal of <code>AsyncTask</code> is to take care of thread management for
    106 you. Our previous example can easily be rewritten with
    107 <code>AsyncTask</code>:</p>
    108 
    109 <pre class="prettyprint">public void onClick(View v) {
    110   new DownloadImageTask().execute("http://example.com/image.png");
    111 }
    112 
    113 private class DownloadImageTask extends AsyncTask&lt;String, Void, Bitmap&gt; {
    114      protected Bitmap doInBackground(String... urls) {
    115          return loadImageFromNetwork(urls[0]);
    116      }
    117 
    118      protected void onPostExecute(Bitmap result) {
    119          mImageView.setImageBitmap(result);
    120      }
    121  }</pre>
    122 
    123 <p>As you can see, <code>AsyncTask</code> <em>must</em> be used by subclassing
    124 it. It is also very important to remember that an <code>AsyncTask</code>
    125 instance has to be created on the UI thread and can be executed only once. You
    126 can read the <a
    127 href="http://developer.android.com/reference/android/os/AsyncTask.html">
    128 AsyncTask documentation</a> for a full understanding on how to use this class,
    129 but here is a quick overview of how it works:</p>
    130 
    131 <ul>
    132 <li>You can specify the type, using generics, of the parameters, the progress values and the final value of the task</li>
    133 <li>The method <a href="http://developer.android.com/reference/android/os/AsyncTask.html#doInBackground%28Params...%29">doInBackground()</a> executes automatically on a worker thread</li>
    134 <li><a href="http://developer.android.com/reference/android/os/AsyncTask.html#onPreExecute%28%29">onPreExecute()</a>, <a href="http://developer.android.com/reference/android/os/AsyncTask.html#onPostExecute%28Result%29">onPostExecute()</a> and <a href="http://developer.android.com/reference/android/os/AsyncTask.html#onProgressUpdate%28Progress...%29">onProgressUpdate()</a> are all invoked on the UI thread</li>
    135 <li>The value returned by <a href="http://developer.android.com/reference/android/os/AsyncTask.html#doInBackground%28Params...%29">doInBackground()</a> is sent to <a href="http://developer.android.com/reference/android/os/AsyncTask.html#onPostExecute%28Result%29">onPostExecute()</a></li>
    136 <li>You can call <a href="http://developer.android.com/reference/android/os/AsyncTask.html#publishProgress%28Progress...%29">publishProgress()</a> at anytime in <a href="http://developer.android.com/reference/android/os/AsyncTask.html#doInBackground%28Params...%29">doInBackground()</a> to execute <a href="http://developer.android.com/reference/android/os/AsyncTask.html#onProgressUpdate%28Progress...%29">onProgressUpdate()</a> on the UI thread</li><li>You can cancel the task at any time, from any thread</li>
    137 </ul>
    138 
    139 <p>In addition to the official documentation, you can read several complex examples in the source code of Shelves (<a href="http://code.google.com/p/shelves/source/browse/trunk/Shelves/src/org/curiouscreature/android/shelves/activity/ShelvesActivity.java">ShelvesActivity.java</a> and <a href="http://code.google.com/p/shelves/source/browse/trunk/Shelves/src/org/curiouscreature/android/shelves/activity/AddBookActivity.java">AddBookActivity.java</a>) and Photostream (<a href="http://code.google.com/p/apps-for-android/source/browse/trunk/Photostream/src/com/google/android/photostream/LoginActivity.java">LoginActivity.java</a>, <a href="http://code.google.com/p/apps-for-android/source/browse/trunk/Photostream/src/com/google/android/photostream/PhotostreamActivity.java">PhotostreamActivity.java</a> and <a href="http://code.google.com/p/apps-for-android/source/browse/trunk/Photostream/src/com/google/android/photostream/ViewPhotoActivity.java">ViewPhotoActivity.java</a>). We highly recommend reading the source code of <a href="http://code.google.com/p/shelves/">Shelves</a> to see how to persist tasks across configuration changes and how to cancel them properly when the activity is destroyed.</p>
    140 
    141 <p>Regardless of whether or not you use <a href="http://developer.android.com/reference/android/os/AsyncTask.html">AsyncTask</a>,
    142 always remember these two rules about the single thread model: </p>
    143 
    144 <ol>
    145 <li>Do not block the UI thread, and 
    146 <li>Make sure that you access the Android UI toolkit <em>only</em> on the UI thread. 
    147 </ol>
    148 
    149 <p><code>AsyncTask</code> just makes it easier to do both of these things.</p>
    150