1. 程式人生 > >Android訊息機制原始碼解讀

Android訊息機制原始碼解讀

  • 不要心急,一點一點的進步才是最靠譜的。

讀完本文你將瞭解:

前言

本來我以為自己很瞭解 Handler,在印象中 Android 訊息機制無非就是:

  1. Handler 給 MessageQueue 新增訊息
  2. 然後 Looper 無限迴圈讀取訊息
  3. 再呼叫 Handler 處理訊息

整體的流程有了,但是一直沒有結合原始碼捋一捋。

直到有一天在使用 Handler 傳送訊息時遇到了一個問題:

This message is already in use.

這才去翻了翻原始碼,今天總結一下。

Android 訊息機制主要涉及 4 個類:

  • Message

  • MessageQueue

  • Handler

  • Looper

我們依次結合原始碼分析一下。

Message

“訊息機制”,其中要傳遞的就是 Message,官方對它的描述是:

包含任意型別的物件和描述資訊,可以被髮送給 Handler。

Message 的主要屬性如下:

    //用來標識一個訊息,接收訊息方可以根據它知道這個訊息是做什麼的
    public int what;

    //如果你的訊息要傳遞的資料是整型的,可以直接使用 arg1 和 arg2,而不需要使用構造一個 Bundle
    public int arg1;
    public int arg2;

    //一個任意型別的物件,在使用 Messenger 跨程序傳遞訊息時,通常使用它傳遞給接收者
//在其他場景下我們一般使用 setData() 方法 public Object obj; //負責回覆訊息的 Messenger,有的場景下(比如接受者、傳送者模型)需要使用它 public Messenger replyTo; //當前訊息的標誌,只在被 Messenger 傳遞訊息時使用,其他情況下都是 -1 public int sendingUid = -1; //標識當前訊息是否在被使用 //當一個訊息入隊時這個標誌會被改變,在被重新獲取後重置 //當一個訊息已經在被使用時,二次入隊或者回收會報錯(這就是我前言中提到的錯誤原因)
/*package*/static final int FLAG_IN_USE = 1 << 0; //標識當前 訊息是否是非同步的 /*package*/static final int FLAG_ASYNCHRONOUS = 1 << 1; //在 copyFrom 方法中要清除的標誌 /*package*/static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE; /*package*/int flags; /*package*/long when; //很關鍵的資料部分 /*package*/Bundle data; //傳送和處理訊息關聯的 Handler /*package*/Handler target; //訊息的回撥 /*package*/Runnable callback; //在有些場景下還會以連結串列的形式關聯後一個訊息 /*package*/Message next; //訊息池 private static final Object sPoolSync = new Object(); private static Message sPool; //回收訊息連結串列 private static int sPoolSize = 0; private static final int MAX_POOL_SIZE = 50; private static boolean gCheckRecycle = true; Pasted from: http://book2s.com/java/src/package/android/os/message.html#a940ebe121994bfb7f9629bca79beab6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

可以看到,Message 中比較關鍵的屬性有:

  • 標識幹什麼的 what
  • 兩個簡易的整型資料儲存物件 arg1 arg2
  • 儲存複雜點的物件 Bundle
  • 跨程序通訊繫結資料的 object
  • 與之關聯的 Handler
  • 還有一些標誌位,和訊息池什麼的

如何獲取一個訊息

訊息機制最開始肯定需要構建一個訊息。

雖然 Message 的建構函式是 public 的,但是官方還是建議我們使用以下 2 種方式獲取一個 Message 物件:

  1. Message.obtain()
  2. Handler.obtainMessage()

原因是這兩個方法會從一個訊息回收池裡獲取訊息,而不是新建一個,這樣可以節省記憶體。

Handler.obtainMessage() 也是呼叫的 Message.obtain()

    public final Message obtainMessage() {
        return Message.obtain(this);
    }
  • 1
  • 2
  • 3

去看一下 Message.obtain() 原始碼。

Message.obtain()

Message.obtain() 有 7 個過載方法,基本就是在獲取一個 Message 物件的同時直接賦值:

我們選最複雜的一個看下原始碼:

    public static Message obtain(Handler h, int what, int arg1, int arg2,
            Object obj) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.arg1 = arg1;
        m.arg2 = arg2;
        m.obj = obj;

        return m;
    }
Pasted from: http://book2s.com/java/src/package/android/os/message.html#50de1a696587dcd5ae3c91eb63985af2
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

的確就是在 obtainI() 的基礎上加了一些賦值。

Message.obtain() 原始碼如下:

    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // 獲取一個複用的訊息時,會重置標誌位,之前它是 FLAG_IN_USE
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
Pasted from: http://book2s.com/java/src/package/android/os/message.html#62dadbbac34056e7ad4fad36d757d6ef
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

可以看到,這個方法會獲取前面提到的 private static Message sPool; ,如果 sPool 存在就從複用訊息連結串列頭部取一個訊息,然後重置它的標誌位;如果不存在複用訊息連結串列就新建一個訊息。

那什麼時候往訊息池中新增訊息呢?

訊息的回收利用

訊息的回收在 Message.recyclerUnchecked() 方法:

    void recycleUnchecked() {
        flags = FLAG_IN_USE;    //當前訊息被回收時,會標誌為 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++;
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

可以看到 recycleUnchecked 方法將當前 Message 標誌為 FLAG_IN_USE,這樣如果這個方法在被入隊,就會報錯。此外它還清除了其他資料,然後把這個訊息加入了回收訊息的連結串列中。

recycleUnchecked() 什麼時候會被呼叫呢?

MessageQueueLooper 中都有。

MessageQueue.removeMessages() 方法:

    void removeMessages(Handler h, int what, Object object) {
        if (h == null) {
            return;
        }

        synchronized (this) {
            Message p = mMessages;

            // Remove all messages at front.
            while (p != null && p.target == h && p.what == what
                    && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycleUnchecked();    //這裡呼叫了
                p = n;
            }

            // Remove all messages after front.
            while (p != null) {
                Message n = p.next;
                if (n != null) {
                    if (n.target == h && n.what == what
                            && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycleUnchecked();    //這裡也呼叫了
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }
Pasted from: http://book2s.com/java/src/package/android/os/messagequeue.html#0dbba2b975383e8b440199e5d69c73de
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

Looper.loop() 方法:

    public static void loop() {
        //...

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            //...

            msg.recycleUnchecked();
        }
    }
Pasted from: http://book2s.com/java/src/package/android/os/looper.html
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

至此我們可以看到,一個訊息在被 Looper 處理時或者移出佇列時會被標識為 FLAG_IN_USE,然後會被加入回收的訊息連結串列,這樣我們呼叫 Message.obtain() 方法時就可以從回收的訊息池中獲取一箇舊的訊息,從而節約成本。

MessageQueue

MessageQueue 管理著一個 Message 的列表,Handlers 為它新增訊息,Looper 從中取訊息。

這裡寫圖片描述

MessageQueue 的屬性

    // 佇列是否可以退出
    private final boolean mQuitAllowed;

    @SuppressWarnings("unused")
    private long mPtr; //底層使用的 code

    //訊息連結串列的開頭
    Message mMessages;
    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
    private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;
    private IdleHandler[] mPendingIdleHandlers;
    private boolean mQuitting;
    //指出獲取下一個訊息的方法 next() 是否阻塞
    private boolean mBlocked;

    // Barriers are indicated by messages with a null target whose arg1 field carries the token.
    //後一個屏障的 token 
    private int mNextBarrierToken;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

可以看到,MessageQueue 雖然叫“訊息佇列”,持有的其實是一個訊息連結串列的節點。

何時初始化

MessageQueue 一般不直接訪問,都是通過 Looper.myQueue() 方法獲取一個訊息佇列。

訊息佇列在 Looper 的建構函式中初始化:

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

MessageQueue 的建構函式中傳入一個是否允許中途退出的標誌,然後呼叫 Native 方法初始化。
這篇文章暫不研究 Native 層原始碼。

訊息入隊的過程

訊息入隊的方法是 enqueueMessage() 方法:

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {    //這裡要求訊息必須跟 Handler 關聯
        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) {    
            //之前是空連結串列的時候讀取訊息會阻塞,新新增訊息後喚醒
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            //插入訊息到佇列時,只有在佇列頭部有個屏障並且當前訊息是非同步的時才需要喚醒佇列
            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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

Message.enqueueMessage()方法中會檢查入隊的訊息是否在被使用,如果是的話會報錯:

    if (msg.isInUse()) {    
        throw new IllegalStateException(msg + " This message is already in use.");
    }
  • 1
  • 2
  • 3

現在我們清楚了,文章開頭我遇到的:

This message is already in use.

是因為我二次使用了已經在使用的訊息,在入隊時 MessageQueue 檢查發現後報的錯。

所以每次呼叫 Handler.sendMessage() 時,都必須是 obtain() 或者 new 一個新的 Message 物件才行。

此外,如果訊息佇列已經退出,還新增訊息入隊就會報錯。

訊息出隊的過程

訊息出隊的方法是 MessageQueue.next() 方法:

Message next() {
    //如果訊息的 looper 退出,就退出這個方法
    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 的這個方法
            Binder.flushPendingCommands();
        }

        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            //獲取下一個訊息
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;    //當前連結串列的頭結點
            if (msg != null && msg.target == null) {
                //如果訊息沒有 target,那它就是一個屏障,需要一直往後遍歷找到第一個非同步的訊息
                                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {    //如果這個訊息還沒到處理時間,就設定個時間過段時間再處理
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 訊息是正常的、可以立即處理的
                    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;
            }

            //如果收到退出的訊息,並且所有等待處理的訊息都處理完時,呼叫 Native 方法銷燬佇列
                        if (mQuitting) {
                dispose();
                return null;
            }

            //有訊息等待過段時間執行時,pendingIdleHandlerCount 增加
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                mBlocked = true;
                continue;
            }

            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }

        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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99

可以看到,MessageQueue.next() 方法裡有一個迴圈,在這個迴圈中遍歷訊息連結串列,找到下一個可以處理的、target 不為空的訊息並且執行時間不在未來的訊息,就返回,否則就繼續往後找。

如果有阻塞(沒有訊息了或者只有 Delay 的訊息),會把 mBlocked這個變數標記為 true,在下一個 Message 進隊時會判斷這個message 的位置,如果在隊首就會呼叫 nativeWake() 方法喚醒執行緒!

其中看到個 IdleHandler 是什麼呢?

public static interface IdleHandler {

    //當訊息佇列沒有訊息時會回撥這個方法,阻塞等待有訊息進入
    //返回 true 的話表示喚醒阻塞的執行緒,false 表示移除
    //如果訊息佇列中有訊息等待在將來執行,也會呼叫這個方法
    boolean queueIdle();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

根據原始碼和註釋我們可以知道 IdleHandler 是一個執行緒阻塞時回撥的介面。

MessageQueue 中提供了監聽阻塞回調的註冊和移除介面:

public void addIdleHandler(@NonNull IdleHandler handler) {
    if (handler == null) {
        throw new NullPointerException("Can't add a null IdleHandler");
    }
    synchronized (this) {
        mIdleHandlers.add(handler);
    }
}

public void removeIdleHandler(@NonNull IdleHandler handler) {
    synchronized (this) {
        mIdleHandlers.remove(handler);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

當訊息佇列阻塞時,會回撥這些監聽阻塞的觀察者,告訴他們:我有空了!來找我玩啊!

queueIdle() 中做的操作,它會在 handler 空閒時被呼叫,可以充分利用 handler,我們可以在程式碼裡這樣使用:

        Handler handler = new Handler();
        try {
            Field field = Looper.class.getDeclaredField("mQueue");
            field.setAccessible(true);
            MessageQueue queue = (MessageQueue) field.get(handler.getLooper());
            queue.addIdleHandler(new MessageQueue.IdleHandler() {
                @Override
                public boolean queueIdle() {
                    //這裡做一些操作
                    return true;
                }
            });
        } catch (Exception e) {

        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

Looper

前面介紹了 Android 訊息機制中訊息和訊息佇列,有了傳遞的訊息和儲存的佇列,接下來我們結合原始碼瞭解下進行排程的 Looper。

這裡寫圖片描述

官方文件對 Looper 的介紹:

Looper 是用於執行一個執行緒中的訊息的類。

Looper 的屬性很簡單:

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper;  // 主執行緒中的 Looepr

final MessageQueue mQueue;    //與之管理的訊息佇列
final Thread mThread;    //所在的執行緒

private Printer mLogging;
private long mTraceTag;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

執行緒中預設沒有 Looper,我們需要呼叫 Looper.prepare() 方法為當前執行緒建立一個 Looper,然後就可以呼叫 loop() 方法排程訊息。

樣例程式碼如下:

classLooperThreadextendsThread {
      public Handler mHandler;

      public void run() {
          Looper.prepare();

          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };

          Looper.loop();
      }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

執行緒相關 ThreadLocal

前面講了在一個執行緒中需要呼叫 Looper.prepare() 方法建立一個 Looper:

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));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

可以看到,一個執行緒中只能有一個 Looper。

建立一個或者或許當前執行緒的 Looper 都通過 ThreadLocal,我們來了解下它的主要原始碼。

ThreadLocal.get()ThreadLocal.set() 原始碼:

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);    
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null)
            return (T)e.value;
    }
    return setInitialValue();
}

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

ThreadLocalMap 中持有一個 Entry 的陣列:


private Entry[] table;
static classEntryextendsWeakReference<ThreadLocal> {
    Object value;

    Entry(ThreadLocal k, Object v) {
        super(k);
        value = v;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

可以看到,ThreadLocal 先通過當前執行緒獲取 ThreadLocalMap ,然後在 ThreadLocalMap 中儲存 ThreadLocal 和 資料的關聯。

也就是一個類似 Map

無限迴圈排程

線上程中建立一個 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) {    //當前執行緒必須建立 Looper 才可以執行
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;

    //底層對 IPC 標識的處理,不用關心 
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) {    //無限迴圈模式
        Message msg = queue.next(); //從訊息佇列中讀取訊息,可能會阻塞
        if (msg == null) {    //當訊息佇列中沒有訊息時就會返回,不過這隻發生在 queue 退出的時候
            return;
        }

        //...
        try {
            msg.target.dispatchMessage(msg);    //呼叫訊息關聯的 Handler 處理訊息
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        //...
        msg.recycleUnchecked();    //標記這個訊息被回收
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

可以看到,Looper.loop() 也很簡單,就是呼叫 MessageQueue.next() 方法取訊息,如果沒有訊息的話會阻塞,直到有新的訊息進入或者訊息佇列退出。

拿到訊息後呼叫訊息關聯的 Handler 處理訊息。

可以看到,Looper 並沒有執行訊息,真正執行訊息的還是新增訊息到佇列中的那個 Handler,真應了那句:解鈴還須繫鈴人啊!

如何停止

loop() 原始碼中的註釋就提醒我們,開啟迴圈排程訊息後不要忘記呼叫 quit() 方法結束迴圈。

Looper.quit() 和 quitSafely () 原始碼:

public void quit() {
    mQueue.quit(false);
}


            
           

相關推薦

Android訊息機制原始碼解讀

不要心急,一點一點的進步才是最靠譜的。 讀完本文你將瞭解: 前言 本來我以為自己很瞭解 Handler,在印象中 Android 訊息機制無非就是: Handler 給 MessageQueue 新增訊息 然後 Looper 無限迴圈讀取訊息 再呼叫 H

Android Handler訊息機制原始碼解讀

這個東西在網上已經被很多人寫過了,自己也看過很多文章,大概因為自己比較愚笨一直對此不太理解,最近重新從原始碼的角度閱讀,並且配合著網上的一些相關部落格才算明白了一些 本文從原始碼的角度順著程式碼的執行去原始碼,限於作者的表達能力及技術水平,可能會有些問題,請耐性

Android 訊息機制原始碼分析

我們知道,當應用啟動的時候,android首先會開啟一個主執行緒,主執行緒管理ui控制元件,進行事件分發,當我們要做一個耗時的操作時,如聯網讀取資料,獲取讀取本地較大的檔案的時候,你應該在子執行緒中操作,因為有ui的更新,android主執行緒是執行緒不安全的,如果將更新介

Android訊息機制原始碼解析(Handler)

Android訊息機制,其實也就是Handler機制,主要用於UI執行緒和子執行緒之間互動。眾所周知,一般情況下,出於安全的考慮,所有與UI控制元件的操作都要放在主執行緒即UI執行緒中,而一些耗時操作應當放在子執行緒中。當在子執行緒中完成耗時操作並要對UI控制元

Android Handler訊息機制原始碼解析

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

Android訊息機制原理,仿寫Handler Looper原始碼解析跨執行緒通訊原理--之仿寫模擬Handler(四)

前篇總結:上一篇實現了用Looper管理訊息佇列和訊息迴圈,但是訊息的傳送和訊息處理都是在Looper中進行的。寫一個子執行緒使用這樣的Looper怎麼才能獲取到loop()死迴圈訊息佇列取出的訊息呢?用回撥!callBack! 第四節 仿寫Handler來發送訊息,實現回

Android訊息機制分析:Handler、Looper、MessageQueue原始碼分析

1.前言 關於Handler訊息機制的部落格實際上是非常多的了。 之前也是看別人的部落格過來的,但是過了一段時間之後,一些細節也就忘了。 所以,就自己擼一篇,權當筆記,方便以後翻閱。 這篇文章主要是分析Handler訊息機制原理以及收集一些面試題來講解,

Android訊息機制原理,仿寫Handler Looper原始碼跨執行緒通訊原理--之執行緒間通訊原理(一)

前言:我們都知道Android的執行緒通訊是用Handler、Looper機制實現的,面試也經常問道,網上也有很多文章介紹原始碼但是可能很多小白只是機械是的記憶,回答不清楚原理究竟是怎麼回事。下邊我將一步一步仿寫一個Handler、Looper模擬Android的執行緒間通訊

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

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

Android訊息機制Handler解析(原始碼+Demo)

新建了一個qq群 482543750,歡迎一起學習Android的小夥伴加入。 提供各種Android學習資料,面試資料,Android簡歷模板。 Handler是開發人員在面試過程中最常見的問題之一了,這篇文章將較為全面地對Handler進行解讀,包括原始碼層,

Android訊息機制之Looper、Handler、MessageQueen

Android訊息機制之Looper、Handler、MessageQueen 本篇文章包括以下內容: 前言 Android訊息機制的簡介 Android訊息機制的使用 Android訊息機制的相關概念 Android訊息機制的通訊流程

Android 訊息機制:Handler、MessageQueue 和 Looper

在這篇文章中,我們將會討論 Android 的訊息機制。提到 Handler,有過一些 Android 開發經驗的都應該很清楚它的作用,通常我們使用它來通知主執行緒更新 UI。但是 Handler 需要底層的 MessageQueue 和 Looper 來支援才能運作。這篇文章中,我們將會討論它們三個之間的關

Android訊息機制Handler原理分析

文章目錄 1、App中Handler的使用 2、Java層Handler的原理 2.1 Handler模型 2.2 圖解Handler 2.3 Handler執行緒的典型例項 2.4 Looper 2.5 Handl

Android訊息機制(Handler、MessageQueue和Looper三者的工作原理)

Android的訊息機制主要是指Handler的執行機制以及Handler所附帶的MessageQueue和Looper的工作過程。messagequeue意思是訊息佇列,它內部儲存一組訊息,有插入和刪除的功能,其實內部是以單鏈表的形式來實現佇列功能的。looper的意思是迴圈,它的主要功能是迴

Android訊息機制

概述 Android應用程式是基於訊息驅動的,也就是說,在Android應用程式主執行緒中,所有函式都是在一個訊息迴圈中執行的。Android應用程式主執行緒是一個特殊的執行緒,因為它同時也是UI執行緒以及觸控式螢幕,鍵盤等輸入事件的執行緒。 在Android應

Android訊息機制(Handler、MessageQueue、Looper)詳細介紹

Android的訊息機制其實在android的開發過程中指的也就是Handler的執行機制,這也就引出了android中常見的面試問題: 簡述Handler、Looper、MessageQueue的含義,以及它們之間的關係 簡述Handler的執行機制 說明

從ThreadLocal到Android訊息機制

   我們都知道,android訊息機制,大概就是,每個Thread 都有一個 Looper,然後,Looper迴圈的從MessageQueue 中取出訊息,交給handler處理,但是這個這個過程是怎麼處理的呢,怎麼保證每個執行緒和執行緒的繫結,訊息的處理機制,訊息佇列的存

Android訊息機制Looper與VSync的傳播

#1 主要內容 本文主要簡單記錄在native層Looper訊息機制的相關內容,主要著手於下面幾個問題: (1)訊息機制的原理; (2)VSync是如何藉助訊息機制進行傳播的; 2 Android訊息機制 2.1 應用程序的建立 說起Androi

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

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

Android訊息機制Handler

不忘初心 砥礪前行, Tomorrow Is Another Day ! 本文概要: 對於Handler兩個常見的問題 messageQueue、Looper、Handler之間的關係 前言: Android的訊息機制主要是指Handler執行機制,handler它的作用就是切