日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

詳解Android 消息處理機制

瀏覽:254日期:2022-09-22 09:32:13

摘要

Android應用程序是通過消息來驅動的,當Android主線程啟動時就會在內部創建一個消息隊列。然后進入一個無限循環中,輪詢是否有新的消息需要處理。如果有新消息就處理新消息。如果沒有消息,就進入阻塞狀態,直到消息循環被喚醒。那么在Android系統中,消息處理機制是怎么實現的呢?在程序開發時,我們經常會使用Handler處理Message(消息)。所以可以知道Handler是個消息處理者,Message是消息主體。除此之外還有消息隊列和消息輪詢兩個角色。它們分別是MessageQueue和Looper,MessageQueue就是消息隊列,Looper負責輪詢消息。

簡介

我們已經知道Android的消息機制處理主要由Handler、Message、MessageQueue、Looper四個類的實現來完成。那么它們之間的關系是怎樣的?其中,Message是消息主體,它負責存儲消息的各種信息,包括發送消息的Handler對象、消息信息、消息標識等。MessageQueue就是消息隊列,在其內部以隊列的形式維護一組Message(消息)。Handler負責發送和處理消息。Looper負責輪詢消息隊列。

詳解Android 消息處理機制

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()方法消息循環才開始起作用。當循環開始時:

獲取當前線程的Looper對象,如果為null,拋出異常; 獲取消息隊列,開始進入消息循環; 從消息隊列中獲取消息(調用MessageQueue的next()方法),如果為null,結束循環;否則,繼續執行; 處理消息,回收消息資源( msg.recycleUnchecked())。

在消息循環過程中,通過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 handlerboolean 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.nextprev.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中情況。

當Message中的callback不為null時,執行Message中的callback中的方法。這個callback時一個Runnable接口。 當Handler中的Callback接口不為null時,執行Callback接口中的方法。 直接執行Handler中的handleMessage()方法。

當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 消息處理機制

這也印證了開始的那句話——Android應用程序是通過消息來驅動的。

是否所有的消息都會在指定時間開始執行

這個問題的意思是當我們像消息隊列中發送消息時(比如延時1000ms執行一個消息postDelay(action, 1000)),是不是會在1000ms后去執行這個消息。答案是不一定。我們只Android的消息是按照時間順序保存在消息隊列中的,如果我們向隊列中添加多個消息,比如10000個延時1000ms執行的消息,那么其實最后一個執行的消息和第一個執行的消息的執行時間是不一樣的。

總結

至此Android系統的消息處理機制就分析完畢了。在Android應用程序中消息處理主要分為3個過程:

啟動Looper中的消息循環,開始監聽消息隊列。 通過Handler發送消息到消息隊列。 通過消息循環調用Handler對象處理新加入的消息。

在使用消息隊列時,主線程中在程序啟動時就會創建消息隊列,所以我們使用主線程中的消息機制時,不需要初始化消息循環和消息隊列。在子線程中我們需要初始化消息隊列,并且注意在不需要使用消息隊列時,應該及時調用Looper的quit或者quitSafely方法關閉消息循環,否則子線程可能一直處于等待狀態。

以上就是詳解Android 消息處理機制的詳細內容,更多關于Android 消息處理機制的資料請關注好吧啦網其它相關文章!

標簽: Android
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
不卡在线一区二区| 美女性感视频久久| 久久精品国产福利| 国产精品亲子伦av一区二区三区| 国产精品亲子伦av一区二区三区| 91麻豆精品激情在线观看最新| 日韩av一级| 亚洲国产专区校园欧美| 欧美一区三区| 国产精品三上| 亚洲人成网站在线在线观看| 中文一区一区三区免费在线观| 亚洲精品黄色| 久久成人av| 999精品一区| 一区二区精彩视频| 美女性感视频久久| 99久久亚洲精品| 亚洲精品看片| 国产99在线| 免费国产亚洲视频| 国产一区二区三区四区五区 | 国产福利一区二区三区在线播放| 精品一区二区三区的国产在线观看| 97精品在线| 日韩av中文字幕一区二区| 亚洲午夜天堂| 久久国产免费看| 99国产精品久久久久久久 | 欧美日韩一二三四| 欧美天堂在线| 亚洲在线观看| 亚洲1234区| 久久国内精品自在自线400部| 久久精品99久久无色码中文字幕| 日本成人在线一区| 少妇久久久久| 久久免费视频66| 国产欧美大片| 日本午夜精品一区二区三区电影 | 粉嫩av一区二区三区四区五区| 亚洲一区av| 中日韩男男gay无套| 99久久亚洲精品蜜臀| 日韩一区二区三区免费播放| 国产精品自拍区| 日韩三区四区| 亚洲精品动态| 日本亚洲欧洲无免费码在线| 99成人在线| 午夜日韩av| 国产在线欧美| 日产精品一区| 欧美+日本+国产+在线a∨观看| 国产欧美69| 国产精品视频3p| 国产精品久久久免费| 日韩精品午夜视频| 日韩高清不卡在线| 国产精品一区二区中文字幕| 91精品尤物| 国产美女久久| 成人午夜亚洲| 亚洲精品一区三区三区在线观看| 裤袜国产欧美精品一区| 久久久久久婷| 在线精品亚洲欧美日韩国产| 日产精品一区| 99国产精品私拍| 亚洲ww精品| 精品女同一区二区三区在线观看| 国产aⅴ精品一区二区三区久久| 日韩免费福利视频| 欧美高清一区| 日本强好片久久久久久aaa| 国产精品日本一区二区不卡视频| 三上亚洲一区二区| 久久不射网站| 国产精品17p| 欧美日韩国产免费观看| 久久中文字幕av| 日韩美女一区二区三区在线观看| 亚洲成人精品| 国产毛片久久久| 欧美日韩国产高清电影| 日韩精选在线| 91亚洲国产| 亚洲深深色噜噜狠狠爱网站 | 亚洲少妇自拍| 精品精品99| 日本欧美在线看| 狠狠躁少妇一区二区三区| 爽爽淫人综合网网站| 麻豆精品久久久| 国产精品人人爽人人做我的可爱| 精品欧美视频| 欧美一区精品| 亚洲免费婷婷| 日韩精品免费一区二区三区| 国产日韩三级| 六月婷婷一区| 久久九九精品| 欧美www视频在线观看| 日韩精品久久久久久| 九九在线精品| 日韩成人亚洲| 国产成人精品亚洲线观看| 亚洲精品极品| 日韩va欧美va亚洲va久久| 久久99性xxx老妇胖精品| 国产一区二区三区自拍| 欧美亚洲综合视频| 久久人人97超碰国产公开结果| 99re国产精品| 欧美激情另类| 日韩精品成人在线观看| 日韩和的一区二在线| 亚洲精品字幕| 精品视频在线一区二区在线| 99久久九九| 美女久久久久久| 国产高清亚洲| 欧美日韩亚洲一区三区| 亚洲资源av| 日韩成人精品一区| 亚洲一二三区视频| 天堂√8在线中文| 美女性感视频久久| 久久99性xxx老妇胖精品| 无码日韩精品一区二区免费| 欧美日韩视频| 99国产成+人+综合+亚洲欧美| 亚洲深夜影院| 激情综合网站| 在线观看精品| 99pao成人国产永久免费视频| 啪啪国产精品| japanese国产精品| 日韩视频二区| 特黄毛片在线观看| 日韩一区亚洲二区| 黄色在线网站噜噜噜| 色一区二区三区四区| 中文精品视频| 亚洲视频电影在线| 香蕉精品视频在线观看| 亚洲深夜影院| 红桃视频欧美| 午夜久久99| 久久精品99国产精品| 精品国产三区在线| 国产高清一区二区| 欧美日韩18| 国产精品福利在线观看播放| 偷拍精品精品一区二区三区| 久久黄色影院| 水蜜桃久久夜色精品一区的特点 | 欧美一区激情| 国产精品亚洲一区二区三区在线观看| 久久久久伊人| 91精品一区二区三区综合| 鲁大师影院一区二区三区| 免费在线欧美黄色| 国产在视频一区二区三区吞精| 国产精品伦理久久久久久| 伊人久久大香线蕉av不卡| 亚洲图片久久| 国产精品精品| 视频一区二区三区入口| 欧美一级久久| 久久久久久久久99精品大| 午夜日韩福利| 国产一区 二区| 日韩一区三区| 中文字幕中文字幕精品| 日韩精品电影| 97se亚洲| 久久久国产精品入口麻豆| 国产一区二区亚洲| 国产专区一区| 美女网站视频一区| 中文在线不卡| 国精品产品一区| 黄色精品网站| 日本在线高清| 一区二区三区国产在线| | 国产在线一区不卡| 日韩精品一区二区三区中文在线| 日产精品一区二区| 日韩av三区| 精品欧美久久| 日韩大片在线| 999国产精品| 99re国产精品| 亚洲精品第一| 国产乱子精品一区二区在线观看| 三级在线观看一区二区| 日韩精品电影| 99国产精品一区二区|