亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

Android 復(fù)盤——幫你徹底了解消息機制

Baoyuan / 2753人閱讀

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

1. 什么是消息機制

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

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

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

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

(1)安排將來某個時間點執(zhí)行的 MessageRunnables;
(2)在不同于當前的線程上執(zhí)行的操作;

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

在 Android 開發(fā)中,默認子線程是不可以更新 UI 的,這一點可以從 View 的最高層級 ViewRootImpl 類中找到答案

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

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

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

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

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

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

通常,我們在主線程中創(chuàng)建 Handler 的寫法如下:

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

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

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

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

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

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

通過靜態(tài)內(nèi)部類的方式實現(xiàn)一個 Handler,此時內(nèi)部類并不持有外部類對象的應(yīng)用,需要在內(nèi)部類的構(gòu)造方法內(nè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();
    }
});

這時候點擊按鈕的運行效果如下:

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

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

請看代碼:

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

此時點擊按鈕:

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

4. Handler 工作原理

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

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

這就是 Handler 工作的第一步 :

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

因為這里主要講 Handler 的工作流程,創(chuàng)建 Looper 的具體過程放到文章的下面講解。我們只要知道
Looper.prepare() 為當前的線程創(chuàng)建了一個 Looper 對象即可。

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

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

Looper.prepareMainLooper();

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

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

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

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

其次還有 post(),postAtTime(),postDelayed() 方法最終調(diào)用的也都是 sendMessageAtTime() 方法,只是多了一步調(diào)用 getPostMessage(Runnable r, Object token) 將 Runnable 封裝為一個 Message 對象的 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) {
    // 先獲取當前 Handler 中的 MessageQueue,mQueue 在 Looper 的構(gòu)造方法中進行初始化。
    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 里的另一個 enqueueMessage() 方法
    return enqueueMessage(queue, msg, uptimeMillis);
}

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

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

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

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

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

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

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

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

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

如果 callback 對象為空,就檢查 mCallback 是否為空,不為空就執(zhí)行它的定義的 handleMessage() 方法,若沒有 mCallback,最終將直接執(zhí)行我們在繼承 Handler 時自己定義的 handleMessage() 方法中的代碼。

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

/**
 * 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 接口的話,我們可以直接實例化一個 Handler 而不用去實現(xiàn)一個 Handler 的子類,

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

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

5.1)enqueueMessage()

代碼量有點多,要耐心看哦!

boolean enqueueMessage(Message msg, long when) {
    // 檢查當前 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 會被設(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;
        }

        // 標記當前的 msg 正在執(zhí)行
        msg.markInUse();
        // 設(shè)置 msg 的 when 為傳進來的 when 參數(shù),when 是 Message 想要被執(zhí)行的時間
        msg.when = when;
        // 得到當前消息隊列的頭部消息
        Message p = mMessages;
        boolean needWake;
        // 當前消息隊列為空,新消息的觸發(fā)時間為 0,或者新消息的觸發(fā)時間早于消息中第一條消息的觸發(fā)時間
        // 則將新消息插入到隊列的頭部,作為當前消息隊列的第一條消息
        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 {
            // 將新消息插入到當前消息隊列當中,(不是頭部)
            // 通常我們不必喚醒事件隊列,
            // 除非隊列頭部有消息障礙,并且消息是隊列中最早的異步消息。
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            // 開始循環(huán)便利消息隊列,比較新消息和隊列中消息的 when(觸發(fā)事件)的值,將新消息插入到適當位置
            for (;;) {
                // 循環(huán)第一次遍歷時,將當前隊列中的頭部消息賦值給 prev
                prev = p;
                // p 指向隊列中的第二個消息
                p = p.next;
                // 如果下一個消息為空,或者新消息的觸發(fā)時間早于下一個消息,找到了要插入的位置,退出循環(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 就成功的插入了消息隊列當中。
但是除了插入新消息,我們還需要從消息隊列中讀取消息,這又要怎么做呢?

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

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

        nativePollOnce(ptr, nextPollTimeoutMillis);
  
        // 嘗試檢索下一條消息。 如果找到則返回。
        synchronized (this) {
            // 獲取系統(tǒng)從開機到現(xiàn)在到時間
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            // 將隊列中到頭部消息賦值給 msg
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                // msg 不為空,但是這個 msg 沒有 handler,則這個 msg 為柵欄
                // 開始遍歷,指到獲取第一個異步消息
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                // 如果當前時間不到 msg 的觸發(fā)時間,則計算時間差,設(shè)置阻塞超時時長
                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 {
                    // 當前時間到了 msg 的觸發(fā)時間,則獲取消息并返回
                    mBlocked = false;
                    // 如果當前的 msg 不是頭部消息,則上一條消息的 next 指向 msg 的 next
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        // 當前 msg 為頭部消息,則將下一個 msg 設(shè)置為頭部消息
                        mMessages = msg.next;
                    }
                    // msg 的下一個 Message 對象置空,表示從消息隊列中取出來了這條 msg
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    // 標記 msg 正在使用
                    msg.markInUse();
                    return msg;
                }
            } else {
                // 如果沒有消息,則設(shè)置阻塞時長為 -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)時,消息隊列為空,或 當前時間未到消息的觸發(fā)時間,獲取 IdleHandler 的數(shù)量
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
  
            // pendingIdleHandlerCount 的數(shù)量為 0 時,線程會繼續(xù)堵塞
            if (pendingIdleHandlerCount <= 0) {
                // No idle handlers to run.  Loop and wait some more.
                mBlocked = true;
                continue;
            }

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

        // 運行 IdleHandler,只有第一次循環(huá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,確保不會重復(fù)運行它們
        pendingIdleHandlerCount = 0;

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

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

6. Looper 工作原理

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

6.1)ThreadLocal 的工作原理

ThreadLocal 是一個線程內(nèi)存儲數(shù)據(jù)的類,當不同的線程去訪問同一個 ThreadLocal 對象時,獲得的值都是不一樣的,下面用一段代碼來證明

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

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

可見不同線程訪問同一個 ThreadLocal 對象得到的值也是不一樣的。

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

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

在 set 方法中,先獲取當前線程,然后獲取當前線程的 ThreadLocalMap 對象。getMap() 的 和 createMap() 的實現(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ù)組用于存儲數(shù)據(jù)
    private Entry[] table;

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

    // 負載因子,用于擴容
    private int threshold; // Default to 0

    // 設(shè)置負載因子為當然容量大小的 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 當作一個哈希表,它的內(nèi)部用 Entry 存儲相應(yīng)的數(shù)據(jù)。

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

在 ThreadLocal 中調(diào)用 set 方法,實際上會調(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.

    // 首先獲取當前 ThreadLocal 對象的 table 屬性,table 一個 Entry 的數(shù)組
    // Entry 相當于一個 HashMap,存儲了當前 ThreadLocal 對象和 Object 類型的 value 對象
    Entry[] tab = table;
    int len = tab.length;
    // 計算出存儲的位置
    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;
        }
        // 如果 當前 entrt 的 key 為 null,調(diào)用 replaceStaleEntry 方法清楚所有 key 為 null 的數(shù)據(jù)
        if (k == null) {
            replaceStaleEntry(key, value, i);
            return;
        }
    }
        // 都不滿足,就新建一個 Entry 對象
    tab[i] = new Entry(key, value);
    int sz = ++size;
    // ThreadLocalMap 的容量到達閥值后擴容
    if (!cleanSomeSlots(i, sz) && sz >= threshold)
        rehash();
}

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

public T get() {
    // 獲取當前線程
    Thread t = Thread.currentThread();
    // 獲取當前線程的 ThreadLocalMap 對象
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        // 獲取 ThreadLocalMap 中對應(yīng)當前線程的 Entry 對象
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            // 將 Entry 對象中的 value 取出來
            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)系呢?我們知道每一個線程都有自己的 Looper,Looper 的作用域就是當前的線程,Android 系統(tǒng)中便通過 ThreadLocal 對象來存儲不同線程中的 Looper。

Looper 中 prepare() 方法為當前線程創(chuàng)建一個 Looper 對象,我們看一下它的實現(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 對象保存到當前線程的 ThreadLocalMap 當中
    sThreadLocal.set(new Looper(quitAllowed));
}

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

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

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

每個線程都有一個 Looper 對象,在 Looper 對象的初始化過程中,會為當前線程創(chuàng)建一個 MessageQueue,而一個線程中可以有多個 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() {
    // 通過 Thread Local 獲取當前線程的 Looper
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn"t called on this thread.");
    }
    // 獲取當前 Looper 對象的 MessageQueue
    final MessageQueue queue = me.mQueue;

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

    for (;;) {
        // 取出來下一條消息
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        
        // 用 logging 打印日志,默認為 null,可通過 setMessageLogging() 方法來指定
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }
        
        // 開始跟蹤,并寫入跟蹤消息,用于 debug 功能
        final long traceTag = me.mTraceTag;
        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }        
        ...
        ...
        try {
            // // 通過 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ā)消息的過程中線程的身份沒有改變
        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() 方法就是不停的遍歷消息隊列中的消息,當發(fā)現(xiàn)有新的消息時,便調(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() 方法用于返回當前 UI 線程的 Looper,UI 線程的 Looper 在 ActivityThread 的建立時通過調(diào)用
prepareMainLooper() 方法創(chuàng)建。

6.5)quit() 和 quitSafely()

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

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

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

可以看到這兩個方法都調(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;
        }

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

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

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

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

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

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

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

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

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/74312.html

相關(guān)文章

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

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

    jay_tian 評論0 收藏0
  • 「碼個蛋」2017年200篇精選干貨集合

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

    wangtdgoodluck 評論0 收藏0

發(fā)表評論

0條評論

Baoyuan

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<