Handler機制-源碼分析

[toc]

問題1:Handler機制是怎么實現(xiàn)線程間通信的?

問題2:post(runnable)方法發(fā)送的runnable為什么能在主線程執(zhí)行?

創(chuàng)建

首先,看一下創(chuàng)建Handler的過程:

//Handler.java

final Looper mLooper;
final MessageQueue mQueue;

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

public Handler(@Nullable Callback callback, boolean async) {
    ...
    mLooper = Looper.myLooper();    //獲取當(dāng)前線程的looper實例
    ...
    mQueue = mLooper.mQueue;    //當(dāng)前線程的消息隊列
    mCallback = callback;       //用于處理消息
    mAsynchronous = async;
}

在Handler的構(gòu)建方法中,主要是字段賦值,通過Looper.myLooper()獲取了Looper實例,接下來就看一下Looper.myLooper()的源碼。

//Looper.java

//ThreadLocal用于線程局部變量,值與調(diào)用線程關(guān)聯(lián)
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

/**
 * 返回與當(dāng)前線程關(guān)聯(lián)的Looper對象。如果調(diào)用線程沒有與循環(huán)器關(guān)聯(lián),則返回null。
 */
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

public static void prepare() {
    prepare(true);
}

private static void prepare(boolean quitAllowed) {
    ...
    sThreadLocal.set(new Looper(quitAllowed));
}

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

:ThreadLocal類型用于線程局部變量,它的值將保存在thread.threadLocals字段,get/set時通過Thread.currentThread()方法獲取當(dāng)前線程thread實例來訪問。

Looper.myLooper()返回的looper來自靜態(tài)字段sThreadLocal,而sThreadLocal的值是在Looper.prepare()方法中創(chuàng)建的looper實例。(Looper.prepare()并不是在創(chuàng)建handler時調(diào)用,而是在之前調(diào)用)

  • 主線程已在ActivityThread.main()方法中調(diào)用了Looper.prepareMainLooper()Looper.loop(),無需我們負(fù)責(zé);
  • 在其他線程創(chuàng)建Handler時,必須在new Handler()前,調(diào)用Looper.prepare(),否則會引發(fā)RuntimeException。

分析到這里,可以知道handler.mLooper引用的looper是和線程關(guān)聯(lián)的,是線程局部變量,在Looper中創(chuàng)建的消息隊列自然也和線程關(guān)聯(lián)。所以Handler在創(chuàng)建時便和線程綁定了,由handler發(fā)送的消息,才能在創(chuàng)建handler的線程處理。

發(fā)送消息

handler最常見的用法是sendMessage(msg)post(runnable),先看handler.sendMessage()方法的源碼:

//Handler.java

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

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    //SystemClock.uptimeMillis()返回開機后的毫秒數(shù)
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

//參數(shù)uptimeMillis指定處理消息的時間
public boolean sendMessageAtTime(@NonNull 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);
}

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
                               long uptimeMillis) {
    msg.target = this;      //消息發(fā)送的目標(biāo),表示消息將投遞到當(dāng)前handler處理
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);     //將消息放入隊列
}

經(jīng)過幾層調(diào)用,添加了預(yù)定處理時間,設(shè)置了發(fā)送目標(biāo)后,消息被放入handler.mQueue,之前已經(jīng)分析過這個消息隊列和創(chuàng)建handler的線程綁定。

另外在handler.enqueueMessage()中設(shè)置了msg.target = this,這說明發(fā)送消息和處理消息一定是同一個handler。

然后看一下queue.enqueueMessage()方法。

//MessageQueue.java

private long mPtr;  // 大概相當(dāng)于native層的線程id
Message mMessages;  //鏈表:保存等待處理的消息
private boolean mBlocked;   //指示消息隊列關(guān)聯(lián)的線程是否阻塞
private boolean mQuitting;  //消息隊列是否已廢棄

boolean enqueueMessage(Message msg, long when) {
    ...
    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();      //回收message對象,保存至Message.sPool
            return false;
        }

        msg.markInUse();
        msg.when = when;    //消息預(yù)設(shè)的投遞時間
        Message p = mMessages;
        boolean needWake;   //是否需要喚醒線程
        //根據(jù)when決定消息插入隊列的位置
        if (p == null || when == 0 || when < p.when) {
            // 新的頭,如果阻塞,喚醒事件隊列。
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            // 插入到隊列中間。通常我們不需要喚醒事件隊列,
            // 除非隊列前面有一個屏障,并且消息是隊列中最早的異步消息。
            // p.target == null代表一個‘同步屏障’消息,它之后的同步消息暫時不投遞
            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;
}

MessageQueue.enqueueMessage()的主要工作是根據(jù)參數(shù)when將消息插入隊列適當(dāng)位置,判斷線程是否阻塞,是否需要喚醒線程。

總結(jié)一下:

  • handler.sendMessage(msg)的過程,就是將msg放入handler.mQueue.mMessags。
  • 消息將被 發(fā)送它的handler 處理

下面看一下handler.post()方法的源碼:

//Handler.java

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

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

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

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

很明顯,post()方法只是生成一個message對象,然后通過sendMessage...()方法發(fā)送。不同的是這里設(shè)置了message.callback字段,另外使用過handler.post()一定知道,這些消息不會通過handler.handleMessage()處理,那么post()產(chǎn)生的消息會怎么處理呢?我們在“分發(fā)消息”部分分析。

分發(fā)消息

消息隊列中的消息是在Looper.loop()方法中分發(fā)的,直接看源碼。

//Looper.java

/**
 * 在此線程中運行消息隊列。一定要調(diào)用quit()來結(jié)束循環(huán)。
 */
public static void loop() {
    final Looper me = myLooper();
    ...
    final MessageQueue queue = me.mQueue;
    ...
    for (;;) {
        Message msg = queue.next(); //從消息隊列取出消息。(可能阻塞)
        //正常時候,隊列中沒有消息,會阻塞線程,不會返回null
        if (msg == null) {
            // 沒有消息 表示消息隊列正在退出。
            return;
        }
        ...
        // 確保觀察者在處理事務(wù)時不會改變。
        final Observer observer = sObserver;
        ...
        Object token = null;
        if (observer != null) {
            token = observer.messageDispatchStarting();
        }
        ...
        try {
            //將消息投遞到handler。(msg.target為發(fā)送消息的handler)
            msg.target.dispatchMessage(msg);
            if (observer != null) {
                observer.messageDispatched(token, msg);
            }
        } catch (Exception exception) {
            if (observer != null) {
                observer.dispatchingThrewException(token, msg, exception);
            }
            throw exception;
        } finally {
            ...
        }
        ...
        msg.recycleUnchecked();     //回收msg實例
    }
}

主要邏輯很簡單,就是從消息隊列取出消息,投遞給指定的handler,然后回收消息。所以在這里只是把消息分發(fā)到了handler,然后再看handler.dispatchMessage(msg)怎么分發(fā)消息:

//Handler.java

@UnsupportedAppUsage
final Callback mCallback;   //創(chuàng)建handler時設(shè)置,用于處理消息

public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        //post(runnable)產(chǎn)生的消息,在此處理
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

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

/**
 * 子類必須實現(xiàn)此方法以接收消息。
 */
public void handleMessage(@NonNull Message msg) {
}

/**
 * 創(chuàng)建Handler實例時使用,避免實現(xiàn)Handler子類
 */
public interface Callback {
    boolean handleMessage(@NonNull Message msg);
}

handler.dispatchMessage(msg)的邏輯很簡單,按照以下優(yōu)先級分發(fā)消息 msg.callback > mCallback > handleMessage()

  • handleCallback()直接運行message.callback
  • handleMessage()需要在子類中實現(xiàn)
  • handler.mCallback只有一個方法,功能與handler.handleMessage()相同,只是省得寫Handler子類。(感覺沒什么用)

Looper.loop()中調(diào)用了MessageQueue.next(),這里也看一下:

//MessageQueue.java

private long mPtr;  // 大概相當(dāng)于native層的線程id
Message mMessages;  //鏈表:保存等待處理的消息
private boolean mBlocked;   //指示消息隊列關(guān)聯(lián)的線程是否阻塞
private boolean mQuitting;  //消息隊列是否已廢棄

@UnsupportedAppUsage
Message next() {
    ...
    int nextPollTimeoutMillis = 0;  //預(yù)設(shè)阻塞時長
    for (;;) {  //注意:沒找到可返回的消息時,會循環(huán)
        ...
        //使線程阻塞,nextPollTimeoutMillis為預(yù)設(shè)阻塞最長時間,可以提前喚醒
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            // 嘗試檢索下一條消息。如果找到則返回。
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                //msg.target==null代表一個‘同步屏障’消息,它之后的同步消息暫時不投遞
                // 隊列頭是同步屏障,則查找下一個異步消息。
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    // 如果還沒有到消息預(yù)設(shè)時間,設(shè)置阻塞時間,下一次循環(huán)時阻塞
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // Got a message.
                    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 {
                // 沒有消息,使線程一直阻塞。enqueueMessage()中有喚醒邏輯
                nextPollTimeoutMillis = -1;
            }

            // 現(xiàn)在處理退出消息,所有掛起的消息都已處理。
            if (mQuitting) {
                dispose();  
                return null;
            }
            ...
        }
        ...
    }
}

總結(jié)

  • 在主線程創(chuàng)建handler實例后,可以在不同線程使用這個handler實例發(fā)送消息,這些消息都會放到主線程的消息隊列中,而主線程在Looper.loop()方法中不斷地取出消息處理消息。
  • 主線程調(diào)用Looper.loop()后會無限循環(huán),那么Activity生命周期方法、屏幕刷新、點擊事件等只能在處理消息時執(zhí)行。
  • 在我們自定義的線程也可以使用Looper,使線程長久運行。

參考

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

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

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