Handler 消息機制

Handler 消息機制是由 Message MessageQueue Handler Looper 共同完成的。

你還在為開發(fā)中頻繁切換環(huán)境打包而煩惱嗎?快來試試 Environment Switcher 吧!使用它可以在app運行時一鍵切換環(huán)境,而且還支持其他貼心小功能,有了它媽媽再也不用擔心頻繁環(huán)境切換了。https://github.com/CodeXiaoMai/EnvironmentSwitcher

Handler 消息機制是用于在同一個進程中的多個線程之間進行通信的。由于工作線程與主線程共享地址空間,即 Handler 實例對象 mHandler 位于線程間共享的內(nèi)存堆上,所以工作線程與主線程都能直接使用該對象,只需要注意多線程的同步問題。工作線程通過 mHandler 向其成員變量 MessageQueue 中添加新 Message,主線程一直處于 loop() 方法中,當收到新的 Message 時按照一定規(guī)則分發(fā)給相應的 handleMessage() 方法來處理。所以說,Handler 消息機制用于同進程的線程間通信的核心是線程間共享內(nèi)存空間,而不同進程擁有不同的地址空間,也就不能用 Handler 來實現(xiàn)進程間通信。

既然是用來通信的消息機制,那用什么來通信呢?當然是 Message,下面就先介紹一下 Message。

Message

Message 默認提供了兩個 int 屬性和一個 object 屬性,能夠滿足我們大多數(shù)情況的需求。

關(guān)于 Message 主要需要注意的是,怎樣正確的創(chuàng)建一個 Message 對象?盡管 Message 的構(gòu)造函數(shù)是公共的,但是最好的方法(效率高,避免重復創(chuàng)建對象)卻是通過調(diào)用 Message.obtain() 或者 Handler.obtainMessage() 方法,從消息池中回收的 Message 中獲取。

Message next;
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;

上面幾個屬性是 Message 中聲明的,每個 Message 對象都可以通過為 next 屬性賦值,添加一個后繼元素,這樣多個 Message 對象就組成了一個單鏈表;而 sPool 就是鏈表的表頭位置的 Message 對象,也就是頭指針。此外我們還可以了解到這個鏈表的最大長度為 50。

那么這個鏈表有什么用呢?

答案就是用來保存已經(jīng)用過的 Message,這個保存是通過 recyclerUnchecked() 方法實現(xiàn)的,而且這個機制是在 Looper 中消息被處理完成之后觸發(fā)的。

public void recycle() {
    if (isInUse()) {
        if (gCheckRecycle) {
            throw new IllegalStateException("This message cannot be recycled because it "
                    + "is still in use.");
        }
        return;
    }
    recycleUnchecked();
}

void recycleUnchecked() {
    flags = FLAG_IN_USE;
    // 將所有數(shù)據(jù)清空
    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) {
            // 如果當前消息池未滿就把 Message 插入到消息池的頭部
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}

現(xiàn)在明白了消息池中的消息是怎么來的,再來看 obtain() 這個方法是怎么從消息池中取出消息的。

public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            // 如果消息池的第一個消息 sPool 不為null,也就意味著消息池中有消息,就將它從消息池中取出來。
            Message m = sPool;
            // 將 sPool 改為消息池的第二個消息
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    // 如果消息池中沒有 Message,就創(chuàng)建新的 Message。
    return new Message();
}

現(xiàn)在已經(jīng)通過正確的方式得到了一個 Message 對象實例,那么我們發(fā)送的消息在哪保存呢?下面就到了 MessageQueue 出場了。

MessageQueue

MessageQueue 的構(gòu)造方法:

// True if the message queue can be quit.
private final boolean mQuitAllowed;

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}

mQuitAllowed 用來標記這個消息隊列是否可以退出,事實也證明這個變量只在 quit() 方法中用到了,主線程是不可以退出的。我們創(chuàng)建的 MessageQueue 都是可以退出的,嚴格來說是必需退出的。因為這個 MessageQueue 是在 Looper 中創(chuàng)建的,而且創(chuàng)建時也確實是傳入的 true,后面在 Looper 中會介紹。

MessageQueue 的兩大主要作用就是:

  • 保存消息:boolean enqueueMessage(Message msg, log when)
  • 取出消息:Message next()

enqueueuMessage()

boolean enqueueMessage(Message msg, long when) {
    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;
        // mMessages 代表鏈表頭結(jié)點的消息
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // 如果 p 為 null 表示 MessageQueue 中沒有消息,如果 when = 0 或者觸發(fā)時間比鏈表頭結(jié)點的消息時間還早,就直接插入鏈表頭部 
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                // 要插入 msg 的前驅(qū)元素
                prev = p;
                // 要插入 msg 的后繼元素
                p = p.next;
                if (p == null || when < p.when) {
                    // p 為 null 表示到達鏈表末尾,when < p.when 表示新 msg 的觸發(fā)時間比 p 的早,插入到它的前面就行了。
                    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 是將每個 Message 按照 when(觸發(fā)時間)進行排列,并存儲到鏈表中,當有新的 Message 需要添加時,從鏈表的第一個 Message 開始循環(huán)遍歷,直到新 Message 的 when 比當前遍歷到的 Message 的 when 要早,就把新 Message 插入到當前遍歷到的 Message 之前,如果沒有遍歷到,就將新的 Message 插入到鏈表尾部。

next()

主要是從 MessageQueue 中取出頭部的消息,具體細節(jié)都是涉及到數(shù)據(jù)結(jié)構(gòu)的操作。

removeMessages()

Handler 中提供了幾個 removeMessages() 和 removeCallbacks() 以及 removeCallbacksAndMessages() 方法,最終都是調(diào)用內(nèi)部的 messageQueue 的 removeMessages() 方法,將消息從 MessageQueue 中移除。

現(xiàn)在 Message 和保存 Message 的 MessageQueue 都了解了,可謂是萬事具備只欠東風了,下面開始發(fā)射。。。

Handler

Handler 允許我們發(fā)送和處理與當前線程關(guān)聯(lián)的 MessageQueue 中的 Message 對象。每個 Handler 實例都與單個線程以及該線程的消息隊列相關(guān)聯(lián)。當創(chuàng)建一個新的 Handler 時,它綁定到創(chuàng)建它的線程和線程的消息隊列上。這樣,它就可以將消息傳遞到消息隊列,并在消息從隊列出來時執(zhí)行它們。

當應用程序創(chuàng)建一個進程時,其主線程將專門運行一個消息隊列,負責管理頂層應用程序?qū)ο螅╝ctivities, broadcast receivers 等)以及它們創(chuàng)建的任何窗口。我們可以創(chuàng)建自己的線程,并通過 Handler 的 post 或 sendMessage 方法與應用程序主線程進行通信,然后 Message 將在 Handler 的 MessageQueue 中進行調(diào)度,并在適當?shù)臅r候進行處理。

要使用 Handler,首先要獲取到 Handler 的實例,因為它沒有提供靜態(tài)的創(chuàng)建實例的方法,所以我們只能通過它的構(gòu)造方法創(chuàng)建。Handler 提供了很多構(gòu)造方法,主要分為兩大類:

第一類構(gòu)造方法

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

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

public Handler(boolean async) {
    this(null, async);
}

public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        // 匿名類、內(nèi)部類或本地類都必須申明為static,否則會警告可能出現(xiàn)內(nèi)存泄露
        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 that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

第一類構(gòu)造方法,都是默認使用當前線程的 Looper 與 Handler 相關(guān)聯(lián),并且可以設(shè)置回調(diào)和是否為異步。Ok,既然有了構(gòu)造方法,我們就可以使用它了。于是我們就這樣創(chuàng)建一個 Handler 對象。

public class SampleActivity extends Activity {

  private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
      // ... 
    }
  }
}

沒問題吧,但是會發(fā)現(xiàn) Android Studio 會發(fā)出如下警告:

This Handler class should be static or leaks might occur (anonymous android.os.Handler).
Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected. If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class; In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.

巴拉巴拉一大堆,其實就是告訴我們這樣創(chuàng)建 Handler 可能會發(fā)生內(nèi)存泄漏。因為在 Java 中,非靜態(tài)內(nèi)部類或匿名類會持有其外部類的引用,導致即使外部類不再使用也不能被垃圾回收機制回收。如果 Handler 是在非主線程中使用 Looper 或 MessageQueue,則沒有問題(不明白為什么,雖然非主線程的 Loop 在 MessageQueue 中沒有消息后就會退出 loop() 方法,但消息沒有處理的時候還是會持有 Activity 的引用啊)。如果 Handler 在使用主線程(主線程的 loop() 方法不會退出)的 Looper 或 MessageQueue,則需要將 Handler 聲明為靜態(tài)類; 在外部類中,實例化 WeakReference 到外部類,并在實例化 Handler 時將此對象傳遞給Handler; 使用WeakReference對象來引用外部類的所有成員。

我們再來捋一遍發(fā)生內(nèi)存泄漏的原因:我們發(fā)送的 Message 對象持有 Activity 中的 Handler 的引用,Handler 又隱式的持有它的外部類(也就是 Activity )的引用。而這個引用會一直存在,直到這個消息被處理,所以垃圾回收機制就沒法回收這個 Activity,內(nèi)存泄露就發(fā)生了。

因此創(chuàng)建 Handler 的正確姿勢如下:

public class SampleActivity extends Activity {

    private static class MyHandler extends Handler {

        private WeakReference<SampleActivity> activityWeakReference;

        private MyHandler(SampleActivity activity) {
            activityWeakReference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            SampleActivity sampleActivity = activityWeakReference.get();
            if (sampleActivity != null) {
                sampleActivity.handleMessage(msg);
            }
        }
    }

    private MyHandler mHandler = new MyHandler(SampleActivity.this);

    private void handlerMessage(Message msg) {
        // 處理消息
    }
}

第二類構(gòu)造方法

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

第二類構(gòu)造方法可以設(shè)置指定的 Looper 與 Handler 相關(guān)聯(lián),當然同樣可以設(shè)置回調(diào)和是否為異步。

現(xiàn)在我們已經(jīng)創(chuàng)建了 Handler 的實例,接下來就可以用它來發(fā)送消息了。

Handler 發(fā)送消息主要分為兩大類:

  • Message 類型的消息
  • Runnable 類型的消息(最終還是轉(zhuǎn)換為 Message 類型的消息)

sendMessage

public final boolean sendEmptyMessage(int what){
    return sendEmptyMessageDelayed(what, 0);
}

public final boolean sendMessage(Message msg){
    return sendMessageDelayed(msg, 0);
}

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}

public final boolean sendMessageDelayed(Message msg, long delayMillis){
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

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

public final boolean sendMessageAtFrontOfQueue(Message msg) {
    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, 0);
}

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

對比上面幾個方法后可以發(fā)現(xiàn)無論是以何種方式發(fā)送消息,最終都是調(diào)用 enqueueMessage() 方法將 Message 保存到當前 Handler 的 MessageQueue 中。

那么 post() 方法呢?

post()

public final boolean post(Runnable r){
   return  sendMessageDelayed(getPostMessage(r), 0);
}

public final boolean postAtTime(Runnable r, long uptimeMillis){
    return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}

public final boolean postAtTime(Runnable r, Object token, long uptimeMillis){
    return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}

public final boolean postDelayed(Runnable r, long delayMillis){
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}

public final boolean postAtFrontOfQueue(Runnable r){
    return sendMessageAtFrontOfQueue(getPostMessage(r));
}

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,其實還是先調(diào)用與之對應的 senMessageXXX()方法,最終調(diào)用 enqueueMessage() 方法。

dispatchMessage

消息發(fā)送后,會進入當前 Handler 的 MessageQueue 中,而 Handler 持有的 MessageQueue 其實就是與當前線程相關(guān)聯(lián)的 Looper 持有的 MessageQueue,Looper 的 loop() 方法,會不斷的從 MessageQueue 中取出消息進行分發(fā),這個分發(fā)機制就是通過調(diào)用 Message 中的 target (其實就是Handler) 的 dispatchMessage() 方法實現(xiàn)的。

Looper.java

public static void loop() {
    ...
    msg.target.dispatchMessage(msg);
    ...
}

Message.java

/*package*/ Handler target;

Handler.java

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    ...
}

通過上面幾段代碼可以看出,msg.target 就是一個 Handler 對象,而這個 target 是在 enqueueMessage() 方法中被賦值的,而且這個值就是 Handler 實例本身。下面就可以看 Handler 的 dispatchMessage() 方法了。

Handler.java

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        // 如果 message 設(shè)置了 callback,也就是 runnable 消息,調(diào)用 callback.run()方法。
        // 源代碼為 handleCallback(msg);實際上 handleCallback(msg) 的具體實現(xiàn)就是下面這行代碼。
        msg.callback.run();
    } else {
        // 如果 handler 設(shè)置了 callback,執(zhí)行 callback 回調(diào)。
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        // 這個方法默認是空的,需要重寫該方法來處理消息。
        handleMessage(msg);
    }
}

從 dispatchMessage 發(fā)放中,還可以看出消息分發(fā)是有三個級別的:

  1. Message 的回調(diào)方法 callback 不為空時,則回調(diào)方法 msg.callback.run()
  2. Handler 的 mCallback 成員變量不為空時,則回調(diào)方法 mCallback.handleMessage(msg)
  3. Handler 自身的回調(diào)方法 handleMessage(),該方法默認為空,我們可以通過重寫該方法來完成具體的邏輯。

現(xiàn)在消息也已經(jīng)發(fā)送了,剩下的就交給 Looper 來處理了。

Looper

Looper 是用于一個線程運行消息循環(huán)的類。每個線程默認情況下沒有與它相關(guān)聯(lián)的消息循環(huán),可以通過調(diào)用 Looper.prepare() 方法創(chuàng)建,創(chuàng)建完成后調(diào)用Lopper.loop()方法開始循環(huán)處理消息。

Looper.prepare()

上面已經(jīng)提過了,可以通過調(diào)用 Looper.prepare() 方法創(chuàng)建 Looper 的實例與一個線程相關(guān)聯(lián),那就先看看這個方法。

Looper提供了兩個prepare()方法:

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

我們只能調(diào)用無參的 prepare()方法(有參的方法是私有的),而且無參的方法內(nèi)部也是調(diào)用有參的方法,并傳入?yún)?shù) true,表示允許退出,false 表示不允許退出。而直接調(diào)用這個私有的構(gòu)造方法,并傳入 false 的地方只有一個,那主是 prepareMainLooper() 方法:

/**
 * 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()}
 */
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

從注釋中也可以看出,這個方法是創(chuàng)建 Application 的主 Looper,由Android系統(tǒng)調(diào)用(ActivityThread.main()和SystemServer.run()),我們不能調(diào)用這個方法。

ThreadLocal

再回到prepare()方法,第一行中的 sThreadLocal 是什么?

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

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 類型的靜態(tài)變量。

什么是 ThreadLocal 呢?

ThreadLocal 是一個用于創(chuàng)建線程局部變量的類。

線程局部變量又是什么呢?

通常情況下,我們創(chuàng)建的變量是可以被任何一個線程訪問并修改的。而使用 ThreadLocal 創(chuàng)建的變量只能被當前線程訪問,其他線程則無法訪問和修改,這就是線程局部變量。

ThreadLocal的特點:

  • Global:在當前線程中,任何地方都可以訪問到 ThreadLocal 的值。
  • Local:該線程的 ThreadLocal 的值只能被該線程自己訪問和修改,一般情況下 其他線程訪問不到。

下面是通過在主線程中創(chuàng)建 ThreadLocal 實例并為其賦值,然后測試子線程能否成功訪問的示例:

public static void main(String[] args) {
    final ThreadLocal<String> threadLocal = new ThreadLocal<>();
    threadLocal.set("hello");
    String s = threadLocal.get();
    System.out.printf("當前線程 %s %s\n", Thread.currentThread().getName(), s);
    new Thread() {
        @Override
        public void run() {
            super.run();
            String s = threadLocal.get();
            System.out.printf("當前線程 %s %s\n", Thread.currentThread().getName(), s);
        }
    }.start();
}

打印結(jié)果:

當前線程 main hello
當前線程 Thread-0 null

從上面的輸出可以證明:在主線程創(chuàng)建的 ThreadLocal 可以在主線程中獲取到它的值,而在子線程中,就不能獲取到了。

上面說一般情況下 ThreadLocal 的值只能被當前線程訪問,那么當然就存在特殊情況了,我們可以使用 ThreadLocal 的子類 InheritableThreadLocal 實現(xiàn)在子線程中訪問主線程中創(chuàng)建的值。

final ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
threadLocal.set("hello");
System.out.printf("當前線程 %s %s\n", Thread.currentThread().getName(), threadLocal.get());
new Thread() {
    @Override
    public void run() {
        super.run();
        System.out.printf("當前線程 %s %s\n", Thread.currentThread().getName(), threadLocal.get());
    }
}.start();

打印結(jié)果:

當前線程 main hello
當前線程 Thread-0 hello

結(jié)果確實是主線程和子線程都可以訪問。

ThreadLocal 怎樣實現(xiàn)線程局部變量

我們使用 ThreadLocal 的目的就是通過它的線程局部變量這個特性,保證數(shù)據(jù)不能被其他線程訪問和修改。

這里既然關(guān)系到數(shù)據(jù)的訪問與修改,那么必然就聯(lián)系到了 ThreadLocal 內(nèi)部的 set() 和 get() 方法了。

ThreadLocal.java

/**
 * Sets the current thread's copy of this thread-local variable
 * to the specified value.  Most subclasses will have no need to
 * override this method, relying solely on the {@link #initialValue}
 * method to set the values of thread-locals.
 *
 * @param value the value to be stored in the current thread's copy of
 *        this thread-local.
 */
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

/**
 * Returns the value in the current thread's copy of this
 * thread-local variable.  If the variable has no value for the
 * current thread, it is first initialized to the value returned
 * by an invocation of the {@link #initialValue} method.
 *
 * @return the current thread's value of this thread-local
 */
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();
}

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

Thread.java

/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. 
 */
ThreadLocal.ThreadLocalMap threadLocals = null;

通過 set() 和 get() 方法的前兩行代碼,可以看到它們都是首先獲取當前線程的實例,然后再獲取當前線程的 threadLocals 屬性,最后才去對這個 threadLocals 進行賦值或取值操作。這樣就保證了每個線程操作的只是它自己的 threadLocals,從而實現(xiàn)線程局部變量的效果。

Looper 的構(gòu)造方法

現(xiàn)在已經(jīng)明白了 ThreadLocal 的實現(xiàn)原理,再次回到 prepare() 方法。

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

當我們第一次調(diào)用 sThreadLocal.get() 方法時,得到的肯定是 null,所以就向 sThreadLocal 中賦值一個 Looper 對象。

下面看一下 Looper 的構(gòu)造方法:

final MessageQueue mQueue;
final Thread mThread;

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

在構(gòu)造方法中可以看到,分別為 mQueue 和 mThread 賦值,而且 quitAllowed 也傳遞給 MessageQueue 的構(gòu)造方法,還記得前面在 MessageQueue 中已經(jīng)介紹過了嗎?而且我們創(chuàng)建的 MessageQueue 都是必須退出的,只有主線程才不可以也不能退出。

其實 Looper 只有這一個私有的構(gòu)造方法,這再一次證明我們不能直接創(chuàng)建 Looper 的實例,而是應該通過調(diào)用 Looper.prepare() 方法創(chuàng)建。

到此為止,我們通過調(diào)用 Looper.prepare() 方法已經(jīng)創(chuàng)建了一個與當前線程綁定,并通過 ThreadLocal 保證每個線程只有一個的 Looper 實例,同時這個 Looper 實例持有一個 MessageQueue 對象實例。

Looper.loop()

現(xiàn)在有了 MessageQueue 的實例,我們就可以調(diào)用 Looper.loop() 循環(huán)處理消息了。

下面是精簡過的 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;
    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);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        msg.recycleUnchecked();
    }
}

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

首先從 sThreadLocal 中獲取當前線程唯一的 Looper 實例 me,然后得到這個 Looper 實例的 MessageQueue 實例,接著就開始無限循環(huán)處理消息了。每當?shù)玫揭粋€ Message 實例,只要不為空就調(diào)用 msg.target.dispatchMessage(msg) 開始分發(fā)消息。

總結(jié)

  • 創(chuàng)建一個 Message 的正確方式是:Message.obtain() 或 Handler.obtain()
  • 創(chuàng)建 Handler 時要注意避免內(nèi)存泄漏
  • Looper 的 prepare() 方法,將 Looper 實例與當前線程綁定,通過 ThreadLocal 保證每個線程只有一個 Looper 實例,同時一個 Looper 實例也只有一個 MessageQueue 實例.
  • Looper 的 loop() 方法,不斷從 MessageQueue 中取出 message 對象,并調(diào)用 message.target.dispatchMessage() 方法分發(fā)處理。

如果上面的內(nèi)容都理解了,就通過下面這個問題檢測一下吧!

我們平時都是使用 Handler 在子線程發(fā)送消息、主線程中接收與處理消息,那么 Handler 可以在主線程中發(fā)送消息,在子線程中接收與處理消息嗎?如果可以怎么實現(xiàn)呢?

答案是可以的。因為我們通過 Handler 發(fā)送的 Message,都會保存到與它相關(guān)聯(lián)的 Looper 的 MessageQueue 中,Looper 的 loop() 方法會不斷循環(huán)取出 MessageQueue 中的 Message 并調(diào)用 message.target.dispatchMessage() 方法進行分發(fā)處理。

還記得 Handler 怎么與 Looper 關(guān)聯(lián),Looper 又是怎樣與線程關(guān)聯(lián)的嗎?再來回顧一下。

Handler 除了可以與創(chuàng)建它的線程相關(guān)聯(lián)的 Looper 相關(guān)聯(lián)外,還可以與指定的 Looper 相關(guān)聯(lián),因此我們可以直接指定子線程的Looper 與 Handler 關(guān)聯(lián)。但是,因為每個線程默認情況下沒有與它相關(guān)聯(lián)的 Looper,所以需要在子線程中先調(diào)用 Looper.prepare() 方法將 Looper 與子線程關(guān)聯(lián),創(chuàng)建完成后就可以調(diào)用Lopper.loop()方法開始循環(huán)處理消息了。

下面是詳細代碼:

public class SampleActivity extends Activity {

    private static class MyThread extends Thread {

        private WeakReference<SampleActivity> activityWeakReference;
        private MyHandler mHandler;

        private MyThread(SampleActivity activity) {
            activityWeakReference = new WeakReference<>(activity);
        }

        @Override
        public void run() {
            super.run();
            
            Looper.prepare();

            SampleActivity sampleActivity = activityWeakReference.get();
            if (sampleActivity != null) {
                mHandler = new MyHandler(sampleActivity);
            }

            Looper.loop();
        }
    }

    private static class MyHandler extends Handler {

        private WeakReference<SampleActivity> activityWeakReference;

        private MyHandler(SampleActivity activity) {
            activityWeakReference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            SampleActivity sampleActivity = activityWeakReference.get();
            if (sampleActivity != null) {
                sampleActivity.handleMessage(msg);
            }
        }
    }

    private void handleMessage(Message message) {
        Log.e("handlerMessage", "currentThread:" + Thread.currentThread().getName() + "message.what:" + message.what);
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Log.e("onCreate", "currentThread:" + Thread.currentThread().getName());
        // 創(chuàng)建子線程
        final MyThread myThread = new MyThread(this);
        // 開啟子線程接收消息
        myThread.start();

        // 創(chuàng)建一個發(fā)送消息的線程
        new Thread() {
            @Override
            public void run() {
                super.run();

                while (true) {
                    SystemClock.sleep(3000);
                    myThread.mHandler.sendEmptyMessage(new Random().nextInt(10));
                }
            }
        }.start();
    }
}

打印結(jié)果:

E/onCreate: currentThread:main
E/handleMessage: currentThread:Thread-9430,message.what:9
E/handleMessage: currentThread:Thread-9430,message.what:2
E/handleMessage: currentThread:Thread-9430,message.what:7
E/handleMessage: currentThread:Thread-9430,message.what:7
E/handleMessage: currentThread:Thread-9430,message.what:9
E/handleMessage: currentThread:Thread-9430,message.what:3
E/handleMessage: currentThread:Thread-9430,message.what:5
E/handleMessage: currentThread:Thread-9430,message.what:2
E/handleMessage: currentThread:Thread-9430,message.what:3
E/handleMessage: currentThread:Thread-9430,message.what:6

很明顯可以看出消息是在子線程中接收與處理的。

延伸

再來一個問題,Android 中什么情況下會在子線程中發(fā)送消息和處理消息呢?

當然是耗時操作,而耗時操作又使用什么來實現(xiàn)呢? Service,其實 IntentService 就是利用 Handler 機制實現(xiàn)的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • 版權(quán)聲明: 本文來自 書生依舊 的簡書,轉(zhuǎn)載請注明出處。原文鏈接: http://www.itdecent.cn/...
    書生依舊閱讀 2,039評論 1 7
  • 找工作面試中這個問題被問到的概率至少是80%,跟面試官扯一扯Handler消息的原理,會讓面試加分很多的。最近我也...
    ldlywt閱讀 734評論 0 3
  • 消息機制主要包含三個元素:Handler、MessageQueue、Looper 工作原理 Hander被創(chuàng)建后,...
    Jdqm閱讀 351評論 0 0
  • 加班太晚,難得入眠; 上邊瞧瞧,下邊轉(zhuǎn)轉(zhuǎn); 北方擺手,風聲太緊; 東莞搖頭,她不方便; 索性起床,繼續(xù)苦干!
    孤獨乞丐閱讀 176評論 0 0
  • 2017-05-16 感謝貓叔、助理與剽悍一只貓內(nèi)容組分享的剽悍晨讀:好運都是上班路上設(shè)計出來的 一個好的開始 早...
    一粟于海閱讀 452評論 0 1

友情鏈接更多精彩內(nèi)容