1.前言
故事要從遠(yuǎn)古時(shí)代的Java說起。
首先隨便寫一個(gè)簡單的Java程序肝陪,編譯后運(yùn)行驳庭,程序“嗖”的一下執(zhí)行完畢,接著就退出運(yùn)行。這時(shí)候饲常,如果我們?cè)诖a中添加一個(gè)無限循環(huán)的函數(shù)蹲堂,再次編譯運(yùn)行,會(huì)發(fā)生什么事呢贝淤?程序會(huì)一直執(zhí)行著直到天荒地老柒竞,那顆象征著程序運(yùn)行的紅燈將永遠(yuǎn)亮著。
現(xiàn)在回到Android程序中播聪,隨便寫一個(gè)簡單的程序朽基,編譯后運(yùn)行,哇离陶,那顆象征著程序運(yùn)行的紅燈一直在亮啊踩晶。
眾所周知,ActivityThread的main()方法是應(yīng)用程序的入口函數(shù)枕磁。所以在這個(gè)方法里一定存在著一個(gè)死循環(huán):
public static void main(String[] args) {
...
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
...
Looper.loop();
...
}
從名字上就可以看出渡蜻, Looper就是你沒跑了!
不過問題又來了计济,既然是死循環(huán)茸苇,那為什么我的手機(jī)屏幕還能響應(yīng)點(diǎn)擊事件,還能執(zhí)行各種各樣的操作呢沦寂?
這就是Looper的神奇之處了学密,下面聽我緩緩道來。
2.Looper與它的朋友們
先總體的介紹下這家人的情況
每一個(gè)主線程都綁定了一個(gè)Looper循環(huán)器传藏,每一個(gè)Looper循環(huán)器又擁有一個(gè)MessageQueue消息隊(duì)列腻暮,Looper做的就是不停的從MessageQueue中拿出Message并交給主線程進(jìn)行處理。
因此如果需要讓主線程執(zhí)行操作的話毯侦,只要向MessageQueue發(fā)送Message即可哭靖。
發(fā)送Message就需要Handler了。Handler分系統(tǒng)與客戶端兩種侈离,多個(gè)Handler可以向同一個(gè)MessageQueue發(fā)送Message试幽。同時(shí),Handler也負(fù)責(zé)Message的處理卦碾,即Handler會(huì)在主線程執(zhí)行具體的操作铺坞。
2.1系統(tǒng)級(jí)!H
在ActivityThread中有這樣一個(gè)餓加載
final H mH = new H();
點(diǎn)進(jìn)去看看洲胖,發(fā)現(xiàn)這是繼承自Handler的一個(gè)類济榨,其本身長得不得了。找到他的handleMessage()
方法绿映,里面有一萬個(gè)對(duì)消息類型的判斷擒滑。
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
...省略剩余9999個(gè)...
}
仔細(xì)閱讀這些類型,發(fā)現(xiàn)其大多數(shù)與四大組件的生命周期有關(guān)。也就是說橘忱,系統(tǒng)級(jí)Handler H負(fù)責(zé)了四大組件生命周期的操作,還包括一些進(jìn)程間通信卸奉、應(yīng)用的退出等等钝诚,總之功能強(qiáng)大。
這里有必要提一下應(yīng)用退出:
case EXIT_APPLICATION:
if (mInitialApplication != null) {
mInitialApplication.onTerminate();
}
Looper.myLooper().quit();
break;
其實(shí)應(yīng)用退出的邏輯與代碼都很簡單榄棵,就是粗暴的退出了消息循環(huán)凝颇。此時(shí)循環(huán)已結(jié)束,程序就運(yùn)行完了疹鳄,相應(yīng)的進(jìn)程也會(huì)被虛擬機(jī)回收拧略。所以說,整個(gè)應(yīng)用都說結(jié)束就結(jié)束了瘪弓,Activity的onDestory是不能保證一定會(huì)被執(zhí)行的垫蛆!Service的一些回調(diào)也是同樣的道理,千萬別在這些地方做什么不可描述的操作腺怯!
2.2Message
2.2.1 Message的獲取
下面介紹Message袱饭,先來看看如何獲取一個(gè)Message:
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
這是一個(gè)典型的獲取鏈表對(duì)象的操作。開始sPool指向第一個(gè)message m呛占, 接著讓sPool指向m的next虑乖,然后將m.next賦空(為了將m拿出來),最后返回m晾虑。還不能理解的話只能看我隨手畫的圖了疹味。
2.2.2 Message的回收
接著看看消息的回收,也是同樣的道理帜篇,這里就不再多做解釋(不想畫圖)
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
...
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
看到這個(gè)方法糙捺,有人也許會(huì)好奇,哪來的笙隙?我咋從來沒用過呢继找?
當(dāng)然啦,回收的操作Google工程師已經(jīng)給安頓好了逃沿,當(dāng)Handler處理完成以后婴渡,Message會(huì)自動(dòng)執(zhí)行回收操作。
在下面的loop()方法中凯亮,先來看注釋五msg.recycleUnchecked()
边臼,message的回收操作就是在這里自動(dòng)進(jìn)行的,在loop()
的死循環(huán)過程中假消,首先是通過queue.next()
獲取msg柠并,接著通過dispatchMessage()
讓相應(yīng)的handler進(jìn)行消息的處理,最后調(diào)用msg.recycleUnchecked()
完成消息的回收。
2.3Looper
Looper中loop()
方法的內(nèi)容非常豐富臼予,且慢慢學(xué)習(xí)鸣戴。
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);
}
...
//注釋五
msg.recycleUnchecked();
}
}
2.3.1 Looper與ThreadLocal的故事
注釋一,獲取looper對(duì)象是通過myLooper()
這個(gè)方法
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
進(jìn)去一看粘拾,發(fā)現(xiàn)這里有一個(gè)叫做sThreadLocal的東西窄锅,去查一查他的三代
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
跟著上面的注釋,找到prepare()
方法缰雇。類似的還有一個(gè)叫prepareMainLooper()
的家伙入偷,區(qū)別是后者會(huì)將當(dāng)前線程作為主線程,在ActivityThread的main()
方法中會(huì)調(diào)用后者械哟。
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));
}
從上面的set疏之、get可以大致猜測(cè)出,sThreadLocal是一個(gè)類似于map的數(shù)據(jù)結(jié)構(gòu)暇咆,它的作用就是將looper與thread進(jìn)行綁定锋爪。來看看具體的set()
方法
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
果然這里出現(xiàn)了ThreadLocalMap ,map對(duì)象是通過getMap(t)
獲取的爸业。
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
也就是說几缭,每個(gè)Thread中都有一個(gè)ThreadLocalMap ,在set()
中沃呢,首先獲取當(dāng)前Thread年栓,在從Thread中獲取其ThreadLocalMap,如果為空則創(chuàng)建薄霜,最后向ThreadLocalMap中插入鍵值對(duì)某抓,鍵是當(dāng)前的ThreadLocal對(duì)象,值就是通過參數(shù)傳入的新建的Looper對(duì)象惰瓜。要注意否副,每新建一個(gè)Looper對(duì)象,就會(huì)在其內(nèi)部新建一個(gè)ThreadLocal與之對(duì)應(yīng)崎坊!
這樣做的意義是什么呢备禀?是將Looper與Thread綁定,使得每一個(gè)Thread都有且只有一個(gè)Looper與之對(duì)應(yīng)奈揍,借此來解線程安全的問題曲尸。
2.3.2 Looper與其他人的故事
注釋二,從Looper對(duì)象中獲取了MessageQueue男翰,這里驗(yàn)證了之前所說的另患,每一個(gè)Looper循環(huán)器又擁有一個(gè)MessageQueue消息隊(duì)列。
注釋三蛾绎,各單位注意昆箕!這里就是Android程序真正進(jìn)行死循環(huán)的地方鸦列!這個(gè)死循環(huán)寫的很死,只能從內(nèi)部通過return語句打破循環(huán)鹏倘。比如當(dāng)queue中的msg為null時(shí)薯嗤,就可以結(jié)束循環(huán)退出程序了。
2.4Handler
2.4.1 Handler處理消息的三種方式
注釋四纤泵,msg.target是封裝在message中的handler對(duì)象骆姐,來看看handler的dispatchMessage()
方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
handler一共有三種處理message的方式
第一種方式中,出現(xiàn)了一個(gè)叫msg.callback的玩意兒夕吻,這是一個(gè)runnable對(duì)象
/*package*/ Runnable callback;
message是handler發(fā)送的诲锹,所以說這個(gè)runnable應(yīng)該也是handler傳進(jìn)來的繁仁。還記得handler中有一個(gè)post()
方法嗎
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
這兩塊代碼是不是特別簡潔明了涉馅,根本不需要多余的解釋。
再來看看具體執(zhí)行調(diào)用的handleCallback(msg)
private static void handleCallback(Message message) {
message.callback.run();
}
哇黄虱!寫的不能再清楚了稚矿!
這種情況下,我們不需要handler來執(zhí)行具體的處理捻浦,而是回調(diào)了runnable對(duì)象的run方法來做些特別的事情晤揣。一般可以在這里執(zhí)行一些循環(huán)調(diào)用的操作。
第二種方式朱灿,就是通過handler中自帶的mCallback來處理message昧识。這個(gè)mCallback是在handler初始化的時(shí)候調(diào)用的
public Handler(Callback callback, boolean async) {
...
mCallback = callback;
...
如果在創(chuàng)建handler的時(shí)候沒有指定,那就會(huì)執(zhí)行第三種盗扒,也就是最常用的方式跪楞,直接通過重寫handleMessage(msg)
來完成對(duì)消息的處理。
2.4.2 Handler發(fā)送消息
上面講了handler處理消息的三種方式侣灶,現(xiàn)在就順便理一理其發(fā)送消息的過程甸祭。
發(fā)送message從sendMessage()
開始經(jīng)歷一系列簡單的回調(diào),這里有個(gè)關(guān)于時(shí)間的參數(shù)需要格外注意
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
queue.enqueueMessage(msg, uptimeMillis)
又會(huì)涉及到鏈表的操作褥影,uptimeMillis決定了message開始進(jìn)行處理的時(shí)間池户。
boolean enqueueMessage(Message msg, long when) {
...
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;
}
...
}
p是頭指針,首先判斷p是否存在凡怎,如果不存在校焦,則讓傳進(jìn)來的message成為頭指針;否則的話進(jìn)入循環(huán)统倒,如果當(dāng)前message的開始執(zhí)行時(shí)間小于鏈表中p的開始執(zhí)行時(shí)間斟湃,就將message插入到p的前面,否則的話再將message插入到鏈表的最后檐薯。
既然發(fā)送消息涉及到了開始執(zhí)行時(shí)間凝赛,那么取出消息一定也和這個(gè)參數(shù)有關(guān)注暗。
MessageQueue.next()
方法并不只是簡單的根據(jù)進(jìn)入消息隊(duì)列的順序來取出message,而是還要考慮到他們具體執(zhí)行的時(shí)間墓猎。如果當(dāng)前時(shí)間小于mesaage開始執(zhí)行的時(shí)間捆昏,那就繼續(xù)獲取下一個(gè)message。
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;
}
2.4.3 Handler與Looper是怎么勾搭起來的
看到這里毙沾,認(rèn)真閱讀的同學(xué)一定有一個(gè)疑問骗卜,既然MessageQueue與Looper綁定,Looper又與Thread綁定左胞,那么Handler是怎么與Looper進(jìn)行綁定的呢寇仓?換句話說,Handler怎么知道自己要將消息發(fā)給哪個(gè)Looper對(duì)象中的MessageQueue烤宙,而這個(gè)Looper從MessageQueue中取出消息之后遍烦,又怎么知道要讓哪一個(gè)Handler來進(jìn)行處理呢?
我們來看Handler的構(gòu)造方法
public Handler() {
this(null, false);
}
其重載了下面這個(gè)兩個(gè)參數(shù)的構(gòu)造方法
public Handler(Callback callback, boolean async) {
...
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;
}
可見躺枕,Looper對(duì)象是通過靜態(tài)方法myLooper()
獲取到的服猪。
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
文章在注釋一處就介紹了這個(gè)方法,如果沒什么印象了要回去再看一遍拐云,前面講了如何set罢猪,這里就涉及到如何get,現(xiàn)在深入進(jìn)去看看
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
看明白了吧叉瘩!Handler在初始化的時(shí)候膳帕,會(huì)從當(dāng)前線程獲取到ThreadLocalMap,由于一般我們都是在主線程初始化Handler薇缅,而主線程在創(chuàng)建的時(shí)候已經(jīng)通過Looper.prepareMainLooper()
將ThreadLocal與Looper以鍵值對(duì)的形式set()
進(jìn)ThreadLocalMap危彩,所以此時(shí)就可以輕松的get()
到Looper對(duì)象了!
2.4.4 Handler與Looper是怎么在子線程勾搭起來的
好事者這時(shí)候又會(huì)問了捅暴,那如果我要在子線程中使用Handler呢恬砂?別急,我們回到Looper的源碼蓬痒,讀一讀最上面的官方注釋:
<pre>
* class LooperThread extends Thread {
* public Handler mHandler;
*
* public void run() {
* Looper.prepare();
*
* mHandler = new Handler() {
* public void handleMessage(Message msg) {
* // process incoming messages here
* }
* };
*
* Looper.loop();
* }
* }</pre>
這下通暢了吧泻骤!在子線程中,首先通過Looper.prepare()
將Looper與Thread綁定梧奢,接著創(chuàng)建的Handler就與Looper對(duì)應(yīng)了起來狱掂,最后就可以通過Looper.loop()
開啟循環(huán)。這個(gè)步驟和ActivityThread的main()
方法一模一樣亲轨,只是前者由系統(tǒng)代勞趋惨,而在子線程中則需要我們自己動(dòng)手。
2.5總結(jié)
沒有總結(jié)
完結(jié)撒花~