Android-Runtime 機制和線程同步 Handler表悬、Looper弥锄、MessageQueue

?引言: 線程是一段可執(zhí)行的代碼,當可執(zhí)行代碼執(zhí)行完成后蟆沫,線程生命周期便該終止了籽暇,線程也即退出了。

帶著兩個問題思考:
1饭庞、代碼執(zhí)行完了線程生命周期便終止戒悠,那為什么主線程能一直執(zhí)行而不退出呢?
2舟山、Android 是如何實現(xiàn)線程同步的绸狐?

此過程涉及到幾個類,先做普及 ActivityThread: ActivityThread.main() 函數(shù)作為程序入口累盗。

Looper: Looper.loop() 死循環(huán)寒矿,用于保證該線程生命周期一直執(zhí)行,不會退出若债。

ThreadLocal: 存放 Looper符相,可實現(xiàn)線程共享。

MessageQueue: 存放有序的 Message蠢琳,管理任務

Message: 消息

Handler: 發(fā)送消息啊终,處理任務(handleMessage() 方法)

public class ThreadLocal<T> {
    ...
    public T get() {
        //以當前線程為 key 獲取 value,實現(xiàn)線程共享
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
}

從主線程開始執(zhí)行到 Handler 處理任務分析:
應用的入口從 ActivityThread.main() 開始執(zhí)行。當前運行的線程即為主線程(mainThread)傲须。
public final class ActivityThread extends ClientTransactionHandler {

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

1 > 在 main() 中調用 Looper.prepareMainLooper() 時實際是通過 Looper.prepare(false) 創(chuàng)建 Looper 蓝牲,并將 looper 保存在 ThreadLocal 中。存放在 ThreadLocal 中的好處是可以線程共享泰讽,即只要在當前線程中就可以獲取到 Looper 對象搞旭。

2 > Looper 對象初始化時生成 MessageQueue 對象,MessageQueue 是一個單向鏈表菇绵,用來存放 message 并指向下一個 messgae,這種結構保證了消息順序執(zhí)行镇眷。

public final class Looper {
    //單例咬最,存放各個線程中 Looper 對象
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    ...
    //消息隊列,用來存放  message
    final MessageQueue mQueue;
    final Thread mThread;

    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //創(chuàng)建 Looper 并存放在 sThreadLocal 中
        sThreadLocal.set(new Looper(quitAllowed));
    }

    //獲取 當前 Looper 對象
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
}

public final class MessageQueue {
    Message mMessages;
 }

3 > Looper.loop() 通過死限循環(huán) 調用 queue.next() 方法獲取 message 信息欠动。

public final class Message implements Parcelable {
    //指向下一個消息
    Message next;
    //發(fā)送消息的 Handler 永乌,用于處理任務
    Handler target;
}

public final class Looper {
    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;
        ...
        //死循環(huán)調用 queue.next() 獲取 message
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                return;
            }
            ...
            try {
                //如果有 msg 不為空惑申,調用 target 即 handler 的 dispatchMessage()
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
            }
            ...
            msg.recycleUnchecked();
        }
    }
}

public class Handler {
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            ...
        } else {
            ...
            //即各 Handler 處理任務的邏輯
            handleMessage(msg);
        }
    }
}

public final class MessageQueue {
    Message next() {
        ...
        for (;;) {
            ...
            nativePollOnce(ptr, nextPollTimeoutMillis);
            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    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 {
                        ...
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    ...
                }
            }
            ...
        }
    }
}

從 Handler 發(fā)送消息分析:
Handler 初始化時會通過 Looper.myLooper() 獲取當前線程的 Looper 對象,并獲取到 Looper 中的 MessageQueue 對象翅雏。由此可以猜測 當 handler.sendMessage(Message msg) 時發(fā)送的消息會被存放在 MessageQueue 中圈驼,與上面分析 MessageQueue.next 獲取消息對應。
public class Handler {
    //通過 Looper.myLooper() 獲取創(chuàng)建該對象時
    final Looper mLooper;
    //用于存放 message
    final MessageQueue mQueue;

    public Handler(Callback callback, boolean async) {
        //創(chuàng)建 Handler 時望几,Handler 會獲取到當前線程的 Looper 對象绩脆,并獲取到 MessageQueue
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
    }
}

發(fā)送消息 sendMessage()

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

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

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        //即 Looper 中的 MessageQueue
        MessageQueue queue = mQueue;
        ...
        return enqueueMessage(queue, msg, uptimeMillis);
    }

下一步 msg.target = this 很重要

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        //將當前 handler 對象賦值給 message.target,此操作是為了綁定 handler 橄抹,用于處理消息時分發(fā)給當前 Handler
        msg.target = this;
        ...
        return queue.enqueueMessage(msg, uptimeMillis);
    }

以上是 handler.sendMessage 發(fā)送消息時將 message 發(fā)送給 messageQueue 對象靴迫,并將自身賦值給 message.target 與 message 綁定,綁定是為了在處理 message 時便于分辨執(zhí)行特定的 handler.handleMessage() 方法楼誓。

再看 MessageQueue 中 enqueueMessage(msg, uptimeMillis) 方法
    boolean enqueueMessage(Message msg, long when) {
        ...
            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 {
                ...
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    ...
                }
                msg.next = p;
                prev.next = msg;
            }
            ...
        }
        return true;
    }

enqueueMessage 只是通過 when 即 delayMillis 延遲處理的時間長短給插入的 message 排序玉锌。

以上分別分析了 處理任務與發(fā)送消息的 流程,流程如下:

ActivityThread.main() --> Looper.prepareMainLooper() --> Looper.loop() --> queue.next() --> msg.target.dispatchMessage() --> handler. dispatchMessage() --> handler. handleMessage()

Handler.jpg

回到開始提出的兩個問題疟羹,通過分析可以得出以下結論:

問:1主守、為什么主線程能一直執(zhí)行而不退出?

答: Looper.loop() 中通過死循環(huán)讓程序一直執(zhí)行榄融,不會退出参淫。并會持續(xù)調用 MessageQueue.next() 方法獲取 message。

問:2剃袍、Android 是如何實現(xiàn)線程同步的黄刚?

答: 不同線程中發(fā)送的消息都是發(fā)送到 創(chuàng)建 Handler 時所在的線程的 MessageQueue 對象中,通過 ThreadLocal 會獲取該線程中的 Looper 和 MessageQueue民效。當消息插入至 MessageQueue 中時憔维, MessageQueue.next() 會獲取到 message ,之后會調用 message.target.dispatchMessage()畏邢,最后調用 handleMessage() 處理任務业扒。

此過程中涉及到 IdleHandler 、和 線程掛起 nativePollOnce() 將另作篇幅分析

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末舒萎,一起剝皮案震驚了整個濱河市程储,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌臂寝,老刑警劉巖章鲤,帶你破解...
    沈念sama閱讀 222,946評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異咆贬,居然都是意外死亡败徊,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,336評論 3 399
  • 文/潘曉璐 我一進店門掏缎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來皱蹦,“玉大人煤杀,你說我怎么就攤上這事』Σ福” “怎么了沈自?”我有些...
    開封第一講書人閱讀 169,716評論 0 364
  • 文/不壞的土叔 我叫張陵,是天一觀的道長辜妓。 經(jīng)常有香客問我枯途,道長,這世上最難降的妖魔是什么嫌拣? 我笑而不...
    開封第一講書人閱讀 60,222評論 1 300
  • 正文 為了忘掉前任柔袁,我火速辦了婚禮,結果婚禮上异逐,老公的妹妹穿的比我還像新娘捶索。我一直安慰自己,他們只是感情好灰瞻,可當我...
    茶點故事閱讀 69,223評論 6 398
  • 文/花漫 我一把揭開白布腥例。 她就那樣靜靜地躺著,像睡著了一般酝润。 火紅的嫁衣襯著肌膚如雪燎竖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,807評論 1 314
  • 那天要销,我揣著相機與錄音构回,去河邊找鬼。 笑死疏咐,一個胖子當著我的面吹牛纤掸,可吹牛的內容都是我干的。 我是一名探鬼主播浑塞,決...
    沈念sama閱讀 41,235評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼借跪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了酌壕?” 一聲冷哼從身側響起掏愁,我...
    開封第一講書人閱讀 40,189評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎卵牍,沒想到半個月后果港,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,712評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡糊昙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,775評論 3 343
  • 正文 我和宋清朗相戀三年京腥,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片溅蛉。...
    茶點故事閱讀 40,926評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡公浪,死狀恐怖,靈堂內的尸體忽然破棺而出船侧,到底是詐尸還是另有隱情欠气,我是刑警寧澤,帶...
    沈念sama閱讀 36,580評論 5 351
  • 正文 年R本政府宣布镜撩,位于F島的核電站预柒,受9級特大地震影響,放射性物質發(fā)生泄漏袁梗。R本人自食惡果不足惜宜鸯,卻給世界環(huán)境...
    茶點故事閱讀 42,259評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望遮怜。 院中可真熱鬧淋袖,春花似錦、人聲如沸锯梁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,750評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽陌凳。三九已至剥懒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間合敦,已是汗流浹背初橘。 一陣腳步聲響...
    開封第一講書人閱讀 33,867評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留充岛,地道東北人保檐。 一個月前我還...
    沈念sama閱讀 49,368評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像裸准,于是被迫代替她去往敵國和親展东。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,930評論 2 361

推薦閱讀更多精彩內容