概述
Handler、Looper、MessageQueue
這幾個,相信接觸
android開發
的,基本都能說出來這三者的基本關系,但是很多人可能也是停留在三者的調用關系上,你如果問他
postDelay是如何實作的
,
為啥子線程中要先prepare
,
為啥handler容易産生記憶體洩露
等等,他可能會一知半解,筆者還是決定從源碼的角度重新審視這些問題
從問題中看源碼
如果讓你線上程中實作
handler
,你可能會很快的寫下如下代碼,那筆者就還是以這個為切入點,分析前面提出的幾個問題
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler(Looper.myLooper());
Looper.loop();
}
}).start();
為啥子線程需要
prepare
,如果不調用會有什麼問題?
我們直接進入
Looper.prepare()
的源碼中看看:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
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));
}
我們可以看到
調用的其實是一個靜态方法
,内部的處理就是
執行個體化了一個Looper對象
,存放在
sThreadLocal
中了,
ThreadLocal
你可以了解為是線程本地存儲,用于儲存線程共享變量,如果不調用
prepare
直接進行
loop操作
,會怎樣呢?
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
public static void loop() {
final Looper me = myLooper();
if (me == null) {
//如果沒有進行prepare操作會抛異常
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//拿到目前線程存儲的MessageQueue
final MessageQueue queue = me.mQueue;
......
for (;;) {
//如果poseDelay方法有延時參數,這個方法會阻塞,等待延時
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
......
final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
......
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
//調用handler的dispatchMessage方法
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
......
msg.recycleUnchecked();
}
}
我們可以很清楚的看到
第一行調用的實際是myLooper方法
,從線程本地存儲空間中拿到共享變量
Looper的執行個體
,是以如果子線程中沒有進行
prepare操作
,這裡就會抛出異常
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
那麼looper和handler究竟什麼聯系呢?
下面是
Handler的構造方法
:
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
可以看到
handler
在初始化的時候,會持有
looper以及對應looper的MessageQueue
的執行個體
下面是
Message類的屬性
:
public final class Message implements Parcelable {
public static final Creator<Message> CREATOR = null;
public int arg1;
public int arg2;
public Object obj;
public Messenger replyTo;
public int sendingUid;
public int what;
......
long when;
Handler target;
Runnable callback;
Message next;
......
}
可以很清楚的看到
Message執行個體
本身會持有
handler引用
,也會有
延遲時長屬性when
,以及下一個
Message
的字段
next
相信看了上述
Looper,Handler,MessageQueue
,你對他們的
持有關系
應該有了解了
handler的postDelayed是如何實作的?
public final boolean postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
通過上面的源碼,我們可以了解一點:
最終是調用MessageQueue的enqueueMessage方法,根據
postDelay
完成消息排隊
延遲時長
而
Message
你可以了解為是一個節點,裡面有
next字段辨別下一個消息
,這裡要注意上面的
loop()方法中有一行代碼
是以當
loop方法
循環從
MessageQueue中
讀取消息,這個
MessageQueue
的
next
方法會根據之前定義的
延遲時長
進行等待,直到達到這個時長才會傳回,然後調用
message.target
即我們目标的
handler
中的
dispatchMessage方法
handler為什麼容易造成記憶體洩露
我們在使用
handler
的時候,如果不注意:很容易會寫出
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
要知道,
匿名内部類會持有外部的引用
,比如是一個
activity引用
,那麼你如果了解上述的
延遲消息的實作原理就會明白
,當我們發送
延遲消息
的時候,這個消息會被存放在
MessageQueue中
,等待
next方法傳回
,而對應的
Message
又會持有
handler對象
,就會導緻我們最終引用的這個
activity一直無法釋放
,進而導緻記憶體洩露的産生
為啥主線程中調用loop不會阻塞呢?
每個應用都有一個主線程,當主線程啟動的時候其實也會調用
loop
方法,但這個過程會不斷的循環從
MessageQueue
中取消息來執行,因為
android的設計
本身是基于
事件驅動的
,是以我們都主線程中的操作都可以看做是不同的
事件
,最終交由主線程的
handler
來執行,是以如果我們在
主線程中做過多耗時操作
,會導緻主線程阻塞,即ANR的産生,這個消息循環的模型跟以前在
Windows
中
Win32下的消息循環類似