android2010-01-18 15:06:23閱讀484評論0 字號:大中小 訂閱
轉(zhuǎn)載時請注明出處和作者聯(lián)系方式
文章出處:http://www.limodev.cn/blog
作者聯(lián)系方式:李先靜 <xianjimli at hotmail dot com>
前幾天和一位同事討論Android中Handler和Thread,其中一個問題是:創(chuàng)建Handler時會不會創(chuàng)建Thread?
我對JAVA編程不熟,但直覺告訴我不會:我認為Handler只是用來輔助實現(xiàn)異步操作的東西。當時我拿了GTK+中的idle來做對比,sendMessage就相當于加一個idle函數(shù),系統(tǒng)處理完前面的Message后就會處理這個Message。畢竟沒有看過里面的實現(xiàn)代碼,所以當時并不確信。今天看了下:
MessageQueue
消息隊列MessageQueue是一個以執(zhí)行時間為序的優(yōu)先級隊列:
o 普通消息的執(zhí)行為當前時間,先發(fā)送的前面,后發(fā)送在后面,這是典型的FIFO。
o 最高優(yōu)先級的消息執(zhí)行時間為0,所以直接插在隊列的最前面,通常會立即執(zhí)行。
o 而在將來執(zhí)行的Message相當于timer,執(zhí)行時間為當前時間+delay的時間。
MessageQueue的函數(shù)boolean enqueueMessage(Message msg, long when)用來向隊列中插入消息。
Message和GTK+ idle不同之處在于,Message只是一段數(shù)據(jù),里面說明了要做什么,但不并知道如何做。而idle帶有自己的數(shù)據(jù)和處理函數(shù),它知道如何做。Message放入隊列中后,在處理這些消息時到底要做些什么呢?這就引入了Handler:
Handler
Handler對消息隊列的enqueueMessage做了包裝,這其實并不重要,因為完全可以直接調(diào)用enqueueMessage來實現(xiàn)。重要的Handler把在包裝enqueueMessage的同時,把Message的target設(shè)成了自己,即為Message指定執(zhí)行的行為:
if (queue != null) {
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
}
這樣一來,當前Message被處理的時候就會調(diào)用Handler的dispatchMessage,而這個函數(shù)就會調(diào)用你要實現(xiàn)的虛函數(shù)handleMessage了。經(jīng)過消息隊列轉(zhuǎn)了一圈,還是調(diào)用了你自己的實現(xiàn)函數(shù),但是同步操作變成了異步操作。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
Looper
Message放在消息隊列里了,誰來一個一個的取出來處理呢?這時輪到Looper上場了,它的函數(shù)loop會一直循環(huán)處理隊列中的消息,直到遇上一個沒有target的Message:
public static final void loop() {
Looper me = myLooper();
MessageQueue queue = me.mQueue;
while (true) {
Message msg = queue.next(); // might block
//if (!me.mRun) {
// break;
//}
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
if (me.mLogging!= null) me.mLogging.println(
">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what
);
msg.target.dispatchMessage(msg);
if (me.mLogging!= null) me.mLogging.println(
"<<<<< Finished to " + msg.target + " "
+ msg.callback);
msg.recycle();
}
}
}
由此可見:一個Looper總是和一個MessageQueue關(guān)聯(lián)起來的。
Thread
loop只是一個函數(shù),它也需要別人來執(zhí)行它。由于它一執(zhí)行就會阻塞在那里,所以一定需要一個線程來調(diào)用:
* class LooperThread extends Thread {
* public Handler mHandler;
*
* public void run() {
* Looper.prepare();
*
* mHandler = new Handler() {
* public void handleMessage(Message msg) {
* // process incoming messages here
* }
* };
*
* Looper.loop();
* }
* }
一個Looper也總是和一個Thread關(guān)聯(lián)起來的,不過不一定要創(chuàng)建新線程,可以是主線程的。Handler和Thread不是一一對應的,理論上,在一個LooperThread中,可以有任何多個Handler,每個消息都可以指定不同的Handler,因為每個消息都可以有不同的行為。
在創(chuàng)建Handler時并不會創(chuàng)建Thread,它只是取當前線程的Looper的MessageQueue:
public Handler() {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = null;
}
通過myLooper取出當前線程的Looper:
public static final Looper myLooper() {
return (Looper)sThreadLocal.get();
}
這個Looper是在Looper.prepare里創(chuàng)建的:
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}