1. 程式人生 > >深入理解 Handler 訊息機制

深入理解 Handler 訊息機制

記得很多年前的一次面試中,面試官問了這麼一個問題,你在專案中一般如何實現執行緒切換? 他的本意應該是考察 RxJava 的使用,只是我的答案是 Handler,他也就沒有再追問下去了。在早期 Android 開發的荒蕪時代,Handler 的確承擔了專案中大部分的執行緒切換工作,通常包括子執行緒更新 UI 和訊息傳遞。不光在我們自己的應用中,在整個 Android 體系中,Handler 訊息機制也是極其重要的,不亞於 Binder 的地位。 ActivityThread.java 中的內部類 H 就是一個 Handler,它內部定義了幾十種訊息型別來處理一些系統事件。

Handler 的重要性毋庸置疑,今天就通過 AOSP 原始碼來深入學習 Handler。相關類的原始碼包含註釋均已上傳到我的 Github 倉庫 android_9.0.0_r45 :

Handler.java

Looper.java

Message.java

MessageQueue.java

Handler

Handler 用來發送和處理執行緒對應的訊息佇列 MessageQueue 中儲存的 Message。每個 Handler 例項對應一個執行緒以及該執行緒的訊息佇列。當你建立一個新的 Handler,它會繫結建立它的執行緒和訊息佇列,然後它會向訊息佇列傳送 Message 或者 Runnable,並且在它們離開訊息佇列時執行。

Handler 有兩個主要用途:

  1. 規劃 Message 或者 Runnable 在未來的某個時間點執行
  2. 在另一個執行緒上執行程式碼

以上翻譯自官方註釋。說白了,Handler 只是安卓提供給開發者用來發送和處理事件的,而訊息如何儲存,訊息如何迴圈取出,這些邏輯則交給 MessageQueueLooper 來處理,使用者並不需要關心。但要真正瞭解 Handler 訊息機制,認真讀一遍原始碼就必不可少了。

建構函式

Handler 的建構函式大致上可以分為兩類,先來看第一類:

public Handler() {
    this(null, false);
}

public Handler(Callback callback) {
    this(callback, false);
}

public Handler(Callback callback, boolean async) {
    // 如果是匿名類、內部類、本地類,且沒有使用 static 修飾符,提示可能導致記憶體洩漏
    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());
        }
    }

    // 從當前執行緒的 ThreadLocal獲取 Looper
    mLooper = Looper.myLooper();
    if (mLooper == null) {  // 建立 Handler 之前一定要先建立 Looper。主執行緒已經自動為我們建立。
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue; // Looper 持有一個 MessageQueue
    mCallback = callback; // handleMessage 回撥
    mAsynchronous = async; // 是否非同步處理
}

這一類建構函式最終呼叫的都是兩個引數的方法,引數中不傳遞 Looper,所以要顯式檢查是否已經建立 Looper。建立 Handler 之前一定要先建立 Looper,否則會直接丟擲異常。在主執行緒中 Looper 已經自動建立好,無需我們手動建立,在 ActivityThread.javamain() 方法中可以看到。Looper 持有一個訊息佇列 MessageQueue,並賦值給 Handler 中的 mQueue 變數。Callback 是一個介面,定義如下:

public interface Callback {
    public boolean handleMessage(Message msg);
}

通過構造器引數傳入 CallBack 也是 Handler 處理訊息的一種實現方式。

再回頭看一下在上面的建構函式中是如何獲取當前執行緒的 Looper 的?

 mLooper = Looper.myLooper(); // 獲取當前執行緒的 Looper

這裡先記著,回頭看到 Looper 原始碼時再詳細解析。

看過 Handler 的第一類建構函式,第二類其實就很簡單了,只是多了 Looper 引數而已:

public Handler(Looper looper) {
    this(looper, null, false);
}
    
public Handler(Looper looper, Callback callback) {
    this(looper, callback, false);
}
    
public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

直接賦值即可。

除此之外還有幾個標記為 @hide 的建構函式就不作說明了。

傳送訊息

傳送訊息大家最熟悉的方法就是 sendMessage(Message msg) 了,可能有人不知道其實還有 post(Runnable r) 方法。雖然方法名稱不一樣,但最後呼叫的都是同一個方法。

sendMessage(Message msg)
sendEmptyMessage(int what)
sendEmptyMessageDelayed(int what, long delayMillis)
sendEmptyMessageAtTime(int what, long uptimeMillis)
sendMessageAtTime(Message msg, long uptimeMillis)

幾乎所有的 sendXXX() 最後呼叫的都是 sendMessageAtTime() 方法。

post(Runnable r)
postAtTime(Runnable r, long uptimeMillis)
postAtTime(Runnable r, Object token, long uptimeMillis)
postDelayed(Runnable r, long delayMillis)
postDelayed(Runnable r, Object token, long delayMillis)

所有的 postXXX() 方法都是呼叫 getPostMessage() 將 引數中的 Runnable 包裝成 Message,再呼叫對應的 sendXXX() 方法。看一下 getPostMessage() 的程式碼:

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

private static Message getPostMessage(Runnable r, Object token) {
    Message m = Message.obtain();
    m.obj = token;
    m.callback = r;
    return m;
}

主要是把引數中的 Runnable 賦給 Message 的 callback 屬性。

殊途同歸,傳送訊息的重任最後都落在了 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() 方法
}

Handler 就是一個撒手掌櫃,傳送訊息的任務轉手又交給了 MessageQueue 來處理。

再額外提一點,enqueueMessage() 方法中的引數 uptimeMillis 並不是我們傳統意義上的時間戳,而是呼叫 SystemClock.updateMillis() 獲取的,它表示自開機以來的毫秒數。

MessageQueue

enqueueMessage()

Message 的入隊工作實際上是由 MessageQueue 通過 enqueueMessage() 函式來完成的。

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) { // msg 必須有 target
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) { // msg 不能正在被使用
        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;
}

從原始碼中可以看出來,MessageQueue 是用連結串列結構來儲存訊息的,訊息是按觸發時間的順序來插入的。

enqueueMessage() 方法是用來存訊息的,既然存了,肯定就得取,這靠的是 next() 方法。

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();
        }

        // 阻塞方法,主要是通過 native 層的 epoll 監聽檔案描述符的寫入事件來實現的。
        // 如果 nextPollTimeoutMillis = -1,一直阻塞不會超時。
        // 如果 nextPollTimeoutMillis = 0,不會阻塞,立即返回。
        // 如果 nextPollTimeoutMillis > 0,最長阻塞nextPollTimeoutMillis毫秒(超時),如果期間有程式喚醒會立即返回。
        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.
                // msg.target == null表示此訊息為訊息屏障(通過postSyncBarrier方法傳送來的)
                // 如果發現了一個訊息屏障,會迴圈找出第一個非同步訊息(如果有非同步訊息的話),
                // 所有同步訊息都將忽略(平常傳送的一般都是同步訊息)
                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.
                    // 得到 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(); // 標記 FLAG_IN_USE
                    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.
            // Idle handle 僅當佇列為空或者佇列中的第一個訊息將要執行時才會執行
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                // No idle handlers to run.  Loop and wait some more.
                // 沒有 idle handler 需要執行,繼續迴圈
                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(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 置零保證不再執行
        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;
    }
}

next() 方法是一個死迴圈,但是當沒有訊息的時候會阻塞,避免過度消耗 CPU。nextPollTimeoutMillis 大於 0 時表示等待下一條訊息需要阻塞的時間。等於 -1 時表示沒有訊息了,一直阻塞到被喚醒。

這裡的阻塞主要靠 native 函式 nativePollOnce() 來完成。其具體原理我並不瞭解,想深入學習的同學可以參考 Gityuan 的相關文 Android訊息機制2-Handler(Native層) 。

MessageQueue 提供了訊息入隊和出隊的方法,但它自己並不是自動取訊息。那麼,誰來把訊息取出來並執行呢?這就要靠 Looper 了。

Looper

建立 Handler 之前必須先建立 Looper,而主執行緒已經為我們自動建立了 Looper,無需再手動建立,見 ActivityThread.javamain() 方法:

public static void main(String[] args) {
...
 Looper.prepareMainLooper(); // 建立主執行緒 Looper
...
}

prepareMainLooper()

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

sMainLooper 只能被初始化一次,也就是說 prepareMainLooper() 只能呼叫一次,否則將直接丟擲異常。

prepare()

public static void prepare() {
        prepare(true);
}

private static void prepare(boolean quitAllowed) {
    // 每個執行緒只能執行一次 prepare(),否則會直接丟擲異常
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    // 將 Looper 存入 ThreadLocal
    sThreadLocal.set(new Looper(quitAllowed));
}

主執行緒中呼叫的是 prepare(false),說明主執行緒 Looper 是不允許退出的。因為主執行緒需要源源不斷的處理各種事件,一旦退出,系統也就癱瘓了。而我們在子執行緒呼叫 prepare() 來初始化 Looper時,預設調動的是 prepare(true),子執行緒 Looper 是允許退出的。

每個執行緒的 Looper 是通過 ThreadLocal 來儲存的,保證其執行緒私有。

再回到文章開頭介紹的 Handler 的建構函式中 mLooper 變數的初始化:

mLooper = Looper.myLooper();
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

也是通過當前執行緒的 ThreadLocal 來獲取的。

建構函式

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed); // 建立 MessageQueue
    mThread = Thread.currentThread(); // 當前執行緒
}

再對照 Handler 的建構函式:

public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

其中的關係就很清晰了。

  • Looper 持有 MessageQueue 物件的引用
  • Handler 持有 Looper 物件的引用以及 Looper 物件的 MessageQueue 的引用

loop()

看到這裡,訊息佇列還沒有真正的運轉起來。我們先來看一個子執行緒使用 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();
    }
}

讓訊息佇列轉起來的核心就是 Looper.loop()

public static void loop() {
    final Looper me = myLooper(); // 從 ThreadLocal 中獲取當前執行緒的 Looper
    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;
        }

        ...  // 省略部分程式碼
       

        try {
            msg.target.dispatchMessage(msg); // 通過 Handler 分發 Message
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }

        ...  // 省略部分程式碼

        msg.recycleUnchecked(); // 將訊息放入訊息池,以便重複利用
    }
}

簡單說就是一個死迴圈不停的從 MessageQueue 中取訊息,取到訊息就通過 Handler 來進行分發,分發之後回收訊息進入訊息池,以便重複利用。

從訊息佇列中取訊息呼叫的是 MessageQueue.next() 方法,之前已經分析過。在沒有訊息的時候可能會阻塞,避免死迴圈消耗 CPU。

取出訊息之後進行分發呼叫的是 msg.target.dispatchMessage(msg)msg.target 是 Handler 物件,最後再來看看 Handler 是如何分發訊息的。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) { // callback 是 Runnable 型別,通過 post 方法傳送
        handleCallback(msg);
    } else {
        if (mCallback != null) { // Handler 的 mCallback引數 不為空時,進入此分支
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg); // Handler 子類實現的  handleMessage 邏輯
    }
}

private static void handleCallback(Message message) {
    message.callback.run();
}
  • Message 的 callback 屬性不為空時,說明訊息是通過 postXXX() 傳送的,直接執行 Runnable 即可。
  • Handler 的 mCallback 屬性不為空,說明建構函式中傳入了 Callback 實現,呼叫 mCallback.handleMessage(msg) 來處理訊息
  • 以上條件均不滿足,只可能是 Handler 子類重寫了 handleMessage() 方法。這好像也是我們最常用的一種形式。

Message

之所以把 Message 放在最後說,因為我覺得對整個訊息機制有了一個完整的深入認識之後,再來了解 Message 會更加深刻。首先來看一下它有哪些重要屬性:

int what :訊息標識
int arg1 : 可攜帶的 int 值
int arg2 : 可攜帶的 int 值
Object obj : 可攜帶內容
long when : 超時時間
Handler target : 處理訊息的 Handler
Runnable callback : 通過 post() 傳送的訊息會有此引數

Message 有 public 修飾的建構函式,但是一般不建議直接通過建構函式來構建 Message,而是通過 Message.obtain() 來獲取訊息。

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();
}

sPool 是訊息快取池,連結串列結構,其最大容量 MAX_POOL_SIZE 為 50。obtain() 方法會直接從訊息池中取訊息,迴圈利用,節約資源。當訊息池為空時,再去新建訊息。

recycleUnchecked()

還記得 Looper.loop() 方法中最後會呼叫 msg.recycleUnchecked() 方法嗎?這個方法會回收已經分發處理的訊息,並放入快取池中。

void recycleUnchecked() {
    // Mark the message as in use while it remains in the recycled object pool.
    // Clear out all other details.
    flags = FLAG_IN_USE;
    what = 0;
    arg1 = 0;
    arg2 = 0;
    obj = null;
    replyTo = null;
    sendingUid = -1;
    when = 0;
    target = null;
    callback = null;
    data = null;

    synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}

總結

說到這裡,Handler 訊息機制就全部分析完了,相信大家也對整個機制瞭然於心了。

  • Handler 被用來發送訊息,但並不是真正的自己去傳送。它持有 MessageQueue 物件的引用,通過 MessageQueue 來將訊息入隊。
  • Handler 也持有 Looper 物件的引用,通過 Looper.loop() 方法讓訊息佇列迴圈起來。
  • Looper 持有 MessageQueue 物件應用,在 loop() 方法中會呼叫 MessageQueue 的 next() 方法來不停的取訊息。
  • loop() 方法中取出來的訊息最後還是會呼叫 Handler 的 dispatchMessage() 方法來進行分發和處理。

最後,關於 Handler 一直有一個很有意思的面試題:

Looper.loop() 是死迴圈為什麼不會卡死主執行緒 ?

看起來問的好像有點道理,實則不然。你仔細思考一下,loop() 方法的死迴圈和卡死主執行緒有任何直接關聯嗎?其實並沒有。

回想一下我們經常在測試程式碼時候寫的 main() 函式:

public static void main(){
    System.out.println("Hello World");
}

姑且就把這裡當做主執行緒,它裡面沒有死迴圈,執行完就直接結束了,沒有任何卡頓。但是問題是它就直接結束了啊。在一個 Android 應用的主執行緒上,你希望它直接就結束了嗎?那肯定是不行的。所以這個死迴圈是必要的,保證程式可以一直執行下去。Android 是基於事件體系的,包括最基本的 Activity 的生命週期都是由事件觸發的。主執行緒 Handler 必須保持永遠可以相應訊息和事件,程式才能正常執行。

另一方面,這並不是一個時時刻刻都在迴圈的死迴圈,當沒有訊息的時候,loop() 方法阻塞,並不會消耗大量 CPU 資源。

關於 Handler 就說到這裡了。還記得文章說過執行緒的 Looper 物件是儲存在 ThreadLocal 中的嗎?下一篇文章就來說說 ThreadLocal 是如何儲存 執行緒區域性變數 的。

文章首發微信公眾號: 秉心說 , 專注 Java 、 Android 原創知識分享,LeetCode 題解。

更多最新原創文章,掃碼關注我吧!

相關推薦

深入理解 Handler 訊息機制

記得很多年前的一次面試中,面試官問了這麼一個問題,你在專案中一般如何實現執行緒切換? 他的本意應該是考察 RxJava 的使用,只是我的答案是 Handler,他也就沒有再追問下去了。在早期 Android 開發的荒蕪時代,Handler 的確承擔了專案中大部分的執行緒切換工作,通常包括子執行緒更新 UI 和

深入理解android訊息機制(一)——handler Looper原始碼

android 重要核心知識點,怎麼深刻理解都不為過,本篇部落格從常用api ,Looper Hanldery以及HanlderTread原始碼角度解讀 一 常用api,主執行緒接收處理訊息 private Handler handler = ne

深入理解windows 訊息機制

再例如,按下一個按鈕,他向父視窗傳送的訊息也可以看作是一個控制元件通知訊息;單擊滑鼠所產生的訊息可以由主視窗直接處理,然後交給控制元件視窗處理。其中視窗訊息及控制元件通知訊息主要由視窗類即直接或間接由CWND類派生類處理。相對視窗訊息及控制元件通知訊息而言,命令訊息的處理物件範圍就廣得多,它不僅可以由視窗類處

Android訊息機制——深入理解Handler

Handler是什麼 Handler允許向一個執行緒的訊息佇列傳送和處理Message和Runnable兩種物件。每一個Handler例項與一個執行緒和其訊息佇列關聯。當你新建一個Handler時,它會與建立它的執行緒和這個執行緒的訊息佇列繫結,從這時起,Ha

iOS runtime探究(二): 從runtime開始深入理解OC訊息轉發機制

你要知道的runtime都在這裡 本文主要講解runtime相關知識,從原理到實踐,由於包含內容過多分為以下五篇文章詳細講解,可自行選擇需要了解的方向: 本文是系列文章的第二篇文章從runtime開始: 深入理解OC訊息轉發機制,主要從runtime

handler訊息機制原始碼級深入全解析

首先我們來看看Handler更新UI執行緒一般使用 首先要進行Handler 申明,複寫handleMessage方法( 放在主執行緒中) private Handler handler = new Handler() { @Override p

深入理解Android訊息處理系統——Looper、Handler、Thread

熟悉Windows程式設計的朋友可能知道Windows程式是訊息驅動的,並且有全域性的訊息迴圈系統。而Android應用程式也是訊息驅動的,按道理來說也應該提供訊息迴圈機制。實際上谷歌參考了Windows的訊息迴圈機制,也在Android系統中實現了訊息迴圈機制。Android通過Looper、Handl

Android Handler訊息機制學習

1.概述   Handler允許你傳送和處理Message,以及和執行緒相關聯的Runnable物件。每一個Handler例項都與一個執行緒及該執行緒的MessageQueue相關聯。既當你建立一個Handler時,該Handler必須繫結一個執行緒以及該執行緒的訊息佇列,一旦它被建立,它能把message

從Android6.0原始碼的角度剖析Handler訊息機制原理

ActivityThread通過ApplicationThread和AMS進行程序間通訊,AMS以程序間通訊的方式完成ActivityThread的請求後回撥ApplicationThread中的Binder方法,然後ApplicationThread會向H傳送訊息,H收到訊息後

Android Handler訊息機制原始碼解析

好記性不如爛筆頭,今天來分析一下Handler的原始碼實現 Handler機制是Android系統的基礎,是多執行緒之間切換的基礎。下面我們分析一下Handler的原始碼實現。 Handler訊息機制有4個類合作完成,分別是Handler,MessageQueue,Looper,Message Handl

深入理解js記憶體機制

原文連結:深入理解js記憶體機制 js的記憶體機制在很多前端開發者看來並不是那麼重要,但是如果你想深入學習js,並將它利用好,打造高質量高效能的前端應用,就必須要了解js的記憶體機制。對於記憶體機制理解了以後,一些基本的問題比如最基本的引用資料型別和引用傳遞到底是怎麼回事兒?比如

【Android自助餐】Handler訊息機制完全解析(二)MessageQueue的佇列管理

Android自助餐Handler訊息機制完全解析(二)MessageQueue的佇列管理 Android自助餐Handler訊息機制完全解析二MessageQueue的佇列管理 新增到訊息佇列enqueueMessage 從佇

【Android自助餐】Handler訊息機制完全解析(五)鳥瞰與總結

Android自助餐Handler訊息機制完全解析(五)鳥瞰與總結 Android自助餐Handler訊息機制完全解析五鳥瞰與總結 Message MessageQueue Handler Looper

【Android自助餐】Handler訊息機制完全解析(四)Looper解析

Android自助餐Handler訊息機制完全解析(四)Looper解析 Android自助餐Handler訊息機制完全解析四Looper解析 Looper 初始化prepare 提供loope

深入理解雙親委託機制

JVM設計者把類載入階段中的“通過'類全名'來獲取定義此類的二進位制位元組流”這個動作放到Java虛擬機器外部去實現,以便讓應用程式自己決定如何去獲取所需要的類。實現這個動作的程式碼模組稱為“類載入器”。 1.類與類載入器 對於任何一個類,都需要由載入它的類載入器和這個類來確立其在JVM

Android Handler訊息機制中的諸多疑問

前言 網上總是有很多闡述Android訊息機制的文章,基本上大同小異,都是講Handle,Message,Looper,MessageQueue這四個類會如何協同工作的。但是動腦筋的童鞋們可能總是會有如下的一些疑問,我翻閱了數多微博,很多年了,也沒有看到相關比較

深入理解docker訊號機制以及dumb-init的使用

一、前言 ● 容器中部署的時候往往都是直接執行二進位制檔案或命令,這樣對於容器的作用更加直觀,但是也會出現新的問題,比如子程序的資源回收、釋放、託管等,處理不好,便會成為可怕的殭屍程序 ● 本文主要討論一下docker容器中程序之間訊號處理以及對程序管理的問題 二、環境準備

深入理解Java回收機制

參考:深入理解Java虛擬機器 一、垃圾回收機制的意義 Java語言中一個顯著的特點就是引入了垃圾回收機制,使c++程式設計師最頭疼的記憶體管理的問題迎刃而解,它使得Java程式設計師在編寫程式的時候不再需要考慮記憶體管理。由於有個垃圾回收機制,Java中的物件不

深入理解HTTP訊息頭(一)

初識HTTP訊息頭     但凡搞 WEB 開發的人都離不開HTTP(超文字傳輸協議),而要了解HTTP,除了HTML本身以外,還有一部分不可忽視的就是HTTP訊息頭。 做 過Socket程式設計的人都知道,當我們設計一個通訊協議時,“訊息頭/訊息

Handler訊息機制之流程解析

Hanlder中Messsage的傳送和處理過程 Handler.sendMessage() -->MessageQueue.enqueueMessage() -->Looper.next() -->MessageQueue.next() --