Android Handler之原理解析

續(xù)上篇
Android Handler之從主線程往子線程發(fā)消息(一)

簡單回顧一下Handler機(jī)制中幾個對象的主要作用

Handler機(jī)制中最重要的四個對象
Handler:負(fù)責(zé)發(fā)送消息及處理消息
Looper:復(fù)制不斷的從消息隊列中取出消息升熊,并且給發(fā)送本條消息的Handler
MessageQueue:負(fù)責(zé)存儲消息
Message:消息本身,負(fù)責(zé)攜帶數(shù)據(jù)
那么页屠,一個消息從發(fā)送出去,到回到Handler自己身上辰企,這個過程具體是怎樣的?
這個就不得不去看源碼了
我們從Android Handler之從主線程往子線程發(fā)消息(一)中的
四牢贸、怎么從主線程發(fā)送消息到子線程?(雖然這種應(yīng)用場景很少)的示例代碼看起

Thread thread = new Thread(){
            @Override
            public void run() {
                super.run();
                //初始化Looper,一定要寫在Handler初始化之前
                Looper.prepare();
                //在子線程內(nèi)部初始化handler即可臭增,發(fā)送消息的代碼可在主線程任意地方發(fā)送
                handler=new Handler(){
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                      //所有的事情處理完成后要退出looper帮辟,即終止Looper循環(huán)
                        //這兩個方法都可以,有關(guān)這兩個方法的區(qū)別自行尋找答案
                        handler.getLooper().quit();
                        handler.getLooper().quitSafely();
                    }
                };
              
                //啟動Looper循環(huán)由驹,否則Handler無法收到消息
                Looper.loop();
            }
        };
        thread.start();
    //在主線程中發(fā)送消息
    handler.sendMessage();

選擇這個蹩腳的demo是為了讓大家更好的理解Handler機(jī)制,耐心看下去并炮,最后你心中所有的疑問都會有答案

一甥郑、先來解釋第一行代碼
Looper.prepare();

這個很好解釋,只要查看Handler的構(gòu)造方法即可

//空參的構(gòu)造方法澜搅,這個方法調(diào)用了兩個參數(shù)的構(gòu)造方法
  public Handler() {
        this(null, false);
    }

//兩個參數(shù)的構(gòu)造方法
public Handler(Callback callback, boolean async) {
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

看到了沒有,Handler的構(gòu)造方法中會驗證Looper癌瘾,如果Looper為空,那么會拋出空指針異常

如果你足夠細(xì)心妨退,你會發(fā)現(xiàn),Handler在構(gòu)造方法中還做了一件事咬荷,
將自己的一個全局消息隊列對象(mQueue)指向了Looper中的消息隊列
即構(gòu)造方法中的這行代碼
mQueue = mLooper.mQueue;
先記住轻掩,有用

二、第二行代碼是初始化了Hanlder并且重寫HandleMessage()方法

這個沒什么好解釋的

三唇牧、我們調(diào)用了Looper.loop()方法基茵,這個方法的具體原理隨后解釋

四壳影、handler.sendMessage(message)的主要作用

最接近我們使用的就是handler.sendMessage(message);這行代碼了弥臼,那么我們從這行代碼看起,看看這行代碼之后發(fā)生了什么径缅,為了方便大家看到,我把代碼執(zhí)行流程畫了出來

sendMessage()之后代碼執(zhí)行流程

紅線畫出來的代表代碼是哪個類的氧卧,可以看到氏堤,我們sendMessage()之后代碼通過圖中所示的幾個方法,最終執(zhí)行到了MessageQueue的enqueueMessage()方法鼠锈。也就是說,接下來我們要看的就是MessageQueue中的方法了购笆。
上源碼

boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        synchronized (this) {
            msg.markInUse();
            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 {
                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是一個單向列表結(jié)構(gòu),而MessageQueue的enqueueMessage()方法主要做的事情就是將Handler發(fā)送過來的Message插入到列表中衫哥。
也就是說娃循,當(dāng)我們調(diào)用handler.senMessage()方法的時候炕檩,最終的結(jié)果只是將這個消息插入到了消息隊列中
上面的流程圖中有一個問題:
最后一行代碼queue.enqueueMessage()中的queue對象是什么時候初始化的捌斧?
還記得本篇博客在解釋Looper.prepare()方法部分的最后一段話嗎?
不記得了就往上翻一翻

發(fā)送消息的工作已經(jīng)完成妇押,那么Looper是什么時候取的消息姓迅,取出來消息又是怎么送回給Handler的呢敲霍?現(xiàn)在我們就不得不看Looper.loop()方法了

Looper.loop()方法

public static void loop() {
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            try {
                msg.target.dispatchMessage(msg);
            } 
        }
    }

同樣的,為了降低閱讀難度柴我,我刪掉了大部分的代碼扩然,只留下了這么幾行核心代碼艘儒,現(xiàn)在我們來看這幾行代碼的邏輯

  • 這是一個死循環(huán)
  • 這個循環(huán)的目的是從MessageQueue中取出消息
  • 取消息的方法是MessageQueue.next()方法
  • 取出消息后調(diào)用message.target對象的dispatchMessage()方法分發(fā)消息
  • 循環(huán)跳出的條件是MessageQueue.next()方法返回了null

不過夫偶,看到這里我們應(yīng)該自然會想到,message.target.是哪個對象兵拢?
該對象的dispatchMessage()方法都做了什么操作翻斟?
帶著疑問,我們進(jìn)入Message類中去尋找target

public final class Message implements Parcelable {
 /*package*/ int flags;

    /*package*/ long when;

    /*package*/ Bundle data;

    /*package*/ Handler target;

    /*package*/ Runnable callback;

    // sometimes we store linked lists of these things
    /*package*/ Message next;
}

看到了吧说铃,Message類中有一個成員變量 target访惜,而這個target是一個Handler,也就是說腻扇,在Looper從MessageQueue中取出Message之后疾牲,調(diào)用了Handler的dispatchMessage()方法。
這里我們不禁要問衙解,這個target指向了哪個Handler
帶著這個疑問阳柔,我翻遍了Message類,也沒有看到我想要的答案蚓峦。此時我就想舌剂,既然Handler發(fā)送了消息就能接收到消息,那么會不會是在發(fā)送消息的途中發(fā)生了什么細(xì)節(jié)是我不知道的暑椰,那么我只好仔細(xì)看發(fā)送消息過程中的代碼霍转,終于讓我發(fā)現(xiàn)了這個

//Handler的方法
 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

在這個方法的第一行代碼就是,將message的target屬性賦值為發(fā)送message的handler自身一汽。如果讀者忘記了這個方法的調(diào)用時機(jī)避消,請往上翻翻,查看一下第四部分sendMeeage()中的流程圖

也就是說岩喷,Looper取出消息后,調(diào)用了發(fā)送消息的Handler的dispatchMessage()方法监憎,并且將message本身作為參數(shù)傳了回去纱意。到此時,代碼的執(zhí)行邏輯又回到了Handler中鲸阔。

接著看handler的dispatchMessage()方法

/**
    *handler的方法
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

看到這里面一個我們非常熟悉到方法了沒有偷霉?---handleMessage()方法迄委,對,這個方法就是我們經(jīng)常要重寫的handleMessage()方法类少,也是我們處理消息時候的邏輯呻纹。
到這里图毕,Handler機(jī)制工作的主要流程就完成了健田。
再來一個系統(tǒng)的工作示意圖

Handler工作示意圖

如果讀者認(rèn)真看了博客哮缺,那么到這里就應(yīng)該對Handler的基本工作流程比較清晰了。我也在前面詳細(xì)講解了流程圖中的方法調(diào)用過程妓忍。不過還有一些問題我們沒有處理
一個線程中最多可以有幾個Looper和幾個MessageQueue?
我們來看Looper的構(gòu)造方法即初始化方法

//Looper暴露出的靜態(tài)初始化方法
//這個方法會調(diào)用下面的私有靜態(tài)方法
  public static void prepare() {
        prepare(true);
    }
//Looper私有的靜態(tài)方法
   private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
//私有的構(gòu)造方法愧旦,禁止外界調(diào)用
  private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

//從sThreadLocal中獲取一個Looper
 public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

解釋一下上面三個方法

  • 我們只能通過Looper.prepare()方法去初始化一個Looper
  • Looper.prepare(boolean)方法的邏輯是一個線程中只能有一個Looper對象世剖,否則在第二次嘗試初始化Looper的時候,就會拋出異常旁瘫。
  • 以線程為單位存儲Looper的主要邏輯是通過ThreadLocal實現(xiàn)的
  • 私有的構(gòu)造方法,禁止外界任意new出一個Looper

通過這段邏輯我們可以看出琼蚯,
一個線程中最多有一個Looper
接著看Looper的構(gòu)造方法,里面有一行代碼
mQueue = new MessageQueue(quitAllowed);
是的遭庶,我們的MessageQueue是隨著Looper的初始化而初始化的。那么峦睡,MessageQueue能不能隨意的被new出來呢翎苫?

//MessageQueue的構(gòu)造方法
 MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }

可以看到,MessageQuede的構(gòu)造方法是default的榨了,也就是說,只有跟MessageQueue同一個包下才可以實例化MessageQueue龙屉,換句話說,我們用戶是無法直接new一個MessageQueue對象出來的转捕。而因為Looper在一個線程中只能有一個作岖,從而導(dǎo)致MessageQueue也只能有一個

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末五芝,一起剝皮案震驚了整個濱河市鳍咱,隨后出現(xiàn)的幾起案子与柑,更是在濱河造成了極大的恐慌蓄坏,老刑警劉巖丑念,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異脯倚,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)推正,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進(jìn)店門植榕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人尊残,你說我怎么就攤上這事∏奚溃” “怎么了?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵隘截,是天一觀的道長汹胃。 經(jīng)常有香客問我,道長统台,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任井赌,我火速辦了婚禮,結(jié)果婚禮上贵扰,老公的妹妹穿的比我還像新娘。我一直安慰自己戚绕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布耘子。 她就那樣靜靜地躺著,像睡著了一般绒障。 火紅的嫁衣襯著肌膚如雪捍歪。 梳的紋絲不亂的頭發(fā)上户辱,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天庐镐,我揣著相機(jī)與錄音,去河邊找鬼必逆。 笑死韧献,一個胖子當(dāng)著我的面吹牛研叫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嚷炉,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼绘证!你這毒婦竟也來了哗讥?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤杆煞,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后队询,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體构诚,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年送膳,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肠缨。...
    茶點(diǎn)故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡晒奕,死狀恐怖闻书,靈堂內(nèi)的尸體忽然破棺而出脑慧,到底是詐尸還是另有隱情,我是刑警寧澤闷袒,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布囊骤,位于F島的核電站,受9級特大地震影響也物,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜滑蚯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一告材、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧斥赋,春花似錦、人聲如沸疤剑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽棘幸。三九已至焰扳,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間扫茅,已是汗流浹背育瓜。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留躏仇,地道東北人。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓糟描,卻偏偏與公主長得像书妻,于是被迫代替她去往敵國和親船响。 傳聞我的和親對象是個殘疾皇子躲履,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評論 2 353