對Handler機制的理解
前言
Handeler機制算是Android中一個比較核心的內容了留凭,本文是經過對Handler源碼的分析做的一個總結耍休。
目錄
主要從以下幾個方面來總結:
- Handler機制簡介
- 核心類以及作用
- Handler工作流程
- 源碼分析
Handler機制簡介
Handler機制是一套消息傳遞機制蛤迎,在Android的多線程開發(fā)場景中會經常使用舔清,實現的功能是多線程之間的通信徐许。由于Android的UI操作不是線程安全的施蜜,所以在子線程中主要是通過Handler來對UI操作。
核心類以及作用
Handler機制由Handler雌隅、Looper翻默、MessageQueue和Message構成。
-
Handler
Handler類兩個作用恰起,發(fā)送消息和處理消息修械。
-
MessageQueue
MessageQueue用來維護消息隊列,根據時間將消息放入隊列和將消息移除隊列检盼。
-
Looper
以死循環(huán)的方式不斷從MessageQueue中獲取消息肯污,分發(fā)消息。
-
Message
消息的承載者
工作流程
工作流程主要分為4步:
-
核心類初始化
為當前線程創(chuàng)建Looper梯皿,MessageQueue,即調用靜態(tài)方法Looper.prepare(),然后創(chuàng)建Handler對象(有多個重載的構造方法仇箱,可以指定Looper,默認是當前線程的Looper)东羹,啟動循環(huán)Looper.loop().
-
發(fā)送消息
通過Handler的sendMessage和post方法發(fā)送消息,將消息加入到消息隊列剂桥。
-
消息循環(huán)
Lopper不停通過循環(huán),從MessageQueue中拿到Message属提,然后調用與Message綁定的Handler來分發(fā)消息(handler.dispatchMessage(Message msg))权逗,具體分發(fā)邏輯會在后文講到。
-
消息處理
Handler的dispatchMessage方法被調用后冤议,會選擇執(zhí)行具體的消息處理邏輯斟薇。
注意事項:Thread、Handler恕酸、MessageQueue和Looper的對應關系堪滨。
- 一個Thread只能綁定一個Looper,一個MessageQueue蕊温,多個Handler袱箱。
- 一個Looper只能綁定一個MessageQueue,多個Handler
- 一個Handler只能綁定一個Looper和一個MessageQueque
源碼分析
這一部分主要是分析核心類的構造方法和主要方法义矛,來提高對Handler機制細節(jié)方面的認識发笔。
Looper類
先看構造方法:
//私有構造方法,參數表示是否允許退出循環(huán)凉翻,
private Looper(boolean quitAllowed) {
//創(chuàng)建消息隊列了讨,保存當前線程,參數由MessageQueue接收,可見退出邏輯應該在MessageQueue中實現
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
//子線程Looper初始化
public static void prepare() {
//子線程允許退出
prepare(true);
}
//在此方法中創(chuàng)建Looper
private static void prepare(boolean quitAllowed) {
//檢查當前線程是否已有Looper前计,每個線程只能執(zhí)行一次胞谭,否則將會拋出異常,即一個線程只能有一個Looper
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//創(chuàng)建Looper男杈,并保存在當前線程中
sThreadLocal.set(new Looper(quitAllowed));
}
//主線程Looper初始化
public static void prepareMainLooper() {
//主線程不允許退出
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
//保存主線程Looper
sMainLooper = myLooper();
}
}
//獲取當前線程的Looper
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
//判斷當前線程是不是運行在與Looper關聯(lián)的線程中
public boolean isCurrentThread() {
return Thread.currentThread() == mThread;
}
//在任何地方都可通過此方法獲取主線程的Looper對象
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
上面列出了Looper的構造方法以及和構造方法相關的幾個方法韭赘,由分析可知:
- Looper的構造方法是私有的,只能通過靜態(tài)方法*prepare()和prepareMainLooper()為當前線程創(chuàng)建Looper势就。
- 創(chuàng)建Looper的時候就會創(chuàng)建MessageQueue泉瞻,并綁定MessageQueue和Thread實例。
- 主線程不允許退出苞冯,子線程可以退出袖牙,退出邏輯在MessageQueue中實現。
- 創(chuàng)建的Looper實例通過ThreadLocal被保存在當前線程中舅锄。(需要知道:ThreadLocal可以將對象保存在當前線程中鞭达,并且也可以從當前線程中獲取之前保存的對象,至于具體實現這里不在贅述)
循環(huán)方法分析:
public static void loop() {
//這里要注意prepare()和loop()的調用順序皇忿,以及要確定在調用此方法的線程中是否有Looper,否則會拋出異常
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;
//開始循環(huán)讀取消息隊列中的消息
for (;;) {
//在隊列中沒有消息時畴蹭,會阻塞線程
Message msg = queue.next(); // might block
//退出消息循環(huán)的時機是在消息隊列返回Null值時
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
//將消息交給與消息綁定的handler處理,也就是發(fā)送這條消息的Handler
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//回收消息
msg.recycleUnchecked();
}
}
Handler類
從以下三個方面來分析源碼:
- 構造方法
- 發(fā)送消息
- 處理消息
構造方法
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(boolean async) {
this(null, async);
}
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Callback callback, boolean async) {
//繼承Handler時鳍烁,若沒有用static修飾叨襟,可能會造成內存泄漏
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());
}
}
//在子線程中直接使用new Handler()會拋出異常的原因:沒有創(chuàng)建Looper
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;
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
/**
*@param looper 指定與handler綁定的Looper
*@param callback Handler中定義的接口,只有一個handleMessage(Message msg)方法
*@param async 只有系統(tǒng)創(chuàng)建Handler會用到這個參數幔荒,表示由此Handler發(fā)送的消息是否要標記為異步消息
*/
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
從上面可以知道糊闽,Handler的構造方法有很多個重載,但是我們能調用的方法只有三個爹梁,其他的都被用@hide注解右犹,只有sdk能調用。
- Handler()
- Handler(Callback callback)
- Handler(Looper looper)
通過構造方法可以總結兩點:
- 在繼承Handler時姚垃,需要注意可能發(fā)生的內存泄漏
- 子線程在沒有調用Looper.prepare()時念链,除了Handler(Looper looper),其他兩個構造方法不能調用积糯,否則會拋出異常掂墓。
發(fā)送消息
發(fā)送消息有兩種方式
- sendMessage:發(fā)送一個Message對象
- post:發(fā)送一個Runnable,Runnable最終會被包裝成Message對象
//包裝Runnable
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
private static Message getPostMessage(Runnable r, Object token) {
Message m = Message.obtain();
m.obj = token;
m.callback = r;
return m;
}
//消息最終都是通過這個方法發(fā)送
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);
}
//直接將消息放在消息隊列頭部絮宁,優(yōu)先處理梆暮,可能有副作用
public final boolean sendMessageAtFrontOfQueue(Message msg) {
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, 0);
}
//將消息加入到消息隊列中
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//將消息與當前Handler綁定
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
處理消息
//分發(fā)消息服协,由Looper調用
public void dispatchMessage(Message msg) {
//先判斷是否是post的消息绍昂,
if (msg.callback != null) {
handleCallback(msg);
} else {
//在判斷當前Handler是否設置CallBack,構造方法有說到
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//最后才調用Handler的成員方法
handleMessage(msg);
}
}
MessageQueue類
MessageQueue用于維護消息隊列,主要有兩種操作:
- 將消息加入到消息隊列
- 將消息移除消息隊列
將消息加入到消息隊列:
boolean enqueueMessage(Message msg, long when) {
...省略
synchronized (this) {
...省略
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//當前是空隊列or消息是通過sendMessageAtFrontOfQueue發(fā)送或時間小于隊列第一個消息的時間窘游,則將msg置于隊列頭部
//若當前是阻塞狀態(tài)唠椭,需要喚醒線程
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//死循環(huán),按時間順序忍饰,將消息放入隊列
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) {
//調用native方法艾蓝,喚醒線程
nativeWake(mPtr);
}
}
return true;
}
注意:消息隊列雖然是一個隊列力崇,但并不完全遵循先入先出的規(guī)則,消息在入隊時赢织,會遍歷鏈表亮靴,比較每一個消息的時間,按時間排序
將消息移除消息隊列:
Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
//
int nextPollTimeoutMillis = 0;
//死循環(huán)
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//nextPollTimeoutMillis不為0時于置,阻塞線程
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
//這里也有一個循環(huán)茧吊,用來找到下一個要處理的message
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
//當前時間小于下一個消息的時間,計算阻塞時間
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//從隊列中取出消息
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
//沒有消息時線程阻塞
// No more messages.
nextPollTimeoutMillis = -1;
}
//若當前處于退出狀態(tài)八毯,返回null
if (mQuitting) {
dispose();
return null;
}
...省略
}
}
至此搓侄,對Handler機制的源碼分析完畢,通過閱讀這些源碼话速,對Handler機制的實現細節(jié)有了一個更深的認識讶踪。