您好,登錄后才能下訂單哦!
這期內容當中小編將會給大家帶來有關Android 消息處理機制是什么,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
簡介
我們已經知道Android的消息機制處理主要由Handler、Message、MessageQueue、Looper四個類的實現來完成。那么它們之間的關系是怎樣的?
其中,Message是消息主體,它負責存儲消息的各種信息,包括發送消息的Handler對象、消息信息、消息標識等。MessageQueue就是消息隊列,在其內部以隊列的形式維護一組Message(消息)。Handler負責發送和處理消息。Looper負責輪詢消息隊列。
Android消息機制原理
創建線程消息隊列
在Android應用程序中,消息處理程序運行前首先要創建消息隊列(也就是MessageQueue)。在主線程中,通過調用Looper類的靜態成員函數prepareMainLooper()來創建消息隊列。在其他子線程中,通過調用靜態成員函數prepare()來創建。
prepareMainLooper()與prepare()的實現:
/** * 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()} * 用來初始化主線程中的Looper,有Android環境調用,不應該有用戶調用. */ public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } } public static @Nullable Looper myLooper() { return sThreadLocal.get(); } /** 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()}. * 交給用戶自己調用,通過loop()方法開啟消息循環.同時當不需要處理消息時,需要手動調用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)); }
在這兩個函數調用的過程中,sThreadLocal變量都有被使用。這個變量是ThreadLocal類型的,用來保存當前線程中的Looper對象。也就是說在Android應用程序中每創建一個消息隊列,都有一個并且是唯一 一個與之對應的Looper對象。而且我們可以從源碼中看到當對象不唯一時就會拋出異常。
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); //創建消息隊列 mThread = Thread.currentThread(); }
從上面的源碼中可以看到,當Looper對象實例化的過程的同時會創建一個消息隊列。
消息循環過程
在消息隊列建立完成之后,調用Looper對象的靜態成員方法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(); // 在接收消息時有可能阻塞 if (msg == null) { //message為null時,退出消息循環 return; } // This must be in a local variable, in case a UI event sets the logger final Printer logging = me.mLogging; if (logging != null) {...} final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs; final long traceTag = me.mTraceTag; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {...} final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); final long end; try { msg.target.dispatchMessage(msg); //處理消息 end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } if (slowDispatchThresholdMs > 0) { final long time = end - start; if (time > slowDispatchThresholdMs) {...} } if (logging != null) {...} // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) {...} msg.recycleUnchecked(); } }
上面的源碼就是消息循環的過程,只用調用了loop()方法消息循環才開始起作用。當循環開始時:
在消息循環過程中,通過MessageQueue的next()方法提供消息,在沒有信息時進入睡眠狀態,同時處理其他接口。這個過程至關重要,通過next()方法也決定了消息循環是否退出。
Message next() { final long ptr = mPtr; //與native方法相關,當mPtr為0時返回null,退出消息循環 if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; //0不進入睡眠,-1進入書面 for (;;) { if (nextPollTimeoutMillis != 0) { //處理當前線程中待處理的Binder進程間通信請求 Binder.flushPendingCommands(); } //native方法,nextPollTimeoutMillis為-1時進入睡眠狀態 nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { 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; //更新到睡眠狀態 } // 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. //非睡眠狀態下處理IdleHandler接口 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(TAG, "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; } }
消息循環退出過程
從上面可以看到loop()方法是一個死循環,只有當MessageQueue的next()方法返回null時才會結束循環。那么MessageQueue的next()方法何時為null呢?
在Looper類中我們看到了兩個結束的方法quit()和quitSalely()。兩者的區別就是quit()方法直接結束循環,處理掉MessageQueue中所有的消息,而quitSafely()在處理完消息隊列中的剩余的非延時消息(延時消息(延遲發送的消息)直接回收)時才退出。這兩個方法都調用了MessageQueue的quit()方法。
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); // 喚醒消息循環 } }
處理消息隊列中的消息:根據safe標志選擇不同的處理方式。
/** * API Level 1 * 處理掉消息隊列中所有的消息 */ private void removeAllMessagesLocked() { Message p = mMessages; while (p != null) { Message n = p.next; p.recycleUnchecked(); //回收消息資源 p = n; } mMessages = null; } /** * API Level 18 * 處理掉消息隊列中所有的延時消息 */ 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; //由于在消息隊列中按照消息when(執行時間排序,所以在第一個延時消息后的所有消息都是延時消息) do { p = n; n = p.next; p.recycleUnchecked(); //回收消息資源 } while (n != null); } } }
消息發送過程
在Android應用程序中,通過Handler類向線程的消息隊列發送消息。在每個Handler對象中持有一個Looper對象和MessageQueue對象。
public Handler(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(); //獲取Looper對象 if (mLooper == null) {...} mQueue = mLooper.mQueue; //獲取消息隊列 mCallback = callback; mAsynchronous = async; }
在Handler類中,我們可以看到多種sendMessage方法,而它們最終都調用了同一個方法sendMessageAtTime()方法。
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); }
可以看出這兩個方法十分容易理解,就是通過MessageQueue對象調用enqueueMessage()方法向消息隊列中添加消息。
boolean enqueueMessage(Message msg, long when) { // Handler為null 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; 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; }
從源碼可以看出,一個消息插入到消息隊列中需要以下步驟:
消息持有的Handler對象為null,拋出異常;當消息已經被消費,拋出異常;
當消息隊列沒有消息時,直接插入;
當消息隊列存在消息時,通過比較消息的執行時間,將消息插入到相應的位置;
判斷是否需要喚醒消息循環。
消息處理過程
在消息循環過程中,如果有新的消息加入,就開始處理消息。從上面的分析中,我們可以看到在消息循環中,目標消息會調用其Handler對象的dispatchMessage()方法,這個就是處理消息的方法。
/** * Handle system messages here. */ public void dispatchMessage(Message msg) { // 消息Callback接口不為null,執行Callback接口 if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { //Handler Callback接口不為null,執行接口方法 if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); //處理消息 } }
從源碼可以看出,Handler處理消息分為3中情況。
當Looper開始調用loop()時主線程為什么不會卡死
在進行完上面的分析后,我們都知道Looper.loop()進入到了一個死循環,那么在主線程中執行這個死循環為什么沒有造成主線程卡死或者說在主線程中的其他操作還可以順利的進行,比如說UI操作。因為Android應用程序是通過消息驅動的,所以Android應用程序的操作也是通過Android的消息機制來實現的。這個時候就需要分析一下Android程序啟動的入口類ActivityThread。我們都知道當Android程序啟動時在Java層就是以ActivityThread的main()方法為入口的,這也就是我們所說的主線程。
public static void main(String[] args) { ... ... ... Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); //建立Binder通道 (創建新線程),與ActivityManagerService建立鏈接 if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } ... ... ... Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
從ActivityThread的main()方法中我們可以看到Looper的初始化以及消息循環的開始,同時還有一個關鍵的方法attach()與ActivityManagerService建立鏈接,這里建立鏈接是為了之后相應Activity中各種事件的發生。講到這里還涉及到Native層Looper的初始化,在Looper初始化時會建立一個管道來維護消息隊列的讀寫并通過epoll機制監聽讀寫事件(一種IO多路復用機制)。
在我們調試程序時,我們通過函數的調用棧就可以發現其中的道理:
這也印證了開始的那句話——Android應用程序是通過消息來驅動的。
是否所有的消息都會在指定時間開始執行
這個問題的意思是當我們像消息隊列中發送消息時(比如延時1000ms執行一個消息postDelay(action, 1000)),是不是會在1000ms后去執行這個消息。
答案是不一定。我們只Android的消息是按照時間順序保存在消息隊列中的,如果我們向隊列中添加多個消息,比如10000個延時1000ms執行的消息,那么其實最后一個執行的消息和第一個執行的消息的執行時間是不一樣的。
總結
至此Android系統的消息處理機制就分析完畢了。在Android應用程序中消息處理主要分為3個過程:
在使用消息隊列時,主線程中在程序啟動時就會創建消息隊列,所以我們使用主線程中的消息機制時,不需要初始化消息循環和消息隊列。在子線程中我們需要初始化消息隊列,并且注意在不需要使用消息隊列時,應該及時調用Looper的quit或者quitSafely方法關閉消息循環,否則子線程可能一直處于等待狀態。
上述就是小編為大家分享的Android 消息處理機制是什么了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。