Thread,Looper,Handler,Message,MessageQueue之間的關係

多線程與異步

Main Thread & UI Thread

當程序啓動的時候Android會自動創建一個進程和一個線程,這個線程負責界面更新,收集系統事件和用戶的操作事件等並分配給對應的組件,所以這個線程非常重要 被稱爲主線程,因爲所的和UI有關的操作都是在這個線程當中進行的所以也被稱作UI線程。所以說默認情況下主線程和UI線程指的是同一個線程。For instance, when the user touches a button on the screen, your app's UI thread dispatches the touch event to the widget, which in turn sets its pressed state and posts an invalidate request to the event queue. The UI thread dequeues the request and notifies the widget that it should redraw itself.

Android的UI系統參考了很多SWT的設計模式。比如,UI線程機制,底層JNI實現 etc.

Android 的UI系統是非線程安全的,意思是說只有創建UI的線程(也就是主線程)纔可以對UI進程操作。如果我們在其它線程中執行了一些操作,而這些操作的結果又需要通過UI展現給用戶,必需把這更新UI的操作轉移到到UI線程中執行。

很多Java起步的程序員對UI線程中的"消息循環"會感覺陌生。其實就是在線程內部有一個死循環一直監聽系統事件(用戶的操作等)並把任務分發給對應的組件(有興趣的可以看看NDK附帶的native-activity的一個sample,在c中的實現方式就是一個while(1)的死循環)。主線程通過Looper實現了消息的循環處理。

The ralationship between Handler,Message,MessageQueue and Looper?

如果一個線程裏邊有一個死循環,那麼這個循環就會一直在死循環裏邊循環,並且這個線程不會過多的cpu資源,那麼這個線程肯定的阻塞的。如果線程只是一直循環沒有什麼意義,實際情況通常需要線程根據不同的條件運行不同的方法。我們通常的作法可能會加一個switch開關,根據條件的不同去調用不同的方法。

線程間通信(最常見的就是,worker線程需要更新UI),這個時候實際是包含兩個內容:一,數據的傳遞;二,方法的傳遞; Android中的Message用於數據傳遞,而Handler就是方法傳遞(其實是方法的執行者,Handler會把這個方法放到Handler所在的線程當中去執行);MessageQueue是Message的容器和Java中的Queue一樣都是容器,只不過Message Queue是專門用於裝載Message的容器。Looper則是一個線程中的死循環用於管理MessageQueue,它負責阻塞讀取MessageQueue中的信息(如果MessageQueue中沒有信息會一直在那裏),挨個讀取並把這個Message分發給對應的Handler去執行。

通過一個小例子+源碼追蹤下它們之間是怎麼工作的,關係是什麼樣的

public class HandleMessageTrackActivity extends Activity {
final static int SHOW_ALERT = 1025;
Activity mActivity;
TextView displayText;
Handler mHandler
= new Handler(){

@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch(msg.what){
case SHOW_ALERT:
showAlert((String)msg.obj);
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mActivity
= this;
displayText
= new TextView(this);
setContentView(displayText);
displayText.setText(
"Just ignore me");

new Thread(){
@Override
public void run(){
String Greetings
= "Greetings from another Thread";
mHandler.obtainMessage(SHOW_ALERT, Greetings).sendToTarget();
}
}.start();
}

private void showAlert(String content){
AlertDialog.Builder builder
= new Builder(mActivity);
builder.setMessage(content);
builder.create().show();
}
}

首先看一下mHandler.obtainMessage(SHOW_ALERT, Greetings).sendToTarget();會執行什麼。 通過查看Handler的源碼發現執行了下邊的代碼。

public final Message obtainMessage(int what, Object obj){
    return Message.obtain(this, what, obj);
}

接着查看Message中的代碼,

 

public static Message obtain(Handler h, int what, Object obj) {
Message m
= obtain();
m.target
= h;
m.what
= what;
m.obj
= obj;
return m;
}
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (mPoolSync) {
if (mPool != null) {
Message m
= mPool;
mPool
= m.next;
m.next
= null;
return m;
}
}
return new Message();
}

看到這裏我們應該明白爲什麼Google建議我們使用obtainMessage而不是new一個Message.這也是符合了Android中的對象回收機制。

 

public void sendToTarget() {

target.sendMessage(
this);//targe就是一個Handler

}
sendMessage()會調用下面的sendMessageAtTime()把Message加入MessagQueue;

public boolean sendMessageAtTime(Message msg, long uptimeMillis){
boolean sent = false;
MessageQueue queue
= mQueue;
if (queue != null) {
msg.target
= this;
sent
= queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e
= new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w(
"Looper", e.getMessage(), e);
}
return sent;
}

到此我們的Message對象被加入到了Handler所在線程(也就是主線程)中的MessageQueue這個存儲和管理Message的容器當中。下一步就該由Looper接手一個一個的取出Message對象處理了。Looper最主要的方法就是loop()方法

 

public static final void loop() {
Looper me
= myLooper();
MessageQueue queue
= me.mQueue;
while (true) {//死循環
Message msg = queue.next(); // might block
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 of a msg is a Handler
if (me.mLogging!= null) me.mLogging.println(
" Finished to " + msg.target + " "
+ msg.callback);//msg.callback is a Runnable
msg.recycle();
}
}
}

Looper會一直阻塞讀取MessagQueue中的Message對象(Message msg = queue.next()),當讀取到一個Message時就會調用msg.target.dispatchMessage(msg)把這個Message分發給對應的Handler去處理,等Handler把任務處理完了再接着讀取下一個Message對象並處理。

 

/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
// call the run method of the runnable object I guess
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

最後回來了我們的handlerMessage(msg);這個方法中。如果是用的不是sendMessage是是Hadler.post(Runnable)會調用

private final void handleCallback(Message message) {
message.callback.run();
}
這就是爲什麼在Handler中post的Runnable不會開啓一個新的線程的原因。經過上面的追蹤我們應該能明白不是所有 的線程都可以有Handler去執行handlerMessage或者處理Runnalbe的。其必要條件就是這個線程要有一個一直循環的Looper.Looper一直循環肯定要有一個方法可以退出循環。
public void quit() {
Message msg
= Message.obtain();
// NOTE: By enqueueing directly into the message queue, the
// message is left with a null target. This is how we know it is
// a quit message.
mQueue.enqueueMessage(msg, 0);
}

當我們調用msg.sendToTarget()的時候,我們的msg對象應付被壓入MessageQueue的尾部。Looper在MessageQueue的另一端一個一個的讀取信息並處理。根據msg的target屬性把cpu分配給對應的Handler去執行任務,這時Handler會根據msg中的屬性調用msg.handleMsg()或者msg.callback.run();當一個msg中的消息處理完返回之後Looper會把這個msg放入msgPool當中方便下次再重複利用。從而減少對象的創建。Looper再從MessageQueue中讀取下一個信息,如此反覆。。

理解了這些其實就不難想象ANR是如何實現的了。當用戶執行操作(比如點擊了一個按鈕)系統會生成一個Message對象,把用戶操作的信息寫入Message對象,並把這個Message對象壓入MessageQueue隊列的尾部。系統過一段時間(一般是五秒)後會再來檢查,剛剛放入的信息是不是已經被處理了,如果信息還在隊列中就表明。處理前面信息的過程當中發生的阻塞,用戶的操作沒有及時得到響應。系統彈出ANR對話框。

作個總結:

  因爲UI線程需要保持一直運行的狀態,所以要有一個循環保持這個線程不會死掉,但這個線程又必需阻塞,以減少cpu的消耗。android中的這個循環就是通過Looper實現的。有了這個 Looper,Looper就佔據了整個線程,導致所有的方法想在些線程中運行就必需通過這個Looper,所以要有個方法可以進入這個Looper的內部。MessageQueue就擔當了這個通道 的角色。Message擔當了集合的角色。所有在UI線程中運行的方法都必需通過MessageQueue進入Looper內部,不管 是用戶定義的方法還是系統事件包括onCreate(),onStop(),用戶點擊事件etc..

轉載自:http://www.cnblogs.com/chon/archive/2011/06/28/2092363.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章