http://www.developer.com/ws/android/programming/Handling-Lengthy-Operations-in-Googles-Android-3762056.htm
Mobile devices are very often resource-limited. That means they usually do not have the CPU power, vast memory, or storage space commonly seen in modern computers. However, sometimes complicated operations are still required to perform many features currently used on these devices. Android engineers lay out some important rules in the design philosophy; you will focus on the part about how to make sure your software is responsive enough without interfering with the main thread and inadvertently causing the system to pop up an ANR (Application Not Responding) dialog. Furthermore, a message handling mechanism is introduced to post back the result and update the view in the main thread—the device screen users operate on most of the time. Because you are going to implement complicated operations in child threads, the usual concurrent class used in the Java threading package java.lang.Thread is used. The calling sequence and life cycle of a thread is identical to the traditional Java concept. For convenience reasons, a progress dialog is utilized to keep track of the progress. One thing to note is that Android's progress range is between 0 and 10000. It is from the android.app.ProgressDialog package. Classes inside the android.os.Message package are used to define messages to be put in the MessageQueue for scheduled execution of a Runnable object. A Message must be processed by a Handler in the MessageQueue from the android.os.Handler package. A Handler is bound to the thread/message queue that creates it. Messages will be processed in the order they come out of the message queue. There are two main uses for a Handler: to schedule messages and runnables to be executed and to enqueue an action to be performed on a different thread. The screen shot illustrated in Figure 1 displays the typical ANR dialog automatically popped up by the Android platform. When and why does it happen? In Android, Activity Manager and Window Manager system services monitor the application responsiveness and issue the ANR dialog if the main view detects no response to an input event over five seconds or an intent receiver has not finished executing over ten seconds. This is the main reason behind this article because this inadvertent and unpleasant ANR dialog not only symbolizes poor software design, but also rubs users the wrong way. Figure 1: Application Not Responding (ANR) dialog Before you start working on your solution in an example, there are some notes from Android experts you should pay attention to. To stress the importance of designing an Android application, the Android web site highlights some recommendations I think are very beneficial to all developers. After all, learning how to do things the right way from the very beginning cuts down significant development and maintenance time and effort. Besides, it does bring along the advantages of enhancing users' experience. For more details about design philosophy, please see http://code.google.com/android/toolbox/philosophy.html. There are three key characteristics for an excellent user experience: fast, responsive, and seamless. You implement the child thread in the setRandomPointsInChildThread function. Basically, a new thread will be created and executed. Within the child thread, a function to emulate the lengthy operations is provided in setRandomPoints, which does nothing but generate a series of random points and delay the process by pausing 50 milliseconds between points. After all the points have been prepared, it will send a message within a unique ID, GUIUPDATEIDENTIFIER, defined by you. A deterministic progress dialog is initiated. The screen shot is illustrated in Figure 2. Figure 2: Deterministic progress dialog First, each message should be associated with an ID that can be randomly assigned by you, but you need to guarantee its uniqueness among all the messages. In the example, you use GUIUPDATEIDENTIFIER as your message ID. Because the child threads cannot modify the view in the main thread directly, we need to update the main view myview with myview.invalidate() when the message handler receives the message ID from a child thread. The example result is illustrated in Figure 3. Figure 3: Final updated result In addition to what I described earlier about the creations of Thread, Message, and Handler, you construct the content view from MyView by dynamically instantiating it in Activity's OnCreate instead of configuring the resources through an XML file. You also set a key event handler to wait for the user's input by hitting the middle button of the direction pad. That completes the example. The article mainly deals with two issues: You also pass along the recommendations from Android designers to build software applications that will enhance user experiences. With these in mind, hopefully when you come up with your own creations next time, not only development time and efforts are better accounted for, but users will also like the products better. It is suggested you find more details about design philosophy at http://code.google.com/android/toolbox/philosophy.html. Chunyen Liu has been with the engineering department at a world's leading GPS company for a while. Some of his applications were among winners at programming contests administered by SUN, ACM, and IBM. He also had co-authored U.S. patents and written articles for various publishers. He holds advanced degrees in computer science and operates a hobby site called The J Maker. On the non-technical side, he is a tournament-rated table tennis player, certified umpire, and certified coach of USA Table Tennis.
Handling Lengthy Operations in Google's Android
July 30, 2008
What Google APIs Are Used?
ANR—Application Not Responding

Android's Suggestions About Writing Efficient and Responsive Code
Performing Lengthy Operations in Child Threads
void setRandomPoints(boolean modeThreaded) {
int delay_in_msecs = 50;
int npoints = 120;
int r = 0, g = 0, b = 0;
Random rand = new Random();
if (!modeThreaded)
mypd = ProgressDialog.show(TutorialOnANR.this,
"Lengthy Calculations",
"Please wait...",
false);
mypoint = null;
mypoint = new MyPoint[npoints];
for (int i = 0; i < npoints; i++) {
mypoint[i] = new MyPoint();
mypoint[i].x = rand.nextFloat() * 320;
mypoint[i].y = rand.nextFloat() * 240;
r = (int)(rand.nextFloat() * 155) + 100;
g = (int)(rand.nextFloat() * 155) + 100;
b = (int)(rand.nextFloat() * 155) + 100;
mypoint[i].c = 0xff000000 | (r << 16) | (g << 8) | b;
mypoint[i].sz = (int)(rand.nextFloat() * 10) + 3
// Update the progress dialog
mypd.setProgress(10000 * i / npoints);
// Emulate the lengthy operations with an intentional
// delay here
try {
Thread.sleep(delay_in_msecs);
} catch (InterruptedException e) {
}
}
// Dismiss the progress dialog
mypd.dismiss();
}
// Do lengthy operations in a child thread
private void setRandomPointsInChildThread() {
// Start a progress dialog here
mypd = ProgressDialog.show(TutorialOnANR.this,
"Lengthy Calculations",
"Please wait...",
false);
Thread t = new Thread() {
public void run() {
setRandomPoints(true);
// Send a message to the handler
Message message = new Message();
message.what = TutorialOnANR.GUIUPDATEIDENTIFIER;
TutorialOnANR.this.myGUIUpdateHandler.
sendMessage(message);
}
};
t.start();
}

Using Message Handlers to Post Back Results
// Set up a random unique ID for message handler
protected static final int GUIUPDATEIDENTIFIER = 12345;
// Set up the message handler
Handler myGUIUpdateHandler = new Handler() {
// @Override
public void handleMessage(Message msg) {
switch (msg.what) {
case TutorialOnANR.GUIUPDATEIDENTIFIER:
myview.invalidate();
break;
default:
break;
}
super.handleMessage(msg);
}
};
[\p>
A Working Example
// Set up the main view and random points
MyView myview = null;
MyPoint[] mypoint = null;
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
myview = new MyView(this);
setContentView(this.myview);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
// Uncomment the following 2 lines to test the case of
// no child thread and no message handling
// setRandomPoints(false);
// myview.invalidate();
// Set points with a child thread and message handling
setRandomPointsInChildThread();
return (true);
}
return super.onKeyDown(keyCode, event);
}
// Define the class for main view
public class MyView extends View {
private Paint mPaint = new Paint();
public MyView(Context context) {
super(context);
mPaint.setAntiAlias(true);
mPaint.setARGB(255, 255, 255, 255);
mPaint.setStrokeWidth(1);
}
@Override protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
if (mypoint != null) {
for (int i = 0; i < mypoint.length; i++) {
mPaint.setColor(mypoint[i].c);
canvas.drawCircle(mypoint[i].x, mypoint[i].y,
mypoint[i].sz, mPaint);
}
}
}
}
// Define the class for our random point
class MyPoint {
float x;
float y;
int c;
float sz;
}
Conclusion
References
About the Author