我們知道在編程時許多操作(如更新UI)需要在主線程中完成,而且,耗時操作(如網(wǎng)絡(luò)連接)需要放在子線程中,否則會引起ANR。所以我們常使用Handler來實現(xiàn)線程間的消息傳遞,這里討論的也就是Handler的運行機制。
成都創(chuàng)新互聯(lián)專注于信州網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗。 熱誠為您提供信州營銷型網(wǎng)站建設(shè),信州網(wǎng)站制作、信州網(wǎng)頁設(shè)計、信州網(wǎng)站官網(wǎng)定制、微信小程序開發(fā)服務(wù),打造信州網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供信州網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。
Handler的運行主要由兩個類來支撐:Looper與MessageQueue。熟悉開發(fā)的朋友都知道在子線程中默認是無法創(chuàng)建Handler的,這是因為子線程中不存在消息隊列。當需要創(chuàng)建一個與子線程綁定的Handler時,標準代碼如下:
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(); } }
在創(chuàng)建Handler前,需要先調(diào)用Looper.prepare()方法,之后再調(diào)用Looper.loop()方法。也就是說Handler的功能實現(xiàn)建立在Looper之上。
static final ThreadLocalsThreadLocal = new ThreadLocal (); final MessageQueue mQueue; final Thread mThread; 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)); } private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
由于Looper的消息循環(huán)是一個死循環(huán),一個線程最多只能有一個Looper,所以Looper.prepare()函數(shù)首先檢查該線程是否已經(jīng)擁有一個Looper,如果有則拋出異常。Looper通過ThreadLocal類為每個線程儲存獨立的Looper實例,簡單說一下ThreadLocal的實現(xiàn)原理:
Java并發(fā)編程:深入剖析ThreadLocal
首先,在每個線程Thread內(nèi)部有一個ThreadLocal.ThreadLocalMap類型的成員變量threadLocals,這個threadLocals就是用來存儲實際的變量副本的,鍵值為當前ThreadLocal變量,value為變量副本。
初始時,在Thread里面,threadLocals為空,當通過ThreadLocal變量調(diào)用get()方法或者set()方法,就會對Thread類中的threadLocals進行初始化,并且以當前ThreadLocal變量為鍵值,以ThreadLocal要保存的副本變量為value,存到threadLocals。
然后在當前線程里面,如果要使用副本變量,就可以通過get方法在threadLocals里面查找。
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; } msg.target.dispatchMessage(msg); msg.recycleUnchecked(); } }
在4行可以看到一個我們熟悉的異常信息,說明并沒有Looper與當前線程相關(guān)聯(lián),也就無法進行消息傳遞。Looper.loop()方法本身是一個死循環(huán),不斷在MessageQueue中取出Message對象進行處理,然后調(diào)用Message.recycleUnchecked()方法對其回收,這也是為什么官方推薦使用Message.obtain()方法來獲取Message實例,而不是直接新建對象。當沒有消息可處理時,MessageQueue.next()方法將阻塞,直到新的消息到來。
對于MessageQueue,我們只需要關(guān)注兩個函數(shù)即可,一個是MessageQueue.enqueueMessage()另一個是MessageQueue.next(),它們分別對應(yīng)著隊列的插入與取出操作。MessageQueue中隊列是使用單鏈表實現(xiàn)的,由Message.next屬性指向其下一個元素。
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; } else { Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } } return true; }
向MessageQueue中插入元素時,需要根據(jù)Message.when屬性的大小決定插入的位置,它代表了Meesage需要被處理的時間,拿Handler.sendMessage()函數(shù)為例。
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; 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)用流程來看,Handler.sendMessage()函數(shù)其實就是向MessageQueue的消息隊列中插入了一個Message.when屬性為當前時間的元素。
對于MessageQueue.next()函數(shù),簡單來說它的作用就是在MessageQueue的頭部取出元素,然后執(zhí)行Handler.dispatchMessage()函數(shù)。
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { handleMessage(msg); } } private static void handleCallback(Message message) { message.callback.run(); }
如果我們使用Handler.post()函數(shù)發(fā)送一個Runnable對象,那么最終Runnable對象會在Handler.handleCallback()函數(shù)中執(zhí)行。如果是一個普通Message,那么它會被分發(fā)到一個我們熟悉的函數(shù)中,Handler.handleMessage(),這就是為什么一般我們都需要重寫這個函數(shù)對消息進行處理。