Handler源碼解析
一、創建Handler對象
使用handler最簡單的方式:直接new一個Handler的對象
Handler handler = new Handler();
所以我們來看看它的構造函數的源碼:
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
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 = callback;
mAsynchronous = async;
}
這段代碼做了四件事:
1、校驗是否可能內存泄漏
2、初始化一個Looper mLooper
3、初始化一個MessageQueue mQueue
我們一件事一件事的看:
1、校驗是否存在內存泄漏
Handler的構造函數中首先判斷了FIND_POTENTIAL_LEAKS的值,爲true時,會獲取該對象的運行時類,如果是匿名類,成員類,局部類的時候判斷修飾符是否爲static,不是則提示可能會造成內存泄漏。
問:爲什麼匿名類,成員類,局部類的修飾符不是static的時候可能會導致內存泄漏呢?
答:因爲,匿名類,成員類,局部類都是內部類,內部類持有外部類的引用,如果Activity銷燬了,而Hanlder的任務還沒有完成,那麼Handler就會持有activity的引用,導致activity無法回收,則導致內存泄漏;靜態內部類是外部類的一個靜態成員,它不持有內部類的引用,故不會造成內存泄漏
這裏我們可以思考爲什麼非靜態類持有外部類的引用?爲什麼靜態類不持有外部類的引用?
問:使用Handler如何避免內存泄漏呢?
答:使用靜態內部類的方式
2、初始化初始化一個Looper mLooper
這裏獲得一個mLooper,如果爲空則跑出異常:
"Can't create handler inside thread that has not called Looper.prepare() "
如果沒有調用Looper.prepare()則不能再線程裏創建handler!我們都知道,如果我們在UI線程創建handler,是不需要調用這個方法的,但是如果在其他線程創建handler的時候,則需要調用這個方法。那這個方法到底做了什麼呢?我們去看看代碼:
public static void prepare() {
prepare(true);
}
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));
}
先取sThreadLocal.get()的值,結果判斷不爲空,則跑出異常“一個線程裏只能創建一個Looper”,所以sThreadLocal裏存的是Looper;如果結果爲空,則創建一個Looper。那我們再看看,myLooper()這個方法的代碼:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
總上我們得出一個結論:當我們在UI線程創建Handler的時候,sThreadLocal裏已經存了一個Looper對象,所以有個疑問:
當我們在UI線程中創建Handler的時候sThreadLocal裏的Looper從哪裏來的?
我們知道,我們獲取主線程的Looper需要調用getMainLooper()方法,代碼如下:
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
所以我們跟蹤一下這個變量的賦值,發現在方法prepareMainLooper()中有賦值,我們去看看代碼:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
第一步調用了prepare(false),這個方法我們剛纔已經看了,是創建一個Looper對象,然後存到sThreadLocal中;
然後判斷sMainLooper是否爲空,空則拋出異常
sMainLooper不爲空,則sMainLooper = myLooper()
至此sMainLooper對象賦值成功,所以,我們需要知道prepareMainLooper()這個方法在哪調用的,跟一下代碼,就發現在ActivityThread的main方法中調用了Looper.prepareMainLooper();。現在真相大白:
當我們在UI線程中創建Handler的時候sThreadLocal裏的Looper是在ActivityThread的main函數中調用了prepareMainLooper()方法時初始化的
ActivityThread是一個在一個應用進程中負責管理Android主線程的執行,包括活動,廣播,和其他操作的類
3、初始化一個MessageQueue mQueue
從代碼裏我們看出這裏直接調用了:mLooper.mQueue來獲取這個對象,那這個對象可能在Looper初始化的時候就產生了。我們去看看Looper的初始化代碼:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
代碼很簡單,就是創建了MessageQueue的對象,並獲得了當前的線程。
至此,Handler的創建已經完成了,本質上就是獲得一個Looper對象和一個MessageQueue對象!
二、使用Handler發送消息
Handler的發送消息的方式有很多,我們跟蹤一個方法sendMessage方法一直下去,發現最後竟然調用了enqueueMessage(queue, msg, uptimeMillis),那我們看看這個方法的代碼:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
這段代碼做了兩件事:
1、給msg.target賦值,也就是Handler對象
2、給消息設置是否是異步消息。
3、調用MessageQueue 的enqueueMessage(msg, uptimeMillis)方法
我們只關注第三步:這一步把Handler的發送消息轉給了MessageQueue的添加消息的方法。
所以至此,Handler發送消息的任務也已經完成了,本質上就是調用MessageQueue自己的添加消息的方法!
三、MessageQueue添加消息
MessageQueue的構造函數代碼如下:
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
也沒做什麼特別的事情。我們去看看enqueueMessage(msg, uptimeMillis)方法代碼:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
代碼很長,但是通過觀察這段代碼我們發現這個MessageQueue實際上是個鏈表,添加消息的過程實際上是一個單鏈表的插入過程。
所以我們知道了Handler發送消息的本質其實是把消息添加到MessageQueue中,而MessageQueue其實是一個單鏈表,添加消息的本質是單鏈表的插入
四、從消息隊列裏取出消息
我們已經知道消息如何存儲的了,我們還需要知道消息是如何取出的。
所以我們要看一下Looper.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;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
msg.target.dispatchMessage(msg);
}
}
}
代碼太長我刪了部分代碼。可以看出這個方法主要的功能是很簡單的。
獲取Looper對象,如果爲空,拋異常。
獲取消息隊列MessageQueue queue
遍歷循環從消息隊列裏取出消息,當消息爲空時,循環結束,消息不爲空時,分發出去!
但是實際上當沒有消息的時候queue.next()方法會被阻塞,並標記mBlocked爲true,並不會立刻返回null。而這個方法阻塞的原因是nativePollOnce(ptr, nextPollTimeoutMillis);方法阻塞。阻塞就是爲了等待有消息的到來。那如果在有消息加入隊列,loop()方法是如何繼續取消息呢?
這得看消息加入隊列的時候有什麼操作,我們去看剛纔的enqueueMessage(msg, uptimeMillis)方法,發現
if (needWake) {
nativeWake(mPtr);
}
當needWake的時候會調用一個本地方法喚醒讀取消息。
所以這裏看一下消息分發出去之後做了什麼?
msg.target.dispatchMessage(msg);
上面講過這個target其實就是個handler。所以我們取handler裏面看一下這個方法代碼
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
代碼非常簡單,當callback不爲空的時候調用callback的handleMessage(msg)方法,當callback爲空的時候調用自己的handleMessage(msg)。一般情況下我們不會傳入callback,而是直接複寫Handler的handleMessage(msg)方法來處理我們的消息。