Windows編程的朋友可能知道Windows程式是訊息驅動的,並且有全域的訊息迴圈系統。而Android應用程式也是訊息驅動的,按道理來說也應該提供訊息迴圈機制。Android通過Looper、Handler來實現訊息迴圈機制,Android訊息迴圈是針對線程的(每個線程都可以有自己的訊息佇列和訊息迴圈)。
在 Android 系統 ,這些工作由由由Looper 及 Handler 來完成。
先分析Looper類:
主要提供負責線程的訊息迴圈和訊息佇列
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類的準備函數:
private static final ThreadLocal sThreadLocal = new ThreadLocal();
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
這裡利用 ThreadLocal 線程局部儲存變數將將Looper與調用線程關聯起來。
而訊息處理流程如何呢?
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static final void loop() {
Looper me = myLooper(); // 線程中儲存的Looper對象
MessageQueue queue = me.mQueue; // Looper類中的訊息佇列
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); // 利用 Target 註冊的方法處理訊息
if (me.mLogging!= null) me.mLogging.println(
"<<<<< Finished to " + msg.target + " "
+ msg.callback);
msg.recycle();
}
}
}
可以通過Loop.myLooper()得到當前線程的Looper對象,通過Loop.getMainLooper()可以獲得當前進程的主線程的Looper對象。
在android系統,UI線程就是訊息的主線程,在 ActivityThread.java 中建立:
public static final void main(String[] args) {
Looper.prepareMainLooper();
Looper.loop(); //訊息迴圈處理
}
-->
public static final void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
if (Process.supportsProcesses()) {
myLooper().mQueue.mQuitAllowed = false;
}
}
private synchronized static void setMainLooper(Looper looper) {
mMainLooper = looper;
}
再來分析 Handler 類:
主要提供負責將訊息加入到特定的Looper訊息佇列中,並分發和處理訪訊息佇列中的訊息,構造Handler的時候可以指定一個Looper對象,如果不指定則利用當前線程的Looper建立。www.2cto.com
請看如下代碼即可明白:
public Handler(Looper looper, Callback callback) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
}
這裡也同時告訴我們將訊息放入到主線程的訊息佇列中,只需要建立Hanlde對象時以主線程的Looper建立即可。則sendMessage及handleMessage都會在主線程中進行處理。
再看如下變數:
final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
這裡也有 隊列,Looper對象及回呼函數類,通過 Handler 不同建構函式完成相應的操作。
簡化使用隊列及訊息傳遞的複雜性,提供方便的調用方法。其中最重要的函數是:
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg); // 1、利用 Calback 函數處理訊息
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) { // 2、利用mCallback處理訊息
return;
}
}
handleMessage(msg); // 3、利用子類處理訊息,這裡最常用的方法,直接重載handleMessage函數
}
}
那麼這幾者的關係是怎麼樣的呢?
一個Activity中可以建立多個背景工作執行緒或者其他的組件,如果這些線程或者組件把他們的訊息放入Activity的主線程訊息佇列,那麼該訊息就會在主線程中處理了。
還有一個問題就是Looper和Handler的同步關係如何處理,在android由HandlerThread類進行解決了。
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait(); // 如果新線程還未建立Looper對象,則等待
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
新線程建立運行run函數建立Looper對象:
public void run() {
mTid = Process.myTid();
Looper.prepare();// 這裡會建立程的Looper對象
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll(); // ok,建立好了則通知,最後調用Looper.loop()進入訊息迴圈
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
這裡利用 notifyAll / wait 輕檢解決此問題了,所以多多使用用HanlderThread類完成多線程同步問題吧。
摘自 andyhuabing的專欄