1. 程式人生 > >[轉]Handler的執行機制

[轉]Handler的執行機制

目錄

一. Handler的主要作用

Handler的主要作用:在子執行緒中傳送訊息, 在主執行緒中更新UI。

二. Handler的基本使用

1. Handler.sendMessage(msg)

@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        mTextView.setText(msg.obj + ""); //更新UI
    }
};

//子執行緒傳送訊息
new Thread(new Runnable
() { @Override public void run() { Message message = new Message(); message.obj = "6666"; mHandler.sendMessage(message); }).start();

2. Handler.post(msg)

@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler();

//子執行緒傳送訊息
new
Thread(new Runnable() { @Override public void run() { mHandler.post(new Runnable() { //切換到主執行緒更新UI @Override public void run() { mTextView.setText("123456"); } }); }).start();

三. Handler的執行機制

1. Handler.sendMessage(msg)做了些什麼?

Handler.java裡面的方法(部分關鍵程式碼):
sendMessage()sendMessageDelayed()sendMessageAtTime()enqueueMessage

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

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;
    ...
    return enqueueMessage(queue, msg, uptimeMillis);
}

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

從上面來看,Handler最終呼叫了MessageQueue.enqueueMessage()
注意:
msg.target = this;msg.target 表示當前物件Handler, 後面Looper.loop()用到

MessageQueue.enqueueMessage()

boolean enqueueMessage(Message msg, long when) {
{
        ...
        synchronized (this) {
            ...
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            ...
        }
        return true;
    }
}

從程式碼中慢慢分析,我們可以得出結論:其實MessageQueue.enqueueMessage()方法就是把傳進來的Message訊息物件,按照時間順序、佇列的結構 儲存起來。
在這裡,我們並沒有看到Handler.handleMessage()方法的執行,繼續往下看。

2. 從ActivityThread.main()分析

我們就不慢慢從問題引進來了,直奔主題。

public static void main(String[] args) {
    Looper.prepareMainLooper();  //初始化
    Looper.loop();
}

2.1 Looper.prepareMainLooper()

prepareMainLooper()→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(); //賦值
    }
}

//有且只有一個ThreadLocal物件
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); 

private static void prepare(boolean quitAllowed) {
    //進入ThreadLocal原始碼裡set()和get()可以看出,每個執行緒只儲存一個Looper物件
    //所以每個執行緒只能呼叫一次prepare(),否則會拋異常
    if (sThreadLocal.get() != null) {  
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //ThreadLocal儲存Looper物件
    sThreadLocal.set(new Looper(quitAllowed)); 
}

注意:prepare()方法 這裡有2個關鍵點

  1. sThreadLocal.set() 和 get()方法
  2. Looper.java構造方法new Looper().

1.ThreadLocal.java 的set()和get()

public T get() {
    // Optimized for the fast path.
    Thread currentThread = Thread.currentThread();
    Values values = values(currentThread);
    if (values != null) {
        Object[] table = values.table;
        int index = hash & values.mask;
        if (this.reference == table[index]) {
            return (T) table[index + 1];
        }
    } else {
        values = initializeValues(currentThread);
    }

    return (T) values.getAfterMiss(this);
}

public void set(T value) {
     Thread currentThread = Thread.currentThread();
     Values values = values(currentThread);
     if (values == null) {
         values = initializeValues(currentThread);
     }
     values.put(this, value);
 }

set():給當前執行緒賦值一個:Loop物件
get():獲取當前執行緒的Loop物件

2. Looper.java構造方法

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

主要初始化訊息佇列MessageQueue,即:一個Loop有一個MessageQueue訊息佇列物件

總結:Looper.prepareMainLooper() 主要作用:

  1. 初始化Looper.sThreadLocal.set(new Looper(quitAllowed))
  2. 初始化 MessageQueue物件, new Loop() 對應一個MessageQueue物件

2.2 Looper.loop();

下面是部分的關鍵程式碼

public static void loop() {
{
        final Looper me = myLooper();
        ...
        final MessageQueue queue = me.mQueue;
        ...
        for (;;) {
            Message msg = queue.next(); // might block
            ...
            msg.target.dispatchMessage(msg);
            ...
            msg.recycle();
        }
    }
}

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

程式碼裡面都有註釋,最核心程式碼是msg.target.dispatchMessage(msg)msg.target就是當前的Handler物件,
接下來我們來看Handler.dispatchMessage()

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

這裡就執行了handleMessage()方法

2.3 資源回收處理 msg.recycle()

 private static int sPoolSize = 0;
 private static final int MAX_POOL_SIZE = 50;

public void recycle() {
    clearForRecycle();

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

結論:Msg 物件範圍0到50個
最後圖解,如下圖:
在這裡插入圖片描述

程式碼分析基於kk

參考文章:
https://www.jianshu.com/p/969a4e8c1584