Android defines two classes in the android.os package that will often be the cornerstones of the interthread communication in your multithreaded applications:
While creating an AsyncTask
object hides the Handler and Looper details from you, in some cases you need to use handlers and loopers explicitly, for example when you need to post a Runnable
to a thread other than the main thread.
Listing 5–2 gave you a glimpse of how the Handler and Looper work together: you use a Handler
object to post a Runnable
in a Looper
's message queue. Your application's main thread already has a message queue, so you don't have to create one explicitly. However, the threads you create do not come automatically with a message queue and message loop, so you would have to create one yourself if needed. Listing 5–7 shows how you can create a thread with a Looper.
public classMyThreadextendsThread{
private static final String TAG = “MyThread”;
privateHandler mHandler;
public MyThread(String name) {
super(name);
}
public Handler getHandler() {
return mHandler;
}
@Override
publicvoid run(){
Looper.prepare(); // binds a looper to this thread
mHandler =newHandler(){
@Override
publicvoid handleMessage(Message msg){
switch (msg.what) {
// process messages here
}
}
};
// the handler is bound to this thread's looper
Looper.loop(); // don't forget to call loop() to start the message loop
// loop() won't return until the loop is stopped (e.g., when Looper.quit() is
called)
}
}
NOTE: The handler object is created in the run()
method as it needs to be bound to a specific looper, which is also created in run()
when Looper.prepare()
is called. Consequently, calling getHandler()
before the thread is spawned will return null.
Once the thread is running, you can post Runnable
objects or send messages to its message queue, as shown in Listing 5–8.
MyThread thread = new MyThread(“looper thread”);
thread.start();
// later...
Handler handler = thread.getHandler();
// careful: this could return null if the handler is not initialized yet!
// to post a runnable
handler.post(new Runnable() {
public void run() {
Log.i(TAG, "Where am I? " + Thread.currentThread().getName());
}
});
// to send a message
int what = 0; // define your own values
int arg1 = 1;
int arg2 = 2;
Message msg = Message.obtain(handler, what, arg1, arg2);
handler.sendMessage(msg);
// another message...
what = 1;
msg = Message.obtain(handler, what, new Long(Thread.currentThread().getId()));
handler.sendMessageAtFrontOfQueue(msg);
TIP: Use one of the Message.obtain()
or Handler.obtainMessage()
APIs to get a Message
object, as they return a Message
object from the global message pool, which is more efficient than allocating a new instance every single time a message is needed. These APIs also make it simpler to set the various fields of the message.
Android provides an easier way to work with looper threads with the HandlerThread
class, which also makes it easier to avoid the potential race condition mentioned in Listing 5–8, where getHandler()
may still return null even after the thread has been started. Listing 5–9 shows how to use the HandlerThread
class.
public class MyHandlerThread extends HandlerThread {
private static final String TAG = "MyHandlerThread";
private Handler mHandler;
public MyHandlerThread(String name) {
super(name);
}
public Handler getHandler() {
return mHandler;
}
@Override
public void start() {
super.start();
Looper looper = getLooper(); // will block until thread's Looper object is
initialized
mHandler = new Handler(looper) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
// process messages here
}
}
};
}
}
Since the handler is created in the start()
method instead of in the run()
method, it will be available (via a call to getHandler()
) after the start()
method returns without any race condition.
18.223.209.180