這幾天對handler有一些研究,寫出來供大家看看。
一、訊息機制詳解
Android程式中,主要是通過訊息機制來推動整個程式運作的。而訊息機制中,完成主要功能的主要有以下幾個類:
1、Looper
2、Message、MessageQueue
3、handler
這是最基礎的幾個訊息機制類,下面探究一下這幾個類如何完成Android訊息機制的執行。
首先看Message和MessageQueue。Message顧名思義,是訊息機制中訊息的載體,繼承Parcelable序列化介面,可以在程式和元件之間作為訊息的載體傳播訊息。而MessageQueue就是承載message的佇列。關於Message,主要有以下幾點總結:
1、Message中是持有Handler物件,作為Message的target的。這也很好理解,為了傳送訊息給handler嘛~這裡要注意的一點是,Message中持有handler,而handler一般是持有Context的,所以這裡是有可能引發記憶體洩漏的,後面會對這點進行進一步講解。
有了target之後,message就可以呼叫:
還有
public void sendToTarget() {
target.sendMessage(this);
} //傳送訊息到target handler
2、Message中有四個引數:
public int what
public int arg1
public int arg2
public Object obj
what引數是訊息的識別碼,用於給handler判斷不同的Message,採取不同的處理方法,這個不用多說了。
arg1和arg2都是一些攜帶在message中的int訊息,相比setData(bundle)更加輕量級。
obj是message中可以承載的物件,注意要實現Parcelable介面。
3、Message中的recyle方法是用於當message被消費後,對message進行回收的。這個方法也蠻重要的,正是因為message會自己回收自己,才避免了很多記憶體洩漏的問題。
4、接下來看看MessageQuene。MessageQueue是承載Message的佇列,其中最重要的方法是next方法,這個方法的作用是返回佇列中下一個message。
接下來看看Looper。Looper我覺得是訊息機制中最核心的類,起到了推動整個訊息機制執行的作用。
looper中的主要有以下四個方法:
public static prepare();
public static myLooper();
public static loop();
public void quit();
prepare方法程式碼如下:
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
這個方法可以理解為為本現場初始化佇列訊息,每個執行緒要使用looper進行訊息迴圈時,都要先呼叫perpare方法。
myLooper方法用於返回當前訊息佇列的執行緒。注意,一個訊息佇列只能有一個執行緒。
Looper中最重要的是loop方法,呼叫這個方法,整個佇列遍開始迴圈。下面看看loop方法的程式碼:
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
可以看到,loop方法中不斷地從訊息佇列mQueue中去獲取下一個要處理的訊息msg,如果訊息的target成員變數為null,就表示要退出訊息迴圈了
否則的話就要呼叫這個target物件的dispatchMessage成員函式來處理這個訊息,這個target物件的型別為Handler
在上面的程式碼中,msg呼叫了它的target的dispatchMessage方法,實際上就是將msg丟給handler去處理了(實際上handler也沒有處理,是提供介面給實現了handleMessage的具體事務類去處理)。
接下來看看Handler。Handler主要分為SendMessage和HandleMessage兩個方法,分別是傳送msg和處理msg。
SendMessage方法程式碼如下:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
重點在最後一行加粗的程式碼,就是把msg裝進msgqueue佇列中(message肯定是持有messageQueue物件的廢話)。其實傳送訊息實際上,也就是把訊息裝進訊息佇列中而已,因為前面的loop方法已經在不斷的迴圈的讀取msgqueue中的訊息,只要把訊息加進messagequeue中,就可以是訊息得到處理。
再來看看訊息是怎樣被處理的:
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
這裡可以看到,實際上handler是沒有處理msg的,而是給繼承了handler的具體類,實現handlermessage,並對訊息做出相應的處理。
我們可以看出,handler實際上並沒有做什麼事情,只是提供回撥介面,給具體的類去實現。在使用handler的時候,可以讓具體處理msg的類繼承handler.Callback介面,實現handleMessage方法去處理訊息。
關於Android中的訊息機制就進行到這裡,下一篇將講講handler中的記憶體洩漏問題以及處理方法。