成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

Android 復(fù)盤(pán)——幫你徹底了解消息機(jī)制

Baoyuan / 2689人閱讀

摘要:什么是消息機(jī)制說(shuō)到消息機(jī)制,作為一名開(kāi)發(fā)者一定先想到的是。但是,在主線程中創(chuàng)建的時(shí)候,我們并沒(méi)有看到的執(zhí)行,這是因?yàn)樵诰€程,即的創(chuàng)建過(guò)程中,已經(jīng)被創(chuàng)建好了。將新消息插入到之前,頭消息之后。

1. 什么是消息機(jī)制

說(shuō)到消息機(jī)制,作為一名 Android 開(kāi)發(fā)者一定先想到的是 Handler。Handler 就是 Android 消息機(jī)制的上層接口,我們可用通過(guò) Handler 輕松的在不同的線程中切換任務(wù),但 Handler 的實(shí)現(xiàn)還有兩個(gè)很重要的概念 MessageQueueLooper

MessageQueue 的翻譯是消息隊(duì)列,它的內(nèi)部采用了單鏈表的結(jié)構(gòu)存儲(chǔ) Handler 對(duì)象發(fā)送的消息。

Looper 的作用是不斷地查詢(xún) MessageQueue 中是否有消息,如果 Looper 發(fā)現(xiàn) MessageQueue 中存入了新的消息,它就會(huì)去處理這條消息,如果沒(méi)有新消息,Looper 就會(huì)以無(wú)限循環(huán)的方式去查詢(xún) MessageQueue 中是否有新消息。

2. 為什么要有 Handler 2.1)官方文檔中 Handler 的主要作用

(1)安排將來(lái)某個(gè)時(shí)間點(diǎn)執(zhí)行的 MessageRunnables
(2)在不同于當(dāng)前的線程上執(zhí)行的操作;

2.2)Handler 被用來(lái)做的最多的一件事就是更新主線程的 UI。

在 Android 開(kāi)發(fā)中,默認(rèn)子線程是不可以更新 UI 的,這一點(diǎn)可以從 View 的最高層級(jí) ViewRootImpl 類(lèi)中找到答案

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views.");
    }
}

ViewRootImpl 類(lèi)中的 checkThread 方法會(huì)在更新 UI 前被執(zhí)行,如果當(dāng)前線程不是主線程,就會(huì)拋出 Only the original thread that created a view hierarchy can touch its views. 的異常

2.3)那么 Android 為什么要設(shè)計(jì)為只能在主線程中更新 UI 呢?

Android 在子線程中更新 UI 是不安全的,如果多個(gè)子線程同時(shí)修改一個(gè)控件的數(shù)據(jù),后果是不可控的

如果給 UI 更新機(jī)制加鎖,會(huì)降低 UI 的訪問(wèn)效率,并且可能阻塞某些線程的執(zhí)行

3. Handler 的用法 3.1)在主線程中創(chuàng)建 Handler

通常,我們?cè)谥骶€程中創(chuàng)建 Handler 的寫(xiě)法如下:

private Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
    }
};

但這樣寫(xiě),系統(tǒng)會(huì)這樣提示:

This Handler class should be static or leaks might occur (anonymous android.os.Handler)
這個(gè)Handler類(lèi)應(yīng)該是靜態(tài)的,否則可能會(huì)發(fā)生泄漏

出現(xiàn)這個(gè)警告但原因是,Handler 在 Activity 中作為一個(gè)匿名內(nèi)部類(lèi)來(lái)定義,它的內(nèi)部持有來(lái) Activity 的實(shí)例。當(dāng) Activity 被用戶(hù)關(guān)閉時(shí),因?yàn)?Handler 持有了 Activity 的引用,就造成了 Activity 無(wú)法被回收,從而導(dǎo)致了內(nèi)存泄漏。

因此,在這里推薦一種更加安全的寫(xiě)法:

private static class MyHandler extends Handler{
    private WeakReference weakReference;
    public MyHandler(Activity activity){
        weakReference = new WeakReference<>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what){
                case 0:                     
                  Toast.makeText(weakReference.get(),Thread.currentThread().getName(),Toast.LENGTH_SHORT).show();
                  break;
            }
    }
}

private MyHandler handler = new MyHandler(this);

通過(guò)靜態(tài)內(nèi)部類(lèi)的方式實(shí)現(xiàn)一個(gè) Handler,此時(shí)內(nèi)部類(lèi)并不持有外部類(lèi)對(duì)象的應(yīng)用,需要在內(nèi)部類(lèi)的構(gòu)造方法內(nèi)增加一個(gè)外部類(lèi)(Activity)的弱應(yīng)用。這樣,即使 Activity 被關(guān)閉,Activity 也能順利被回收。

onCreate() 中的代碼如下:

btn_0 = findViewById(R.id.btn_0);
btn_0.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        new Thread(){
            @Override
            public void run() {
                super.run();
                Message message = Message.obtain();
                message.what = 0;
                handler.sendMessage(message);
            }
        }.start();
    }
});

這時(shí)候點(diǎn)擊按鈕的運(yùn)行效果如下:

3.2)在子線程中創(chuàng)建 Handler

在官方文檔中 Handler 的主要作用是在不同于當(dāng)前線程的線程中執(zhí)行操作,那么如何用 Handler 解決兩個(gè)子線程之間的通信呢?

請(qǐng)看代碼:

btn_1 = findViewById(R.id.btn_1);
btn_1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        new Thread(){
            @Override
            public void run() {
                super.run();
                Looper.prepare();
                handler = new MyHandler(MainActivity.this);
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Looper.loop();
            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                super.run();
                Message message = Message.obtain();
                message.what = 0;
                handler.sendMessage(message);
            }
        }.start();
     }
});

此時(shí)點(diǎn)擊按鈕:

可見(jiàn)當(dāng)前的處理線程已經(jīng)變成了子線程。

4. Handler 工作原理

如果細(xì)心的觀察代碼,可以看到在子線程中創(chuàng)建 Handler 的時(shí)候調(diào)用了 Looper.prepare()Looper.loop() 兩個(gè)方法。這兩句代碼有什么用呢?

我們暫時(shí)可以把 Looper 理解為消息的管理者,它負(fù)責(zé)從 MessageQueue 中提取出消息,傳遞給 Handler 進(jìn)行處理,每一個(gè) Handler 都必須要有一個(gè) Looper,在 Handler 創(chuàng)建的時(shí)候,它會(huì)自動(dòng)使用當(dāng)前線程的 Looper,而 Looper.prepare() 的作用就是為當(dāng)前線程準(zhǔn)備一個(gè) Looper,Looper.loop() 的作用是開(kāi)始查找當(dāng)前 MessageQueue 中是否有了新的消息。

這就是 Handler 工作的第一步 :

4.1)采用當(dāng)前線程的 Looper 創(chuàng)建 Handler

因?yàn)檫@里主要講 Handler 的工作流程,創(chuàng)建 Looper 的具體過(guò)程放到文章的下面講解。我們只要知道
Looper.prepare() 為當(dāng)前的線程創(chuàng)建了一個(gè) Looper 對(duì)象即可。

但是,在主線程中創(chuàng)建 Handler 的時(shí)候,我們并沒(méi)有看到 Looper.prepare() 的執(zhí)行,這是因?yàn)樵?UI 線程,即 ActivityThread 的創(chuàng)建過(guò)程中,Looper 已經(jīng)被創(chuàng)建好了。

我們可以在 ActivityThread 的 main() 方法中看到這樣一句代碼:

Looper.prepareMainLooper();

這個(gè)方法內(nèi)部也調(diào)用了 Looper.prepare() 為 UI 線程創(chuàng)建了一個(gè) Looper。

4.2)通過(guò) Handler 的 sendMessageAtTime() 方法發(fā)送 Message

為什么是 sendMessageAtTime?不是還有 sendMessage(),sendEmptyMessage()sendEmptyMessageDelayed(),sendEmptyMessageAtTime(),sendMessageDelayed() 這么多方法嗎?

通過(guò)閱讀這些方法的源碼可以發(fā)現(xiàn),這些方法最終調(diào)用的都是 sendMessageAtTime()。

其次還有 post()postAtTime(),postDelayed() 方法最終調(diào)用的也都是 sendMessageAtTime() 方法,只是多了一步調(diào)用 getPostMessage(Runnable r, Object token) 將 Runnable 封裝為一個(gè) Message 對(duì)象的 callback 里。

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

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

那么 sendMessageAtTime() 里的具體操作是什么呢?我們?nèi)ピ创a里一探究竟

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    // 先獲取當(dāng)前 Handler 中的 MessageQueue,mQueue 在 Looper 的構(gòu)造方法中進(jìn)行初始化。
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    // queue 不為空,則執(zhí)行 Handler.java 里的另一個(gè) enqueueMessage() 方法
    return enqueueMessage(queue, msg, uptimeMillis);
}

    
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    // 指定 msg 的 Target 對(duì)象為當(dāng)前的 Handler
    msg.target = this;
    if (mAsynchronous) {
       msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

Handler 中的 enqueueMessage() ,最終會(huì)調(diào)用 MessageQueue.java 中的 enqueueMessage() 方法。

之后,Message 對(duì)象最終傳遞到 MessageQueue 即消息隊(duì)列里中,在消息隊(duì)列里的具體處理邏輯在文章的 MessageQueue 工作原理 部分會(huì)具體解釋。

4.3)Looper 處理消息后調(diào)用 Handler 的 dispatchMessage() 方法

在第二步將消息插入消息隊(duì)列后,Looper 就開(kāi)始遍歷消息隊(duì)列,找到新的消息,再通知 Handler 去執(zhí)行這條消息,調(diào)用的就是 Handler 的 dispatchMessage() 方法。

public void dispatchMessage(Message msg) {
   // msg 的 callback 對(duì)象就是一個(gè) Runnable
   if (msg.callback != null) {
        handleCallback(msg);
    } else {
        // 檢查 mCallback 是否為空,不為空就執(zhí)行它內(nèi)部定義的 handleMessage() 方法
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        // 如果 mCallback 為空,就執(zhí)行在實(shí)例化 Handler 過(guò)程中我們自己定義的 handleMessage() 方法中的內(nèi)容
        handleMessage(msg);
    }
}

dispatchMessage() 方法首先會(huì)檢查 Message 的 Callback 對(duì)象是否為空,callback 就是通過(guò) post() 方法傳遞的 Runnable 對(duì)象,如果 callback 不為空,就去執(zhí)行 handleCallback() 方法。

handleCallback() 方法的實(shí)現(xiàn)也很簡(jiǎn)單,它在內(nèi)部執(zhí)行了 Runnable 的 run() 方法

private static void handleCallback(Message message) {
    message.callback.run();
}

如果 callback 對(duì)象為空,就檢查 mCallback 是否為空,不為空就執(zhí)行它的定義的 handleMessage() 方法,若沒(méi)有 mCallback,最終將直接執(zhí)行我們?cè)诶^承 Handler 時(shí)自己定義的 handleMessage() 方法中的代碼。

Callback 是 Handler 中定義的的一個(gè)接口,它的代碼如下:

/**
 * Callback interface you can use when instantiating a Handler to avoid
 * having to implement your own subclass of Handler.
 */
public interface Callback {
    /**
     * @param msg A {@link android.os.Message Message} object
     * @return True if no further handling is desired
     */
    public boolean handleMessage(Message msg);
}

如果使用 Callback 接口的話,我們可以直接實(shí)例化一個(gè) Handler 而不用去實(shí)現(xiàn)一個(gè) Handler 的子類(lèi),

private Handler mHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        return false;
    }
});
5. MessageQueue 工作原理

我們從上一部分的 MessageQueue.java 中的 enqueueMessage() 方法開(kāi)始入手。

5.1)enqueueMessage()

代碼量有點(diǎn)多,要耐心看哦!

boolean enqueueMessage(Message msg, long when) {
    // 檢查當(dāng)前 msg 的 target 是否為空
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    // msg 如果正在被執(zhí)行,就拋出異常
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    synchronized (this) {
        // 在 quit() 方法中,mQuitting 會(huì)被設(shè)置為 true
        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;
        }

        // 標(biāo)記當(dāng)前的 msg 正在執(zhí)行
        msg.markInUse();
        // 設(shè)置 msg 的 when 為傳進(jìn)來(lái)的 when 參數(shù),when 是 Message 想要被執(zhí)行的時(shí)間
        msg.when = when;
        // 得到當(dāng)前消息隊(duì)列的頭部消息
        Message p = mMessages;
        boolean needWake;
        // 當(dāng)前消息隊(duì)列為空,新消息的觸發(fā)時(shí)間為 0,或者新消息的觸發(fā)時(shí)間早于消息中第一條消息的觸發(fā)時(shí)間
        // 則將新消息插入到隊(duì)列的頭部,作為當(dāng)前消息隊(duì)列的第一條消息
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            // 將當(dāng)前消息的下一條消息指向頭部消息
            msg.next = p;
            // 頭部消息修改為當(dāng)前消息
            mMessages = msg;
            // 當(dāng)阻塞時(shí),需要喚醒
            needWake = mBlocked;
        } else {
            // 將新消息插入到當(dāng)前消息隊(duì)列當(dāng)中,(不是頭部)
            // 通常我們不必喚醒事件隊(duì)列,
            // 除非隊(duì)列頭部有消息障礙,并且消息是隊(duì)列中最早的異步消息。
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            // 開(kāi)始循環(huán)便利消息隊(duì)列,比較新消息和隊(duì)列中消息的 when(觸發(fā)事件)的值,將新消息插入到適當(dāng)位置
            for (;;) {
                // 循環(huán)第一次遍歷時(shí),將當(dāng)前隊(duì)列中的頭部消息賦值給 prev
                prev = p;
                // p 指向隊(duì)列中的第二個(gè)消息
                p = p.next;
                // 如果下一個(gè)消息為空,或者新消息的觸發(fā)時(shí)間早于下一個(gè)消息,找到了要插入的位置,退出循環(huán)
                if (p == null || when < p.when) {
                    break;
                }
                // needWake 為 true,并且 下一條消息是異步的,則不需要喚醒。
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            // 將新消息插入到 p 之前,頭消息之后。
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // 如果需要喚醒,調(diào)用 nativeWake 方法去喚醒
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

執(zhí)行完 enqueueMassage 方法,我們新發(fā)送的 Message 就成功的插入了消息隊(duì)列當(dāng)中。
但是除了插入新消息,我們還需要從消息隊(duì)列中讀取消息,這又要怎么做呢?

5.2)next()
Message next() {
    // 如果消息循環(huán)已退出,并且被丟棄,則返回空。
    // 這個(gè)將在應(yīng)用重啟一個(gè) looper 時(shí)發(fā)生
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }

    // 記錄空閑時(shí)處理的 IdlerHandler 數(shù)量,只在第一次迭代時(shí)為 -1
    // IdleHandler 只在隊(duì)列為空 或者 是頭部消息時(shí)執(zhí)行
    int pendingIdleHandlerCount = -1;
    //  native 層使用的變量,設(shè)置的阻塞超時(shí)時(shí)長(zhǎng),0 為不阻塞,-1 為阻塞
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }

        nativePollOnce(ptr, nextPollTimeoutMillis);
  
        // 嘗試檢索下一條消息。 如果找到則返回。
        synchronized (this) {
            // 獲取系統(tǒng)從開(kāi)機(jī)到現(xiàn)在到時(shí)間
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            // 將隊(duì)列中到頭部消息賦值給 msg
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                // msg 不為空,但是這個(gè) msg 沒(méi)有 handler,則這個(gè) msg 為柵欄
                // 開(kāi)始遍歷,指到獲取第一個(gè)異步消息
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                // 如果當(dāng)前時(shí)間不到 msg 的觸發(fā)時(shí)間,則計(jì)算時(shí)間差,設(shè)置阻塞超時(shí)時(shí)長(zhǎng)
                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 {
                    // 當(dāng)前時(shí)間到了 msg 的觸發(fā)時(shí)間,則獲取消息并返回
                    mBlocked = false;
                    // 如果當(dāng)前的 msg 不是頭部消息,則上一條消息的 next 指向 msg 的 next
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        // 當(dāng)前 msg 為頭部消息,則將下一個(gè) msg 設(shè)置為頭部消息
                        mMessages = msg.next;
                    }
                    // msg 的下一個(gè) Message 對(duì)象置空,表示從消息隊(duì)列中取出來(lái)了這條 msg
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    // 標(biāo)記 msg 正在使用
                    msg.markInUse();
                    return msg;
                }
            } else {
                // 如果沒(méi)有消息,則設(shè)置阻塞時(shí)長(zhǎng)為 -1,直到被喚醒
                nextPollTimeoutMillis = -1;
            }

            // 所有的消息都被處理后,判斷是否退出,并返回 null。
            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.
            // 第一次循環(huán)時(shí),消息隊(duì)列為空,或 當(dāng)前時(shí)間未到消息的觸發(fā)時(shí)間,獲取 IdleHandler 的數(shù)量
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
  
            // pendingIdleHandlerCount 的數(shù)量為 0 時(shí),線程會(huì)繼續(xù)堵塞
            if (pendingIdleHandlerCount <= 0) {
                // No idle handlers to run.  Loop and wait some more.
                mBlocked = true;
                continue;
            }

            // 判斷當(dāng)前空閑時(shí)處理任務(wù)的handler是否是為空,如果為空,就實(shí)例化出新的對(duì)象
            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }

        // 運(yùn)行 IdleHandler,只有第一次循環(huán)時(shí)才會(huì)運(yùn)行
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            // 釋放 IdleHandler 的引用
            mPendingIdleHandlers[i] = null;

            boolean keep = false;
            try {
                // 執(zhí)行 IdleHandler 的方法
                keep = idler.queueIdle();
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }

            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }

        // 重置 IdleHandler 的數(shù)量為 0,確保不會(huì)重復(fù)運(yùn)行它們
        pendingIdleHandlerCount = 0;

        // 在執(zhí)行 IdleHandler 時(shí),一個(gè)新的消息可能插入或消息隊(duì)列中的消息到了觸發(fā)時(shí)間
        // 所以將 nextPollTimeoutMillis 設(shè)為 0,表示不需要阻塞,重新檢查消息隊(duì)列。
        nextPollTimeoutMillis = 0;
    }
}

至此,MessageQueue 的兩個(gè)最重要的方法已經(jīng)分析完了,下面來(lái)看 Looper 如何循環(huán)地從消息隊(duì)列中取出消息。

6. Looper 工作原理

在講 Looper 之前,需要先理解 ThreadLocal 的工作原理

6.1)ThreadLocal 的工作原理

ThreadLocal 是一個(gè)線程內(nèi)存儲(chǔ)數(shù)據(jù)的類(lèi),當(dāng)不同的線程去訪問(wèn)同一個(gè) ThreadLocal 對(duì)象時(shí),獲得的值都是不一樣的,下面用一段代碼來(lái)證明

private ThreadLocal mThreadLocal = new ThreadLocal<>();

btn_1 = findViewById(R.id.btn_1);
btn_1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        new Thread(){
            @Override
            public void run() {
                super.run();
                mThreadLocal.set("Thread_A");
                Log.d("ThreadLocalValue",mThreadLocal.get());
            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                super.run();
                mThreadLocal.set("Thread_B");
                Log.d("ThreadLocalValue",mThreadLocal.get());
            }
        }.start();        
    }
);

我在兩個(gè)線程中分別存入在 mThreadLocal 中存入了不同的值,然后在控制臺(tái)輸出它們的內(nèi)容

可見(jiàn)不同線程訪問(wèn)同一個(gè) ThreadLocal 對(duì)象得到的值也是不一樣的。

ThreadLocal 實(shí)現(xiàn)這種特性的原因也很簡(jiǎn)單,下面來(lái)看它內(nèi)部的 set 方法:

public void set(T value) {
    // 獲取當(dāng)前線程 t
    Thread t = Thread.currentThread();
    // 根據(jù)當(dāng)前線程 t,獲取當(dāng)前線程的 ThreadLocalMap 對(duì)象
    ThreadLocalMap map = getMap(t);
    if (map != null)
        // map 不為空,調(diào)用 ThreadLocalMap 的 set() 方法。
        map.set(this, value);
    else
        // map 為空,則為當(dāng)前線程創(chuàng)建一個(gè)新的 ThreadLocalMap 對(duì)象
        createMap(t, value);
}

在 set 方法中,先獲取當(dāng)前線程,然后獲取當(dāng)前線程的 ThreadLocalMap 對(duì)象。getMap() 的 和 createMap() 的實(shí)現(xiàn)如下:

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

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

那么 ThreadLocalMap 又是什么呢,這里是它的一部分源碼:

static class ThreadLocalMap {
    static class Entry extends WeakReference> {
        /** The value associated with this ThreadLocal. */
        Object value;

        Entry(ThreadLocal k, Object v) {
            super(k);
            value = v;
        }
    }

    // 初始的 table 容量
    private static final int INITIAL_CAPACITY = 16;
  
    // Entry 數(shù)組用于存儲(chǔ)數(shù)據(jù)
    private Entry[] table;

    // table 的大小
    private int size = 0;

    // 負(fù)載因子,用于擴(kuò)容
    private int threshold; // Default to 0

    // 設(shè)置負(fù)載因子為當(dāng)然容量大小的 2 / 3 
    private void setThreshold(int len) {
        threshold = len * 2 / 3;
    }
  
    // 初始化 Entry 數(shù)組
    ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
        table = new Entry[INITIAL_CAPACITY];
        int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
        table[i] = new Entry(firstKey, firstValue);
        size = 1;
        setThreshold(INITIAL_CAPACITY);
    }
}

可以將 ThreadLocalMap 當(dāng)作一個(gè)哈希表,它的內(nèi)部用 Entry 存儲(chǔ)相應(yīng)的數(shù)據(jù)。

在 Thread 的屬性中有 ThreadLocal.ThreadLocalMap threadLocals = null;,所以每一個(gè)線程內(nèi)部,都持有一個(gè) ThreadLocalMap 對(duì)象,系統(tǒng)才可以通過(guò) getMap() 方法獲取當(dāng)前線程的 ThreadLocalMap 對(duì)象。

在 ThreadLocal 中調(diào)用 set 方法,實(shí)際上會(huì)調(diào)用 ThreadLocalMap 中的 set 方法,源碼如下:

// ThreadLocalMap 的 set 方法
private void set(ThreadLocal key, Object value) {

    // We don"t use a fast path as with get() because it is at
    // least as common to use set() to create new entries as
    // it is to replace existing ones, in which case, a fast
    // path would fail more often than not.

    // 首先獲取當(dāng)前 ThreadLocal 對(duì)象的 table 屬性,table 一個(gè) Entry 的數(shù)組
    // Entry 相當(dāng)于一個(gè) HashMap,存儲(chǔ)了當(dāng)前 ThreadLocal 對(duì)象和 Object 類(lèi)型的 value 對(duì)象
    Entry[] tab = table;
    int len = tab.length;
    // 計(jì)算出存儲(chǔ)的位置
    int i = key.threadLocalHashCode & (len-1);

    // 遍歷 tab
    for (Entry e = tab[i];
        e != null;
        e = tab[i = nextIndex(i, len)]) {
        ThreadLocal k = e.get();
        // 如果 tab 中已經(jīng)存在了相同的 key 值,就覆蓋它原有的 value
        if (k == key) {
            e.value = value;
            return;
        }
        // 如果 當(dāng)前 entrt 的 key 為 null,調(diào)用 replaceStaleEntry 方法清楚所有 key 為 null 的數(shù)據(jù)
        if (k == null) {
            replaceStaleEntry(key, value, i);
            return;
        }
    }
        // 都不滿足,就新建一個(gè) Entry 對(duì)象
    tab[i] = new Entry(key, value);
    int sz = ++size;
    // ThreadLocalMap 的容量到達(dá)閥值后擴(kuò)容
    if (!cleanSomeSlots(i, sz) && sz >= threshold)
        rehash();
}

ThreadLocal 中的 get() 方法和 set() 方法一樣,都是對(duì) Thread 中對(duì) ThreadLocalMap 進(jìn)行操作

public T get() {
    // 獲取當(dāng)前線程
    Thread t = Thread.currentThread();
    // 獲取當(dāng)前線程的 ThreadLocalMap 對(duì)象
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        // 獲取 ThreadLocalMap 中對(duì)應(yīng)當(dāng)前線程的 Entry 對(duì)象
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            // 將 Entry 對(duì)象中的 value 取出來(lái)
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

private Entry getEntry(ThreadLocal key) {
    int i = key.threadLocalHashCode & (table.length - 1);
    Entry e = table[i];
    if (e != null && e.get() == key)
        return e;
    else
        return getEntryAfterMiss(key, i, e);
}
6.2)Looper 中的 prepare() 方法

那么 ThreadLocal 和 Looper 有什么關(guān)系呢?我們知道每一個(gè)線程都有自己的 Looper,Looper 的作用域就是當(dāng)前的線程,Android 系統(tǒng)中便通過(guò) ThreadLocal 對(duì)象來(lái)存儲(chǔ)不同線程中的 Looper。

Looper 中 prepare() 方法為當(dāng)前線程創(chuàng)建一個(gè) Looper 對(duì)象,我們看一下它的實(shí)現(xiàn):

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");
    }
    // 將 Looper 對(duì)象保存到當(dāng)前線程的 ThreadLocalMap 當(dāng)中
    sThreadLocal.set(new Looper(quitAllowed));
}

這里再看一下 Looper 的構(gòu)造方法

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

可以看到在一個(gè) Looper 中創(chuàng)建了一個(gè) MessageQueue,這里我們就可以搞清楚 Handler、Looper 和 MessageQueue 的對(duì)應(yīng)關(guān)系了:

每個(gè)線程都有一個(gè) Looper 對(duì)象,在 Looper 對(duì)象的初始化過(guò)程中,會(huì)為當(dāng)前線程創(chuàng)建一個(gè) MessageQueue,而一個(gè)線程中可以有多個(gè) Handler。

6.3)Looper 中的 loop() 方法:

prepare() 調(diào)用后,就是調(diào)用 loop() 方法:

/**
  * Run the message queue in this thread. Be sure to call
  * {@link #quit()} to end the loop.
  */
public static void loop() {
    // 通過(guò) Thread Local 獲取當(dāng)前線程的 Looper
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn"t called on this thread.");
    }
    // 獲取當(dāng)前 Looper 對(duì)象的 MessageQueue
    final MessageQueue queue = me.mQueue;

    // 清空遠(yuǎn)程調(diào)用端進(jìn)程的身份,確保此線程的身份是本地進(jìn)程的身份,并跟蹤該身份令牌
    // 這里主要用于保證消息處理是發(fā)生在當(dāng)前 Looper 所在的線程
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) {
        // 取出來(lái)下一條消息
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        
        // 用 logging 打印日志,默認(rèn)為 null,可通過(guò) setMessageLogging() 方法來(lái)指定
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }
        
        // 開(kāi)始跟蹤,并寫(xiě)入跟蹤消息,用于 debug 功能
        final long traceTag = me.mTraceTag;
        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }        
        ...
        ...
        try {
            // // 通過(guò) Handler 分發(fā)消息
            msg.target.dispatchMessage(msg);
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            if (traceTag != 0) {
                // 停止跟蹤
                Trace.traceEnd(traceTag);
            }
        }
        
        if (logSlowDispatch) {
            showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
        }

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        //  確保在分發(fā)消息的過(guò)程中線程的身份沒(méi)有改變
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }
        // 回收消息,并把消息放入消息池
        msg.recycleUnchecked();
    }
}

可以看到 loop() 方法就是不停的遍歷消息隊(duì)列中的消息,當(dāng)發(fā)現(xiàn)有新的消息時(shí),便調(diào)用 Handler 的 dispatchMessage() 方法。

6.4)getMainLooper()
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}
  /**
    * Returns the application"s main looper, which lives in the main thread of the application.
    */
public static Looper getMainLooper() {
    synchronized (Looper.class) {
        return sMainLooper;
    }
}

getMainLooper() 方法用于返回當(dāng)前 UI 線程的 Looper,UI 線程的 Looper 在 ActivityThread 的建立時(shí)通過(guò)調(diào)用
prepareMainLooper() 方法創(chuàng)建。

6.5)quit() 和 quitSafely()

在子線程中,如果手動(dòng)為其創(chuàng)建了Looper,那么在所有消息處理完成之后應(yīng)該調(diào)用 quit() 方法終止消息循環(huán),不然 Looper 就會(huì)一直處于等待狀態(tài)。

public void quitSafely() {
    mQueue.quit(true);
}

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

可以看到這兩個(gè)方法都調(diào)用了 MessageQueue 中都 quit(boolean safe) 方法,quitSafely 的參數(shù)為 true,quit 的參數(shù)為 false。

void quit(boolean safe) {
    // 主線程不退出消息循環(huán)
    if (!mQuitAllowed) {
        throw new IllegalStateException("Main thread not allowed to quit.");
    }
    synchronized (this) {
        // 如果已經(jīng)退出了,直接 return
        if (mQuitting) {
            return;
        }

        // 標(biāo)記為已經(jīng)退出
        mQuitting = true;
        // 如果 safe 的值為 true,執(zhí)行完當(dāng)前的消息后退出消息循環(huán)
        if (safe) {
            removeAllFutureMessagesLocked();
        } else {
            // 直接退出消息循環(huán)
            removeAllMessagesLocked();
        }
        // We can assume mPtr != 0 because mQuitting was previously false.
        nativeWake(mPtr);
    }
}

quitSafely() 會(huì)等待當(dāng)前消息執(zhí)行完畢后退出消息循環(huán),而 quit() 方法會(huì)直接退出消息循環(huán)。

private void removeAllMessagesLocked() {
    // 獲取當(dāng)前 MessageQueue 的頭部消息
    Message p = mMessages;
    while (p != null) {
        // 循環(huán)遍歷所有的 Message
        Message n = p.next;
        // 回收消息,并把消息放入消息池
        p.recycleUnchecked();
        p = n;
    }
    // 將頭部消息置為空
    mMessages = null;
}

private void removeAllFutureMessagesLocked() {
    // 獲取系統(tǒng)從開(kāi)機(jī)到現(xiàn)在到時(shí)間
    final long now = SystemClock.uptimeMillis();
    // 將當(dāng)前的頭部消息賦值給 p
    Message p = mMessages;
    if (p != null) {
        if (p.when > now) {
            // 如果當(dāng)前頭部消息將要執(zhí)行的時(shí)間大于系統(tǒng)開(kāi)機(jī)到現(xiàn)在的時(shí)間,則執(zhí)行 removeAllMessagesLocked() 方法
            // 清空 MessageQueue 隊(duì)列
            removeAllMessagesLocked();
        } else {
            Message n;
            // 遍歷當(dāng)前的 MessageQueue,直到某個(gè)消息的執(zhí)行時(shí)間小于 now 值(即這個(gè)消息正在執(zhí)行)
            // 將這個(gè)消息的 next 賦值為 null
            for (;;) {
                n = p.next;
                if (n == null) {
                    return;
                }
                if (n.when > now) {
                    break;
                }
                p = n;
            }
            p.next = null;
            // 回收不會(huì)被執(zhí)行的 Message
            do {
                p = n;
                n = p.next;
                p.recycleUnchecked();
            } while (n != null);
        }
    }
}

終于講完了,希望大家能通過(guò)我的文章,徹底理解 Handler 的機(jī)制,但我的能力有限,如果存在錯(cuò)誤的地方,還請(qǐng)指出。

零碎的東西很多,為了方便大家記憶,我把上面的內(nèi)容做成了思維導(dǎo)圖,需要的朋友可以保存下來(lái),偶爾看一下,幫助自己記憶。

歡迎關(guān)注本文作者:

掃碼關(guān)注并回復(fù)「干貨」,獲取我整理的千G Android、iOS、JavaWeb、大數(shù)據(jù)、人工智能等學(xué)習(xí)資源。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/74312.html

相關(guān)文章

  • SegmentFault 技術(shù)周刊 Vol.4 - 這份 Android 有點(diǎn)甜

    摘要:閱讀本期周刊,你將快速入門(mén),開(kāi)啟甜蜜之旅。然則的原理負(fù)責(zé)發(fā)送以及處理消息,創(chuàng)建消息隊(duì)列并不斷從隊(duì)列中取出消息交給,則用于保存消息。 showImg(/img/bVCN99?w=900&h=385); 2016 年 8 月,Android 7.0 Nougat(牛軋?zhí)牵┱桨l(fā)布,那么問(wèn)題來(lái)了,你 Marshmallow 了么(? -? ?) Cupcake、Donut、Gingerbre...

    jay_tian 評(píng)論0 收藏0
  • 「碼個(gè)蛋」2017年200篇精選干貨集合

    摘要:讓你收獲滿滿碼個(gè)蛋從年月日推送第篇文章一年過(guò)去了已累積推文近篇文章,本文為年度精選,共計(jì)篇,按照類(lèi)別整理便于讀者主題閱讀。本篇文章是今年的最后一篇技術(shù)文章,為了讓大家在家也能好好學(xué)習(xí),特此花了幾個(gè)小時(shí)整理了這些文章。 showImg(https://segmentfault.com/img/remote/1460000013241596); 讓你收獲滿滿! 碼個(gè)蛋從2017年02月20...

    wangtdgoodluck 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<