Android Handler分析(一) Handler和Message詳解

在《Android Handler機(jī)制》中我們對Handler機(jī)制的整體流程做了分析负拟,知道了在Android中是怎樣通過Handler進(jìn)行線程切換的了。接下來,我們就看看Handler中的相關(guān)類分析佃扼。

Message源碼分析:

前面說到了Message表示一個消息次和,可以理解為線程間通訊的數(shù)據(jù)單元浦妄,可以用來傳遞一些數(shù)據(jù)厨喂。

1. 首先看一下Message類中的一些成員變量

  • public int what:可以用來區(qū)分不同的消息曾雕,然后在Handler中做不同的處理
  • public int arg1,arg2:可以傳遞數(shù)據(jù)奴烙,當(dāng)需要傳遞的數(shù)據(jù)僅僅是int類型時,可以用arg1和arg2翻默,而不需要使用復(fù)雜的obj和data
  • public Object obj:可以傳遞數(shù)據(jù)缸沃,可以傳遞任意的Object對象
  • Bundle data:可以Bundle類型傳遞數(shù)據(jù),但是使用了默認(rèn)修飾符修械,不能直接使用Message對象.data的形式設(shè)置和獲取值趾牧,但Message提供了對應(yīng)的get和set方法用來設(shè)置和獲取值
  • Handler target:保存發(fā)送消息的Handler對象,可以在創(chuàng)建Message時指定肯污,在后面會有用到target這個參數(shù)翘单,所以特別說明
  • Runnable callback:Runable對象,可以在創(chuàng)建Message時指定蹦渣,另外在Handler使用post形式發(fā)送消息時的callback參數(shù)最終也是保存在 Message的這個參數(shù)中
  • Message next:Message對象哄芜,用來使Message組成鏈表的形式
  • private static Message sPool:用于保存回收的Message消息對象
  • private static boolean gCheckRecycle = true:判斷Android版本是否大于Android5.0(Build.VERSION_CODES.LOLLIPOP),小于為false

2. 創(chuàng)建(獲取)一個Message對象(以下方法都是使用Public修飾的)

Message(){} // 空參數(shù)構(gòu)造方法柬唯,直接new Message()創(chuàng)建對象
static Message obtain() // 使用靜態(tài)的obtain()方法獲取认臊,內(nèi)部會重復(fù)使用回收的Message對象,比較常用
static Message obtain(Message orig) // 根據(jù)傳遞的Message對象復(fù)制一個新的Message對象
static Message obtain(Handler h) // 給Message指定Handler锄奢,會將 h 賦值給 成員變量 target
static Message obtain(Handler h, Runnable callback) // 給Message指定Handler和Runable失晴,會將 h 賦值給 成員變量 target,callback賦值給Message的成員變量 callback
static Message obtain(Handler h, int what) // 給Message指定Handler和what拘央,賦值給Message中對應(yīng)的成員變量
static Message obtain(Handler h, int what, Object obj) // 給Message指定Handler涂屁、what和Object類型的值,賦值給Message中對應(yīng)的成員變量
static Message obtain(Handler h, int what, int arg1, int arg2) // 指定Message更多的屬性
Message obtain(Handler h, int what, int arg1, int arg2, Object obj) // 指定Message更多的屬性

3. Message中的部分方法

// 回收消息Message對象
public void recycle() {
    // 判斷是否在使用
    if (isInUse()) {
        // 判斷版本灰伟,在說成員變量時有說明拆又,如果版本大于5.0就會報錯
        if (gCheckRecycle) {
            throw new IllegalStateException("This message cannot be recycled because it "
                    + "is still in use.");
        }
        // 如果該Message對象在使用就返回
        return;
    }
    // 調(diào)用回收Message對象的方法
    recycleUnchecked();
}
// 回收消息
void recycleUnchecked() {
    flags = FLAG_IN_USE; // 將標(biāo)記設(shè)置為沒有使用
    what = 0; 
    // ...  清除Message成員變量的值

    // 使用同步代碼
    synchronized (sPoolSync) {
        // 判斷回收消息隊列的長度是否大于最大長度
        if (sPoolSize < MAX_POOL_SIZE) {
            // 將回收的消息添加到回收的鏈表中
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}
// 給消息指定一個Handler
public void setTarget(Handler target) {
    // 賦值給target
    this.target = target;
}
// 給消息設(shè)置data值
public void setData(Bundle data) {
    this.data = data;
}
// 獲取data,但是當(dāng)data為null時會創(chuàng)建一個返回
public Bundle getData() {
    if (data == null) {
        data = new Bundle();
    }
    
    return data;
}
// 和getData()方法一樣獲取data值栏账,但是不會進(jìn)行判斷和創(chuàng)建
public Bundle peekData() {
    return data;
}
// 將消息發(fā)送給指定的Handler
public void sendToTarget() {
    target.sendMessage(this);
}
// 設(shè)置是否為異步消息
public void setAsynchronous(boolean async) {
    if (async) {
        flags |= FLAG_ASYNCHRONOUS;
    } else {
        flags &= ~FLAG_ASYNCHRONOUS;
    }
}
// 判斷消息是否在使用
/*package*/ boolean isInUse() {
    return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
}
// 將消息設(shè)置為使用狀態(tài)
/*package*/ void markInUse() {
    flags |= FLAG_IN_USE;
}

Handler源碼分析:

1. Handler的創(chuàng)建(構(gòu)造方法):在Handler中的構(gòu)造方法比較多帖族,但是最終調(diào)用的只有兩個

// 兩個參數(shù)的構(gòu)造方法
public Handler(Callback callback, boolean async) {
    ...
    // 通過Looper中的方法myLooper()獲取與當(dāng)前線程綁定的Looper,具體實現(xiàn)在Looper部分說明
    mLooper = Looper.myLooper();
    // 當(dāng)前線程不是Looper線程挡爵,也就是沒有調(diào)用Looper.prepare()給線程創(chuàng)建Looper對象
    if (mLooper == null) {
        // 拋出異常
        throw new RuntimeException(
        "Can't create handler inside thread that has not called Looper.prepare()");
    }
    // 讓Handler持有當(dāng)前線程消息隊列(MessageQueue)的引用
    mQueue = mLooper.mQueue;
    // 主要用于Handler的消息發(fā)送的回調(diào)竖般,優(yōu)先級是比handlerMessage()方法高,但不常用
    mCallback = callback;
    // 是否異步了讨,如果為true則會調(diào)用Message中的setAsynchronous(boolean async)方法捻激,設(shè)置參數(shù)為true
    mAsynchronous = async;
}
// 三個參數(shù)的構(gòu)造方法
public Handler(Looper looper, Callback callback, boolean async) {
    // 直接賦值,獲取Looper中的MessageQueue引用讓Handler持有
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

我們常用的Handler handler = new Handler();實際上調(diào)用的就是兩個參數(shù)的構(gòu)造方法前计。

2. Handler發(fā)送消息:發(fā)送消息的方法比較多胞谭,下面都列出來了

// 1 sendMessage(Message msg)
// 2 sendEmptyMessage(int what)
// 3 sendEmptyMessageDelayed(int what, long delayMillis)
// 4 sendEmptyMessageAtTime(int what, long uptimeMillis)
// 5 sendMessageDelayed(Message msg, long delayMillis)

// 6 前面5個發(fā)送消息的方法最終調(diào)用的都是這個(第6個)方法
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    // 將Handler中的MessageQueue引用賦值給新的MessageQueue對象
    MessageQueue queue = mQueue;
    // 判斷MessageQueue是否為null,如果為null拋出一個 RuntimeException 異常
    if (queue == null) {
        RuntimeException e = new RuntimeException(
            this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    // 調(diào)用Handler的enqueueMessage()方法男杈,并將時間傳遞過去
    return enqueueMessage(queue, msg, uptimeMillis);
}
// 7 這個方法表示將消息對象(Message)插入到消息隊列(MessageQueue)的最前面
public final boolean sendMessageAtFrontOfQueue(Message msg) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
            this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;   
    }
    // 傳的時間值為0
    return enqueueMessage(queue, msg, 0);
}

我們接著看Handler中的enqueueMessage()方法:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    // 特別注意:將當(dāng)前對象(Handler對象)賦值給Message的target變量
    msg.target = this;
    // 根據(jù)是否異步調(diào)用Message的setAsynchronous()方法
    // 在上面Handler的構(gòu)造方法中提到過
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    // 調(diào)用MessageQueue的enqueueMessage()方法對Message進(jìn)行排序
    return queue.enqueueMessage(msg, uptimeMillis);
}

到了這一步丈屹,我們看到了在調(diào)用Handler中的發(fā)送消息方法時,Handler在這里并沒有做過多操作伶棒,然后就通過參數(shù)將Message對象傳遞到了MessageQueue(消息隊列)中旺垒,調(diào)用的是MessageQueue中的相關(guān)方法進(jìn)行相關(guān)操作,在下一篇博客《Android Handler分析 (二) MessageQueue詳解》中我們就會說到肤无。

3. Handler發(fā)送一個Runable對象

public final boolean post(Runnable r)
public final boolean postAtTime(Runnable r, long uptimeMillis)
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
public final boolean postDelayed(Runnable r, long delayMillis)
public final boolean postAtFrontOfQueue(Runnable r)

以上就是Handler提供用來發(fā)送一個Runnable對象到消息隊列的方法先蒋,在下面我貼出兩個方法的源碼:

// 通過getPostMessage()方法獲取一個消息對象,然后調(diào)用sendMessageDelayed()方法
public final boolean postDelayed(Runnable r, long delayMillis){
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}
// 調(diào)用sendMessageAtFrontOfQueue()方法將消息添加到消息隊列的隊首
public final boolean postAtFrontOfQueue(Runnable r){
    return sendMessageAtFrontOfQueue(getPostMessage(r));
}

我們來看一下getPostMessage()方法干了什么事

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

這個方法獲取一個消息對象宛渐,并將Runnable賦值給Message的callback變量竞漾。

我們看到使用post方式發(fā)送一個Runnable對象到消息隊列其實在內(nèi)部使用的也是sendXXX的方式,只是系統(tǒng)幫我們把Runnable對象賦值給了Message的callback變量窥翩。

4. Handler移除消息

// 移除Runnable這個消息
public final void removeCallbacks(Runnable r){
    mQueue.removeMessages(this, r, null);
}
// 移除指定了token的Runnable的消息业岁,如果token為null,將移除所有的callback消息
public final void removeCallbacks(Runnable r, Object token){
    mQueue.removeMessages(this, r, token);
}
// 移除指定的what值的消息
public final void removeMessages(int what) {
    mQueue.removeMessages(this, what, null);
}
// 移除指定的what值和object值的消息
public final void removeMessages(int what, Object object) {
    mQueue.removeMessages(this, what, object);
}
// 移除指定的token的消息寇蚊,如果token為null笔时,將移除所有的消息(callbacks和messages)
public final void removeCallbacksAndMessages(Object token) {
    mQueue.removeCallbacksAndMessages(this, token);
}

由以上方法我們可以看到,Handler并沒有對消息進(jìn)行移除操作仗岸,只是調(diào)用了MessageQueue中的相關(guān)方法對消息對象進(jìn)行操作允耿,在下一篇博客《Android Handler分析 (二) MessageQueue詳解》我們主要說這個類。

5. Handler中的dispatchMessage(Message msg)方法

public void dispatchMessage(Message msg) {
    // 判斷Message中的callback是否為null爹梁,也就是判斷是否為Runnable類型的消息
    if (msg.callback != null) {
        // 執(zhí)行Runnable中的run()方法
        handleCallback(msg);
    } else {
        // 如果不是右犹,判斷Handler中的mCallback是否為null
        if (mCallback != null) {
            // 不為null,就執(zhí)行mCallback中的handleMessage()方法
            // 并且如果該方法返回true姚垃,就不會執(zhí)行Handler中的handleMessage()方法了
            // 在Handler的創(chuàng)建這個部分注釋中說到了它的優(yōu)先級更高
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        // 執(zhí)行Handler中的handleMessage()方法念链,也就是我們在創(chuàng)建Handler對象時候重寫的方法
        handleMessage(msg);
    }
}
// 調(diào)用Runnable的run()方法
private static void handleCallback(Message message) {
    message.callback.run();
}

說明:前面所說的Handler中的方法(Handler的創(chuàng)建、發(fā)送/移除消息等)我們在開發(fā)中都調(diào)用過积糯,但是對于dispatchMessage()方法我們卻并沒有調(diào)用過掂墓,那么它是在哪里調(diào)用然后讓系統(tǒng)來執(zhí)行我們的handleMessage()方法呢?不要著急看成,在這里我們先記住這個方法君编,在后面的篇博客《Android Handler分析 (三) Looper詳解和Handler其他知識》 中我們就會說到,其實它是在Looper中被調(diào)用的川慌。

最后來個帶CallBack的Handler創(chuàng)建方式:

private Handler handler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        // 如果返回true吃嘿,就不會執(zhí)行下面的那個handleMessage()方法了
        return false;
    }
}) {
    @Override
    public void handleMessage(Message msg) {
        int what = msg.what; // 獲取消息的what
        // 通過what對消息進(jìn)行判斷祠乃,不同消息不同的處理
        if (what == HANDLER_TEST_WHAT) {
            // 處理消息
            Toast.makeText(MainActivity.this, "hhh", Toast.LENGTH_SHORT).show();
        }
    }
};

接下一篇博客《Android Handler分析 (二) MessageQueue詳解》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末兑燥,一起剝皮案震驚了整個濱河市亮瓷,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌降瞳,老刑警劉巖嘱支,帶你破解...
    沈念sama閱讀 222,807評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異挣饥,居然都是意外死亡除师,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評論 3 399
  • 文/潘曉璐 我一進(jìn)店門扔枫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來汛聚,“玉大人,你說我怎么就攤上這事短荐≌炅耄” “怎么了?”我有些...
    開封第一講書人閱讀 169,589評論 0 363
  • 文/不壞的土叔 我叫張陵搓侄,是天一觀的道長瞄桨。 經(jīng)常有香客問我,道長讶踪,這世上最難降的妖魔是什么芯侥? 我笑而不...
    開封第一講書人閱讀 60,188評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮乳讥,結(jié)果婚禮上柱查,老公的妹妹穿的比我還像新娘。我一直安慰自己云石,他們只是感情好唉工,可當(dāng)我...
    茶點故事閱讀 69,185評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著汹忠,像睡著了一般淋硝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宽菜,一...
    開封第一講書人閱讀 52,785評論 1 314
  • 那天谣膳,我揣著相機(jī)與錄音,去河邊找鬼铅乡。 笑死继谚,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的阵幸。 我是一名探鬼主播花履,決...
    沈念sama閱讀 41,220評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼芽世,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了诡壁?” 一聲冷哼從身側(cè)響起捂襟,我...
    開封第一講書人閱讀 40,167評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎欢峰,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體涨共,經(jīng)...
    沈念sama閱讀 46,698評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡纽帖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,767評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了举反。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片懊直。...
    茶點故事閱讀 40,912評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖火鼻,靈堂內(nèi)的尸體忽然破棺而出室囊,到底是詐尸還是另有隱情,我是刑警寧澤魁索,帶...
    沈念sama閱讀 36,572評論 5 351
  • 正文 年R本政府宣布融撞,位于F島的核電站,受9級特大地震影響粗蔚,放射性物質(zhì)發(fā)生泄漏尝偎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,254評論 3 336
  • 文/蒙蒙 一鹏控、第九天 我趴在偏房一處隱蔽的房頂上張望致扯。 院中可真熱鬧,春花似錦当辐、人聲如沸抖僵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽耍群。三九已至,卻和暖如春找筝,著一層夾襖步出監(jiān)牢的瞬間世吨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評論 1 274
  • 我被黑心中介騙來泰國打工呻征, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留耘婚,地道東北人。 一個月前我還...
    沈念sama閱讀 49,359評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像抓歼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子论寨,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,922評論 2 361

推薦閱讀更多精彩內(nèi)容