1涌献、什么是Android消息機(jī)制?什么是Handler?為什么要用Handler首有?
Android消息機(jī)制主要是指Handler的運(yùn)行機(jī)制燕垃,Handler運(yùn)行需要底層的MessageQueue和Looper支撐。其中MessageQueue采用的是單鏈表的數(shù)據(jù)結(jié)構(gòu)來(lái)存儲(chǔ)消息列表绞灼,Looper可以理解為消息循環(huán)利术。由于MessageQueue只是一個(gè)消息存儲(chǔ)單元呈野,不能去處理消息低矮,而Looper就是用來(lái)處理消息的,Looper會(huì)以無(wú)限循環(huán)的形式去MessageQueue中查找是否有新消息被冒,如果有則取出消息并處理军掂,否則就一直阻塞等待。Looper中還有一個(gè)特殊的概念是ThreadLocal昨悼,ThreadLocal的作用是可以在每個(gè)線程中存儲(chǔ)數(shù)據(jù)蝗锥,在Handler創(chuàng)建的時(shí)候會(huì)采用當(dāng)前線程的Looper來(lái)構(gòu)造消息循環(huán)系統(tǒng),Handler內(nèi)部就是通過ThreadLocal來(lái)獲取每個(gè)線程的Looper率触,線程默認(rèn)是沒有Looper的终议,在使用Handler之前必須先為主線程創(chuàng)建Looper。大家可能有疑惑了,我們?cè)诖a中使用Handler的時(shí)候穴张,并沒有為主線程去創(chuàng)建Looper的代碼细燎,為什么也可以正常使用呢?那是因?yàn)橹骶€程中ActivityThread被創(chuàng)建時(shí)已經(jīng)初始化主線程的Looper皂甘。
Handler主要用于異步消息的處理:當(dāng)發(fā)出一個(gè)消息(Message)之后玻驻,首先進(jìn)入一個(gè)消息隊(duì)列(MessageQueue),發(fā)送消息的函數(shù)(sendMessage())即刻返回偿枕,而另外一個(gè)部分在消息隊(duì)列中逐一將消息取出璧瞬,然后對(duì)消息進(jìn)行處理,也就是發(fā)送消息和接收消息不是同步的處理渐夸。 這種機(jī)制通常用來(lái)處理相對(duì)耗時(shí)比較長(zhǎng)的操作嗤锉,比如從服務(wù)端拉取一些數(shù)據(jù)、下載圖片等墓塌。
在Android中規(guī)定訪問UI只能在主線程中進(jìn)行(因?yàn)锳ndroid的UI控件不是線程安全的档冬,如果在多線程中并發(fā)訪問可能會(huì)導(dǎo)致UI控件處于不可預(yù)期的狀態(tài)),如果在子線程訪問UI桃纯,程序會(huì)拋出異常酷誓。如在子線程中拉取服務(wù)端數(shù)據(jù)之后需要更新UI上控件展示內(nèi)容,這個(gè)時(shí)候就需要切換到主線程去操作UI态坦,此時(shí)就可以通過Handler將操作UI的工作切換到主線程去完成涂乌。因此,系統(tǒng)之所以提供Handler莱坎,主要原因就是為了解決在子線程中無(wú)法訪問UI的矛盾倔撞。
2、Handler簡(jiǎn)單使用
2.1谜诫、創(chuàng)建Handler實(shí)例的兩種方式
2.1.1 通過Handler.Callback
private Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
2.1.2 通過派生Handler的子類
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
2.2漾峡、Handler發(fā)送消息常用的兩種方式
2.2.1 sendMessage
Message message = new Message();
message.obj = "JokerWan";
message.what = 666;
// 發(fā)送消息
handler.sendMessage(message);
// 發(fā)送延遲消息
handler.sendMessageDelayed(message,3000);
2.2.2 post
handler.post(new Runnable() {
@Override
public void run() {
// action
}
});
3、Handler運(yùn)行機(jī)制原理圖
4喻旷、Handler運(yùn)行機(jī)制源碼分析
溫馨提示:為了方便大家閱讀生逸,我這里貼出的代碼會(huì)對(duì)源碼進(jìn)行刪減,只保留部分跟本文相關(guān)的關(guān)鍵代碼且预,其他代碼用...代替
4.1槽袄、主線程Looper的創(chuàng)建
首先我們看到ActivityThread
類中的main()
方法的代碼
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
...
Looper.prepareMainLooper();
...
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
其中有兩個(gè)關(guān)鍵代碼Looper.prepareMainLooper();
和Looper.loop();
,我們先看Looper.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();
}
}
繼續(xù)跟進(jìn)其中調(diào)用的prepare(false);
方法
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));
}
這個(gè)方法中用到了sThreadLocal
變量锋谐,找到它的聲明
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
類型是ThreadLocal遍尺,泛型是Looper,ThreadLocal是一個(gè)線程內(nèi)部的數(shù)據(jù)存儲(chǔ)類涮拗,通過它可以在指定的線程存儲(chǔ)數(shù)據(jù)乾戏,其他線程無(wú)法獲取該線程數(shù)據(jù)迂苛。我們繼續(xù)跟進(jìn)下sThreadLocal.get()
方法,ThreadLocal#get()
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
調(diào)用getMap
方法并傳入當(dāng)前線程t
鼓择,返回保存當(dāng)前線程中的數(shù)據(jù)ThreadLocal.ThreadLocalMap
的對(duì)象map
灾部,ThreadLocalMap
類似于HashMap
,只不過ThreadLocalMap
僅僅是用來(lái)存儲(chǔ)線程的ThreadLocal
數(shù)據(jù)惯退,首先通過getMap()
獲取當(dāng)前線程的ThreadLocal
數(shù)據(jù)的map
赌髓,接著用ThreadLocal的對(duì)象作為key,取出當(dāng)前線程的ThreadLocal
數(shù)據(jù)的map
中存儲(chǔ)的result催跪,由于前面sThreadLocal
變量聲明的時(shí)候約定的泛型是Looper锁蠕,所以這里返回result對(duì)象就是Looper對(duì)象,繼續(xù)回到prepare()
方法,首先取出當(dāng)前線程的Looper對(duì)象懊蒸,校驗(yàn)下Looper對(duì)象的唯一性荣倾,然后new Looper(quitAllowed)
并保存在ThreadLocal當(dāng)前線程中的數(shù)據(jù)中。
接著跟進(jìn)Looper的構(gòu)造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在Looper的構(gòu)造函數(shù)中創(chuàng)建了一個(gè)MessageQueue的對(duì)象骑丸,并保存在Looper的一個(gè)全局變量mQueue
中舌仍,所以,每個(gè)Looper對(duì)象都綁定了一個(gè)MessageQueue對(duì)象通危。
4.2铸豁、Handler發(fā)送消息到MessageQueue
4.2.1 Handler構(gòu)造器
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
...
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
首先調(diào)用Looper.myLooper();
方法獲取Looper對(duì)象,并保存在mLooper變量中菊碟,看一下Looper#myLooper()
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
若獲取的Looper對(duì)象為null节芥,則拋出異常Can't create handler inside thread xxThread that has not called Looper.prepare()
,因?yàn)樯厦嫖覀円呀?jīng)分析過了逆害,Looper對(duì)象的創(chuàng)建是在prepare()
方法中头镊。
從ThreadLocal中獲取當(dāng)前線程(主線程)的Looper對(duì)象,接著將Looper中的MessageQueue保存到mQueue
中魄幕,并將callback賦值給mCallback相艇,當(dāng)我們通過匿名內(nèi)部類創(chuàng)建Handler時(shí)callback為null。
4.2.2 Handler#sendMessage
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + 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);
}
可以看到不管是調(diào)用sendMessage
還是sendMessageDelayed
纯陨,還有后面會(huì)講到的post(runnable)
坛芽,最終都會(huì)調(diào)用enqueueMessage
方法,方法里面首先將Handler對(duì)象賦值給msg.target
队丝,后面會(huì)通過msg.target
獲取Handler對(duì)象去處理消息(后面會(huì)講到)靡馁,然后調(diào)用了mQueue
的enqueueMessage(msg, uptimeMillis)
方法欲鹏,
跟進(jìn)MessageQueue#enqueueMessage()
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) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
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;
}
...
}
return true;
}
MessageQueue是一個(gè)消息隊(duì)列机久,主要包含兩個(gè)操作,插入和讀取赔嚎,插入就是上面這個(gè)方法enqueueMessage
膘盖,讀取是next
方法胧弛,后面會(huì)講到。盡管MessageQueue叫消息隊(duì)列侠畔,但它內(nèi)部實(shí)現(xiàn)并不是隊(duì)列结缚,而是用單鏈表的數(shù)據(jù)結(jié)構(gòu)來(lái)維護(hù)消息列表,主要原因是單鏈表在插入和刪除上效率比較高软棺。從enqueueMessage
方法的實(shí)現(xiàn)來(lái)看红竭,它的主要操作就是單鏈表的插入操作,將傳進(jìn)來(lái)的msg
插入到鏈表中喘落,并將msg.next
指向上一個(gè)傳進(jìn)來(lái)的Message茵宪。
4.2.3 Handler#post
查看post方法源碼
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
發(fā)現(xiàn)原來(lái)post
方法也是調(diào)用的sendMessageDelayed
方法去send Message,只是把我們傳進(jìn)來(lái)的Runnable對(duì)象通過調(diào)用getPostMessage
方法構(gòu)造成一個(gè)Message
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
通過Message.obtain()
獲取一個(gè)Message瘦棋,將Runnable對(duì)象賦值給m.callback
稀火,并返回Message,到這里我們就知道了赌朋,原來(lái)post(Runnable r)方法只是把r
放進(jìn)一個(gè)message中凰狞,并將message發(fā)送出去
4.3、Looper輪詢MessageQueue并將消息發(fā)送給Handler處理
前面我們已經(jīng)分析過沛慢,在ActivityThread類中的main()方法中會(huì)調(diào)用Looper.prepareMainLooper();
和Looper.loop();
赡若,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;
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;
}
...
try {
msg.target.dispatchMessage(msg);
...
} finally {
...
}
...
msg.recycleUnchecked();
}
}
首先獲取到Looper對(duì)象和Looper對(duì)象里面的MessageQueue,然后構(gòu)造一個(gè)死循環(huán)团甲,在循環(huán)里面通過調(diào)用MessageQueue的next()
方法取出Message分發(fā)給Handler去處理消息斩熊,直到取出來(lái)的msg
為null,則跳出循環(huán)伐庭。msg.target
在enqueueMessage
方法中賦值為發(fā)送此Message的Handler對(duì)象粉渠,這里取出Handler對(duì)象并調(diào)用其dispatchMessage(msg)
去處理消息,而Handler的dispatchMessage(msg)
是在創(chuàng)建Handler時(shí)所使用的Looper中執(zhí)行的圾另,這樣就成功的將代碼邏輯切換到了指定的線程去執(zhí)行霸株。來(lái)看一下MessageQueue的next()
具體實(shí)現(xiàn)
Message next() {
...
for (;;) {
...
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) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
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;
}
...
}
}
可以看到next
是一個(gè)阻塞操作,開啟一個(gè)死循環(huán)來(lái)取Message集乔,當(dāng)沒有Message時(shí)去件,next
方法就會(huì)一直阻塞在那里,這也導(dǎo)致Looper.loop()
方法也一直阻塞扰路。
4.4尤溜、Handler處理消息
Looper對(duì)象從MessageQueue中取到Message然后調(diào)用Handler的dispatchMessage(msg)
去處理消息
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
首先檢查msg
的callback是否為null,不為null則調(diào)用handleCallback(msg)
來(lái)處理消息汗唱,還記得上面我們分析的post(runnable)
嗎宫莱,這里從msg
取出的callback就是調(diào)用Handler的post方法傳入的Runnable對(duì)象,看下handleCallback(msg)
代碼實(shí)現(xiàn)
private static void handleCallback(Message message) {
message.callback.run();
}
實(shí)現(xiàn)很簡(jiǎn)單哩罪,就是取出Runnable對(duì)象并調(diào)用其中的run
方法授霸。
dispatchMessage
方法接著檢查mCallback是否為null巡验,不為null就調(diào)用其handleMessage(msg)
方法,mCallback
類型為Callback接口碘耳,上面我們講過Handler創(chuàng)建的兩種方式显设,第一種方式就是在Handler的構(gòu)造器中通過匿名內(nèi)部類傳入一個(gè)實(shí)現(xiàn)了Handler.Callback接口的對(duì)象,并在構(gòu)造器賦值給mCallback
辛辨。
dispatchMessage
方法最后調(diào)用Handler的handleMessage(msg)
方法來(lái)處理消息捕捂,就是上面我們講到的Handler創(chuàng)建的第二種方式:派生一個(gè)Handler的子類并重寫其handleMessage
方法來(lái)處理消息。
5斗搞、主線程的消息循環(huán)
前面我們分析了ActivityThread
類中的main()
會(huì)創(chuàng)建Looper和MessageQueue绞蹦,并將MessageQueue關(guān)聯(lián)到Looper中,調(diào)用Looper的loop
方法來(lái)開啟主線程的消息循環(huán)榜旦,那么主線程的消息是怎么發(fā)送的呢幽七?ActivityThread
類內(nèi)部定義了一個(gè)Handler的子類H
,ActivityThread
就是通過這個(gè)內(nèi)部類H
來(lái)和消息隊(duì)列進(jìn)行交互并處理消息溅呢。
ActivityThread
通過 ApplicationThread
和 AMS 進(jìn)行進(jìn)程間通信澡屡, AMS以進(jìn)程間通信的方式完成 ActivityThread
的請(qǐng)求后會(huì)回調(diào) ApplicationThread
中的 Binder 方法,然后 ApplicationThread
會(huì)向 H
發(fā)送消息咐旧,H
收到消息后會(huì)將 ApplicationThread
中的邏輯切換到 ActivityThread
中去執(zhí)行驶鹉,即切換到主線程中取執(zhí)行,這個(gè)過程就是主線程的消息循環(huán)模型铣墨。
6室埋、總結(jié)
- Handler 的背后有 Looper、MessageQueue 支撐伊约,Looper 負(fù)責(zé)消息分發(fā)姚淆, MessageQueue 負(fù)責(zé)消息管理;
- 在創(chuàng)建 Handler 之前一定需要先創(chuàng)建 Looper;
- Looper 有退出的功能,但是主線程的 Looper 不允許退出;
- 異步線程的 Looper 需要自己調(diào)用 Looper.myLooper().quit(); 退出;
- Runnable 被封裝進(jìn)了 Message屡律,可以說是一個(gè)特殊的 Message;
- Handler.handleMessage() 所在的線程是 Looper.loop() 方法被調(diào)用的線程腌逢,也可
以說成 Looper 所在的線程,并不是創(chuàng)建 Handler 的線程; - 使用內(nèi)部類的方式使用 Handler 可能會(huì)導(dǎo)致內(nèi)存泄露超埋,即便在 Activity.onDestroy
里移除延時(shí)消息搏讶,必須要寫成靜態(tài)內(nèi)部類;