Android Handler消息機制分析
Handler 是一個可以實現多線程間切換的類,通過 Handler 可以輕松地將一個任務切換到 Handler 所在的線程中去執行。我們最常用的使用的場景就是更新 UI 了,比如我們在子線程中訪問網絡,拿到數據后我們 UI 要做一些改變,如果此時我們直接訪問 UI 控件,就會觸發異常了。這個時候我們往往會通過 Handler 將更新 UI 的操作切換到主線程中。
Handler 的基本使用用法一:通過 send 方法public class MainActivity extends AppCompatActivity { private static final String TAG = 'MainActivity'; private MyHandler mMyHandler = new MyHandler(); @Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);new Thread(new Runnable() { @Override public void run() {Message message = Message.obtain(mMyHandler,0,'通過 send 方法');mMyHandler.sendMessage(message); }}).start(); } private static class MyHandler extends Handler{@Overridepublic void handleMessage(Message msg) { switch (msg.what){case 0: Toast.makeText(MainActivity.this,msg.obj.toString(),Toast.LENGTH_SHORT).show(); break; }} }}用法二:通過 post 方法
public class MainActivity extends AppCompatActivity { private static final String TAG = 'MainActivity'; private Handler mMyHandler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);new Thread(new Runnable() { @Override public void run() {mMyHandler.post(new Runnable() { @Override public void run() {Toast.makeText(MainActivity.this,'通過post方法',Toast.LENGTH_SHORT).show(); }}); }}).start(); }}
其實,通過 post 方法最后通過 send 方法來完成的。這個我們稍后會分析。講到 Handler,我們不得不提起 MessageQueue 類 和 Looper 類。 Handler 通過 send 方法 發送一個消息,會調用 MessageQueue 的 enqueueMessage 方法 將這個消息插入到 MessageQueue 中,然后 Looper 發現有消息來臨時,通過一系列的方法調用后,Handler 如果是通過 post 方法就會執行 post 方法里面的 Runnable ,如果是通過 send 方法就會執行 Handler 的 handleMessage 。這么說感覺有點云里霧里的,讓我們仔細的來看下 Handler 類、MessageQueue 類和 Looper 類。
Handler 類我們先來看下 Handler 類的結構
Handler 類結構.png
Handler 的工作主要包括消息的發送和接收過程。一般來說,消息的發送和消息的接收是位于不同的線程。我們首先來看 post 方法。
/** * Causes the Runnable r to be added to the message queue. * The runnable will be run on the thread to which this handler is * attached. * * @param r The Runnable that will be executed. * * @return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */public final boolean post(Runnable r){ return sendMessageDelayed(getPostMessage(r), 0);}
這里調用了 sendMessageDelayed 方法
/** * Enqueue a message into the message queue after all pending messages * before (current time + delayMillis). You will receive it in * {@link #handleMessage}, in the thread attached to this handler. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the message will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. */public final boolean sendMessageDelayed(Message msg, long delayMillis){ if (delayMillis < 0) {delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}
而 sendMessageDelayed 又調用了 sendMessageAtTime() 方法
/** * Enqueue a message into the message queue after all pending messages * before the absolute time (in milliseconds) <var>uptimeMillis</var>. * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b> * Time spent in deep sleep will add an additional delay to execution. * You will receive it in {@link #handleMessage}, in the thread attached * to this handler. * * @param uptimeMillis The absolute time at which the message should be * delivered, using the * {@link android.os.SystemClock#uptimeMillis} time-base. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the message will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. */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);}
千呼萬喚始出來,在 sendMessageAtTime 這個方法我們終于看到了 MessageQueue 類,這里的邏輯主要向 MessageQueue 中插入了一條消息(Message)。咦?我們不是通過 post 方法傳進來的 Runnable 么?什么時候變成 Message 了?其實剛才我們忽略了一個方法。
public final boolean post(Runnable r){ return sendMessageDelayed(getPostMessage(r), 0);}
沒錯,就是 getPostMessage 方法
private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m;}
從這里看到,系統通過調用 Message.obtain() 創建一個 Message,并把我們通過 post 方法傳進來的 Runnable 賦值給 Message 的 callback。這里的 callback 需要留意,這個在我們之后的分析會用到。接下里我們看 Handler 的 send 方法。
/** * Pushes a message onto the end of the message queue after all pending messages * before the current time. It will be received in {@link #handleMessage}, * in the thread attached to this handler. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */public final boolean sendMessage(Message msg){ return sendMessageDelayed(msg, 0);}
是不是很熟悉?post 方法也是調用這個 sendMessageDelayed 方法,這也是為什么我們之前說 post 方法 也是通過 send 方法來執行的。到此為止,我們已經弄懂 Handler 的消息發送過程。總結的來說,通過 post 方法系統會把 我們傳進來的 Runnable 轉變成 Message,然后就和 send 方法一樣,通過一系列的方法調用之后把 Message 插入到 MessageQueue 當中。至于 Handler 的消息接收過程,我們暫且放一下,先來看 MessageQueue 類。
MessageQueue 類前面說到,Handler 發送消息的過程就是往 MessageQueue 中插入 一個 Message,即調用 MessageQueue 的 enqueueMessage 方法。首先,我們來看下 MessageQueue 的類結構
MessageQueue類結構.png
我們看到 MessageQueue 是比較簡單的。其實,MessageQueue 主要包含兩個操作:插入和讀取。
插入方法:enqueueMessage
boolean enqueueMessage(Message msg, long when) { if (msg.target == null) {throw new IllegalArgumentException('Message must have a target.'); } if (msg.isInUse()) {throw new IllegalStateException(msg + ' This message is already in use.'); } synchronized (this) {if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + ' sending message to a Handler on a dead thread'); Log.w('MessageQueue', e.getMessage(), e); msg.recycle(); return false;}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;}// We can assume mPtr != 0 because mQuitting is false.if (needWake) { nativeWake(mPtr);} } return true;}
讀取方法:next
需要注意的是:讀取操作本身會伴隨著刪除操作
Message next() { // Return here if the message loop has already quit and been disposed. // This can happen if the application tries to restart a looper after quit // which is not supported. final long ptr = mPtr; if (ptr == 0) {return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) {if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands();}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) {// 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 (false) Log.v('MessageQueue', 'Returning message: ' + msg); return msg;} } else {// No more messages.nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. if (mQuitting) {dispose();return null; } // If first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) {// No idle handlers to run. Loop and wait some more.mBlocked = true;continue; } if (mPendingIdleHandlers == null) {mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);}// Run the idle handlers.// We only ever reach this code block during the first iteration.for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try {keep = idler.queueIdle(); } catch (Throwable t) {Log.wtf('MessageQueue', 'IdleHandler threw exception', t); } if (!keep) {synchronized (this) { mIdleHandlers.remove(idler);} }}// Reset the idle handler count to 0 so we do not run them again.pendingIdleHandlerCount = 0;// While calling an idle handler, a new message could have been delivered// so go back and look again for a pending message without waiting.nextPollTimeoutMillis = 0; }}Looper 類
首先,我們也來看下 Looper 的類結構
Looper類結構.png
關于 Looper ,我們首先要明確一點,Looper 是線程相關的,即每個線程的 Looper 是不一樣的,但是線程默認是沒有 Looper 的。可能會有點繞,要理清這里面的邏輯的關系,我們首先要了解 ThreadLocal,關于 ThreadLocal 網上的資料挺多的。簡單地來說,ThreadLocal 是一個線程內部的數據存儲類,比如有有一個 int 類型的 x,在線程 A 的值是 1,在線程 B 的值可以是 0,1,2,..,在線程 C 的值可以是 0,1,2... 我們來看下 Looper 相關的源碼
// sThreadLocal.get() will return null unless you’ve called prepare().static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();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));}/** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */public static Looper myLooper() { return sThreadLocal.get();}
我們為什么要明確 Looper 是線程相關的呢?因為 Handler 創建的時候會采用當前線程的 Looper 來構造消息循環系統的。Handler 創建的時候要先創建 Looper,這時候疑問就來了?我們平常創建 Handler 的時候直接就創建了啊,沒有創建什么 Looper 啊。這是因為我們通常是在主線程 ActivityThread 中創建 Handler。我們看到 Loop 類中有個 prepareMainLooper 方法。
/** * Initialize the current thread as a looper, marking it as an * application’s main looper. The main looper for your application * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} */public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) {if (sMainLooper != null) { throw new IllegalStateException('The main Looper has already been prepared.');}sMainLooper = myLooper(); }}
主線程在創建時,就會調用這個方法創建 Looper。但是如果我們在子線程(如下代碼)直接創建 Handler 就會拋出異常
new Thread(new Runnable() { @Override public void run() {//Looper.prepare();Handler handler = new Handler(); // Looper.loop(); }}).start();
這時只要我們把注釋去掉就不會報異常了。通過源碼我們知道 Looper.prepare() 主要是為當前線程一個 Looper 對象。
/** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */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.loop()方法是干什么的呢?其實,Looper 最重要的一個方法就是 loop 方法了。只有調用 loop 后,消息系統才會真正地起作用。我們來看 loop 方法
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the 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; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) {Message msg = queue.next(); // might blockif (msg == null) { // No message indicates that the message queue is quitting. return;}// This must be in a local variable, in case a UI event sets the loggerPrinter logging = me.mLogging;if (logging != null) { logging.println('>>>>> Dispatching to ' + msg.target + ' ' + msg.callback + ': ' + msg.what);}msg.target.dispatchMessage(msg);if (logging != null) { logging.println('<<<<< Finished to ' + msg.target + ' ' + msg.callback);}// Make sure that during the course of dispatching the// identity of the thread wasn’t corrupted.final long newIdent = Binder.clearCallingIdentity();if (ident != newIdent) { Log.wtf(TAG, 'Thread identity changed from 0x' + Long.toHexString(ident) + ' to 0x' + Long.toHexString(newIdent) + ' while dispatching to ' + msg.target.getClass().getName() + ' ' + msg.callback + ' what=' + msg.what);}msg.recycleUnchecked(); }}
我們可以看到 loop 方法是一個死循環,在這個死循環方法里面會調用 MessageQueue 的 next 方法來獲取新消息。但是如果 next 方法返回了 null,loop 就退出循環。這種情況發生在 Loop 的 quit 方法被調用時,Looper 會 調用 MessageQueue 的 quit 方法來通知消息隊列退出,當消息隊列被標記退出狀態時,它的 next 方法就會返回 null。由于 next 是一個阻塞方法,所以 loop 也會一直阻塞在那里,如果有消息到來, msg.target.dispatchMessage(msg)。這個 msg.target 就是發送這個消息的 Handler 對象啦。這樣 Handler 發送的消息最終又交給自己的 dispatchMessage 方法來處理了。因為 Handler 的 dispatchMessage 方法是創建 Handler 時使用的 Looper 中執行的,這樣就成功地完成線程切換了。
Handler 的消息接收過程經過跋山涉水,通過 Handler 發送的消息最終又會回到自己的 diapatchMessage 中來,那就讓我們來看下 diapatchMessage 方法。
/** * Handle system messages here. */public void dispatchMessage(Message msg) { if (msg.callback != null) {handleCallback(msg); } else {if (mCallback != null) { if (mCallback.handleMessage(msg)) {return; }}handleMessage(msg); }}
首先,檢查 Messgae 的 callback 是否為 null,不為 null 就調用 handleCallback 方法,這個 Message 的 callback 就是我們之前post的。其次,檢查 mCallback 是否為 null ,不為 null 就調用 mCallback 的 handleMessage 方法來處理消息。如果我們是通過繼承 Handler 來實現邏輯的話,此時的mCallback 是為空的,即會調用 handleMessage(msg),也就是我們重寫的 handleMessage 方法。至此,完成了完美的閉環。
有的同學可能會疑問 mCallback 是什么?什么時候會為空?
/** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. * * @param msg A {@link android.os.Message Message} object * @return True if no further handling is desired */public interface Callback { public boolean handleMessage(Message msg);} /** * Constructor associates this handler with the {@link Looper} for the * current thread and takes a callback interface in which you can handle * messages. * * If this thread does not have a looper, this handler won’t be able to receive messages * so an exception is thrown. * * @param callback The callback interface in which to handle messages, or null. */public Handler(Callback callback) { this(callback, false);}
通過源碼可以看出,我們也可以采用 Handler handler = new Handler(callback) 來創建 Handler,這時dispatchMessage 里面就會走 mCallback 不為空的邏輯。
到此這篇關于Android Handler消息機制分析的文章就介紹到這了,更多相關Android Handler消息機制內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!
相關文章:
