Documente Academic
Documente Profesional
Documente Cultură
Chapter
15
155
156
Messages
To send a Message to a Handler, first invoke obtainMessage() to get the Message object out of the pool. There are a few flavors of obtainMessage(), allowing you to create empty Message objects or ones populated with message identifiers and arguments. The more complicated your Handler processing needs to be, the more likely it is you will need to put data into the Message to help the Handler distinguish different events. Then you send the Message to the Handler via its message queue, using one of the sendMessage...() family of methods, such as the following: sendMessage(): Puts the message on the queue immediately. sendMessageAtFrontOfQueue(): Puts the message on the queue immediately, placing it at the front of the message queue, so your message takes priority over all others. sendMessageAtTime(): Puts the message on the queue at the stated time, expressed in the form of milliseconds based on system uptime (SystemClock.uptimeMillis()). sendMessageDelayed(): Puts the message on the queue after a delay, expressed in milliseconds. To process these messages, your Handler needs to implement handleMessage(), which will be called with each message that appears on the message queue. There, the handler can update the UI as needed. However, it should still do that work quickly, as other UI work is suspended until the Handler is finished. For example, lets create a ProgressBar and update it via a Handler. Here is the layout from the Threads/Handler sample project:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <ProgressBar android:id="@+id/progress" style="?android:attr/progressBarStyleHorizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout>
The ProgressBar, in addition to setting the width and height as normal, also employs the style property. This particular style indicates the ProgressBar should be drawn as the traditional horizontal bar showing the amount of work that has been completed. And here is the Java:
package com.commonsware.android.threads; import android.app.Activity; import android.os.Bundle; import android.os.Handler;
157
import android.os.Message; import android.widget.ProgressBar; public class HandlerDemo extends Activity { ProgressBar bar; Handler handler=new Handler() { @Override public void handleMessage(Message msg) { bar.incrementProgressBy(5); } }; boolean isRunning=false; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(R.layout.main); bar=(ProgressBar)findViewById(R.id.progress); } public void onStart() { super.onStart(); bar.setProgress(0); Thread background=new Thread(new Runnable() { public void run() { try { for (int i=0;i<20 && isRunning;i++) { Thread.sleep(1000); handler.sendMessage(handler.obtainMessage()); } } catch (Throwable t) { // just end the background thread } } }); isRunning=true; background.start(); } public void onStop() { super.onStop(); isRunning=false; } }
As part of constructing the Activity, we create an instance of Handler, with our implementation of handleMessage(). Basically, for any message received, we update the ProgressBar by 5 points, and then exit the message handler. In onStart(), we set up a background thread. In a real system, this thread would do something meaningful. Here, we just sleep 1 second, post a Message to the Handler, and repeat for a total of 20 passes. This, combined with the 5-point increase in the
158
ProgressBar position, will march the bar clear across the screen, as the default maximum value for ProgressBar is 100. You can adjust that maximum via setMax(). For example, you might set the maximum to be the number of database rows you are processing, and update once per row. Note that we then leave onStart(). This is crucial. The onStart() method is invoked on the activity UI thread, so it can update widgets and such. However, that means we need to get out of onStart(), both to let the Handler get its work done and also so Android does not think our activity is stuck. The resulting activity is simply a horizontal progress bar, as shown in Figure 151.
Note, though, that while ProgressBar samples like this one show your code arranging to update the progress on the UI thread, for this specific widget, that is not necessary. At least as of Android 1.5, ProgressBar is now UI thread-safe, in that you can update it from any thread, and it will handle the details of performing the actual UI update on the UI thread.
Runnables
If you would rather not fuss with Message objects, you can also pass Runnable objects to the Handler, which will run those Runnable objects on the activity UI thread. Handler offers a set of post...() methods for passing Runnable objects in for eventual processing.
159
Running in Place
Just as Handler supports post() and postDelayed() to add Runnable objects to the event queue, you can use those same methods on View. This slightly simplifies your code, in that you can then skip the Handler object. However, you lose a bit of flexibility. Also, the Handler has been in the Android toolkit longer, and it may be more tested.
Asyncing Feeling
Android 1.5 introduced a new way of thinking about background operations: AsyncTask. In one (reasonably) convenient class, Android will handle all of the chores of doing work on the UI thread versus on a background thread. Moreover, Android itself allocates and removes that background thread. And it maintains a small work queue, further accentuating the fire-and-forget feel to AsyncTask.
The Theory
There is a saying, popular in marketing circles: When a man buys a 1/4-inch drill bit at a hardware store, he does not want a 1/4-inch drill bithe wants 1/4-inch holes. Hardware stores cannot sell holes, so they sell the next-best thing: devices (drills and drill bits) that make creating holes easy. Similarly, Android developers who have struggled with background thread management do not strictly want background threads. Rather, they want work to be done off the UI thread, so users are not stuck waiting and activities do not get the dreaded application not responding (ANR) error. And while Android cannot magically cause work to not consume UI thread time, it can offer things that make such background operations easier and more transparent. AsyncTask is one such example. To use AsyncTask, you must: Create a subclass of AsyncTask, commonly as a private inner class of something that uses the task (e.g., an activity).
160
Override one or more AsyncTask methods to accomplish the background work, plus whatever work associated with the task that needs to be done on the UI thread (e.g., update progress). When needed, create an instance of the AsyncTask subclass and call execute() to have it begin doing its work. What you do not need to do is: Create your own background thread. Terminate that background thread at an appropriate time. Call all sorts of methods to arrange for bits of processing to be done on the UI thread.