在Android中的多進程、多線程中提過,只有主線程(UI線程)能夠更新UI,其餘線程不能夠,因此通常耗時操做放到子線程。子線程能夠經過Handler將相關信息通知到主線程。
Android的消息機制主要是Handler機制。Handler的工做過程,還有兩個重要部分MessageQueue(消息隊列,下面簡稱MQ)和Looper。html
因爲下面總結中穿插了很多源碼 便於理解,致使篇幅比較長(加代碼有600多行)。因此先大體總結並給出大體目錄,提早了解是不是須要的內容。java
Handler能作到跨線程,主要是Looper及內部的消息隊列。最多見的:程序啓動主線程建立Looper並綁定了消息隊列,在主線程建立Handler,這個Handler與Looper綁定的。在其餘線程(任何地方)經過這個Handler發送消息,消息都加入到了主線程Looper內部的消息隊列(消息發送到的MQ是 建立Handler時綁定的Looper內部MQ),當消息被Looper循環取出,天然就回到了主線程android
1 Looper、Handler與MQ 1.1 Looper 1.1.1 Looper的建立:prepare() 1.1.2 Looper循環:loop() 1.2 Handler 1.2.1 Handler的建立 1.2.2 Handler發送消息 1.2.3 Handler分派處理:dispatchMessage 1.3 MessageQueue 1.3.1 入隊:enqueueMessage() 1.3.2 next()方法 1.3.3 退出:quit() 2 其餘注意點 2.1 Handler通常使用 2.2 消息池 2.3 子線程到主線程的方法 2.4 主線程的Looper 2.5 ANR問題
Looper是循環器,爲一個線程運行消息循環,不斷檢查消息隊列中是否有新的消息。
Looper.prepare()爲當前線程建立一個looper,並在其內部維護一個MQ。
Looper.loop()即looper開始工做,運行消息循環。緩存
下面是Looper部分的幾處源碼,有助於理解。安全
// sThreadLocal.get() will return null unless you've called prepare(). @UnsupportedAppUsage static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); @UnsupportedAppUsage final MessageQueue mQueue; final Thread mThread; 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)); } private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
Looper經過prepare()方法建立,主要有如下幾點:數據結構
Message.java @UnsupportedAppUsage /*package*/ Handler target; @UnsupportedAppUsage /*package*/ Runnable callback; Looper.java public static void loop() { ...... for (;;) { //不斷取出下一條消息,mgs爲null即消息隊列退出,若沒有消息且沒有退出 消息隊列一直阻塞的 Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } ...... try { //分派消息,經過Handler處理 msg.target.dispatchMessage(msg); if (observer != null) { observer.messageDispatched(token, msg); } dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } catch (Exception exception) { ...... msg.recycleUnchecked(); } } public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
因爲代碼比較長,截取了關鍵代碼,......表示該處有省略的代碼。
loop()是消息循環運行的關鍵,總體把握這裏關注兩行代碼:Message msg = queue.next(); 和 msg.target.dispatchMessage(msg);
。這兩個分別在MQ部分和Handler部分有詳述。多線程
Handler主要包含消息的發送和接收處理。less
@UnsupportedAppUsage final Looper mLooper; final MessageQueue mQueue; @UnsupportedAppUsage final Callback mCallback; final boolean mAsynchronous; /** * @hide */ public Handler(@Nullable Callback callback, boolean async) { 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()); } } 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; }
注意幾點:異步
public final boolean post(@NonNull Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; } public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } public boolean sendMessageAtTime(@NonNull 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(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) { msg.target = this; msg.workSourceUid = ThreadLocalWorkSource.getUid(); if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
這裏都是使用SystemClock.uptimeMillis(),簡單說明下SystemClock.uptimeMillis()與System.currentTimeMillis()區別:
System.currentTimeMillis()是1970年1月1日(UTC)到如今的毫秒值。
SystemClock.uptimeMillis()是設備啓動到如今的時間的毫秒值。(不包括深度睡眠)
SystemClock.elapsedRealtime()是設備啓動到如今時間的毫秒值。(包括深度睡眠)
爲何基本都用SystemClock.uptimeMillis()做爲時間間隔的獲取方法呢?
System.currentTimeMillis()經過設置設備的時間是能夠改變的,這樣設置後 那些計劃的執行明顯會發生異常。async
@UnsupportedAppUsage /*package*/ Handler target; @UnsupportedAppUsage /*package*/ Runnable callback; public void dispatchMessage(@NonNull Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } private static void handleCallback(Message message) { message.callback.run(); } public interface Callback { /** * @param msg A {@link android.os.Message Message} object * @return True if no further handling is desired */ boolean handleMessage(@NonNull Message msg); } /** * Subclasses must implement this to receive messages. */ public void handleMessage(@NonNull Message msg) { }
Looper循環經過queue.next()獲取到一條消息,再經過Handler的dispatchMessage()分派處理。
消息隊列MQ。主要列出Looper和Handler中提到的幾個關於MQ的重要過程。
消息隊列是單鏈表實現的,這屬於數據結構,瞭解的話能夠參考數據結構之隊列(Queue)。
boolean enqueueMessage(Message msg, long when) { //Handler爲空 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(TAG, e.getMessage(), e); msg.recycle(); return false; } //下面開始是正在入隊操做 msg.markInUse();//標記使用中 msg.when = when; Message p = mMessages;//當前消息,也是隊首便可以理解爲第一個消息 boolean needWake; //隊列是空或者msg比隊列中其餘消息要先執行,該msg做爲隊首入隊。 if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. //加入隊首。p是指向以前隊首的,瞭解隊列鏈表實現很容易理解 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循環,跳出時:p指向null,prev指向隊尾最後一個消息,即msg最後執行。 //或者p指向第一個when大於msg的消息,prev則指向前面一個(最後一個when小於msg的消息) for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } //msg插入到對應的位置 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; }
Handler發送消息,將消息加入了消息隊列,即上面的enqueueMessage的方法。
這個方法不難理解,能夠看添加的中文註釋。
這裏主要注意的是消息的處理時間,看入隊邏輯 能夠看出消息隊列是按消息處理時間排隊的。
@UnsupportedAppUsage Message next() { ...... for (;;) { ...... 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) { //尚未到消息處理時間,設置阻塞時間nextPollTimeoutMillis,進入下次循環的時候會調用nativePollOnce(ptr, nextPollTimeoutMillis) 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 { //沒有消息要處理,nextPollTimeoutMillis設置爲-1。 // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. //消息隊列退出,返回null if (mQuitting) { dispose(); return null; } ...... } ...... } }
這個方法比較複雜,代碼比較長。上面只截取了部分關鍵代碼,能夠看下添加的中文註釋,可以理解。
注意兩個地方:
最後來看下消息隊列的退出
void quit(boolean safe) { if (!mQuitAllowed) { throw new IllegalStateException("Main thread not allowed to quit."); } synchronized (this) { if (mQuitting) { return; } mQuitting = true; if (safe) { removeAllFutureMessagesLocked(); } else { removeAllMessagesLocked(); } // We can assume mPtr != 0 because mQuitting was previously false. nativeWake(mPtr); } } private void removeAllFutureMessagesLocked() { final long now = SystemClock.uptimeMillis(); Message p = mMessages; if (p != null) { if (p.when > now) { removeAllMessagesLocked(); } else { Message n; for (;;) { n = p.next; if (n == null) { return; } if (n.when > now) { break; } p = n; } p.next = null; do { p = n; n = p.next; p.recycleUnchecked(); } while (n != null); } } } private void removeAllMessagesLocked() { Message p = mMessages; while (p != null) { Message n = p.next; p.recycleUnchecked(); p = n; } mMessages = null; }
這個也不復雜,簡單關注兩點:
Handler使用,通常是子線程進入主線程更新UI。下面是常見的操做。
主要注意Hanler的建立(多種方式的選擇)以及回調的處理,發送消息的方式。
private final String TAG = "HandlerActivity"; private final int MAIN_HANDLER_1 = 1; private Handler mMainHandler = new Handler() { @Override public void handleMessage(@NonNull Message msg) { super.handleMessage( msg ); switch (msg.what) { case MAIN_HANDLER_1: //Do something. like Update UI break; } } };; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate( savedInstanceState ); Log.d( TAG, "onCreate: MainHandler Looper=" + mMainHandler.getLooper() ); SubThread subThread = new SubThread(); subThread.start(); } private class SubThread extends Thread { @Override public void run() { //Do something Message message = mMainHandler.obtainMessage(); message.what = MAIN_HANDLER_1; mMainHandler.sendMessage(message); } }
Message內部保存了一個緩存的消息池,咱們能夠經過Message.obtain()或者mMainHandler.obtainMessage()從緩存池得到一個消息對象。避免每次建立Message帶來的資源佔用。
Message.obtain()的多種方法以及mMainHandler.obtainMessage()最終都是調用obtain()從消息池中獲取一個消息對象。
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(); }
Handler的一個重要做用就是子線程進入主線程更新UI。
Android中的多進程、多線程也提到過2種
Activity.runOnUiThread(Runnable);View.post(Runnable)/View.postDelayed(Runnable, long)。
這2種方法其實就是Handler機制實現的。
Activity.java public final void runOnUiThread(Runnable action) { if (Thread.currentThread() != mUiThread) { mHandler.post(action); } else { action.run(); } } View.java public boolean post(Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { return attachInfo.mHandler.post(action); } // Postpone the runnable until we know on which thread it needs to run. // Assume that the runnable will be successfully placed after attach. getRunQueue().post(action); return true; } private HandlerActionQueue getRunQueue() { if (mRunQueue == null) { mRunQueue = new HandlerActionQueue(); } return mRunQueue; } @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) void dispatchAttachedToWindow(AttachInfo info, int visibility) { ...... // Transfer all pending runnables. if (mRunQueue != null) { mRunQueue.executeActions( info.mHandler ); mRunQueue = null; } ...... } HandlerActionQueue.java public void executeActions(Handler handler) { synchronized (this) { final HandlerAction[] actions = mActions; for (int i = 0, count = mCount; i < count; i++) { final HandlerAction handlerAction = actions[i]; handler.postDelayed(handlerAction.action, handlerAction.delay); } mActions = null; mCount = 0; } }
ActivityThread中的main()是主線程的入口。
從下面代碼中能夠看出來,應用啓動 主線程默認建立了Looper,它是不可退出的。Looper有單獨保存並獲取主線程Looper的方法。
主線程Looper建立參數爲false(prepare(false)),即looper()的循環是不會中止的,當沒有消息時,一直是阻塞的。
Run|Debug public static void main(String[] args) { ...... Looper.prepareMainLooper(); ...... // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); /// M: ANR Debug Mechanism mAnrAppManager.setMessageLogger(Looper.myLooper()); Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); } Looper.java 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()死循環爲何沒致使ANR?ANR具體什麼形成?ANR和Looper有什麼關係? ---這篇已通過長,這些問題在ANR部分總結更好。