Handler 丽旅、 Looper 椰棘、Message、HandlerThread總結(jié)

Looper主要有prepare()和loop()兩個(gè)方法榄笙,Looper是用來(lái)從MessageQueue中抽取Message邪狞,發(fā)送給Handler進(jìn)行處理。
prepare()方法:

public static final void prepare() {  
        if (sThreadLocal.get() != null) {  
            throw new RuntimeException("Only one Looper may be created per thread");  
        }  
        sThreadLocal.set(new Looper(true));  
} 

sThreadLocal是一個(gè)ThreadLocal對(duì)象茅撞,可以在一個(gè)線程中存儲(chǔ)變量帆卓。第一次調(diào)用prepare()方法,if條件不成立米丘,所以將創(chuàng)建的Looper對(duì)象保存到sThreadLocal中剑令。第二次調(diào)用就會(huì)拋出異常,說(shuō)明Looper.prepare()方法不能被調(diào)用兩次拄查,也保證了一個(gè)線程中只有一個(gè)Looper實(shí)例尚洽。

Looper的構(gòu)造方法:

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

在構(gòu)造方法中,創(chuàng)建了一個(gè)MessageQueue(消息隊(duì)列)

loop()方法中調(diào)用myLooper()方法取出sThreadLocal中保存的Looper實(shí)例靶累,如果Looper實(shí)例為null則拋出異常腺毫,所以looper方法必須在prepare方法之后運(yùn)行。接著會(huì)獲取到該looper實(shí)例中的mQueue(消息隊(duì)列)挣柬。然后進(jìn)入了無(wú)限循環(huán)潮酒,從消息隊(duì)列中取出消息。調(diào)用 msg.target.dispatchMessage(msg);把消息交給msg的target的dispatchMessage方法去處理邪蛔,Msg的target就是handler對(duì)象急黎。

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

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

Handler的構(gòu)造方法中會(huì)調(diào)用Looper.myLooper()獲取了當(dāng)前線程保存的Looper實(shí)例,然后又通過(guò)mLooper.mQueue獲取了Looper實(shí)例中保存的MessageQueue(消息隊(duì)列)侧到,這樣就保證了handler的實(shí)例與我們Looper實(shí)例中MessageQueue關(guān)聯(lián)上了Handler的sendMessage(Message msg)方法最后調(diào)用了sendMessageAtTime()勃教,在此方法內(nèi)部直接獲取MessageQueue然后調(diào)用了enqueueMessage方法

public boolean sendMessageAtTime(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(MessageQueue queue, Message msg, long uptimeMillis) {  
       msg.target = this;  
       if (mAsynchronous) {  
           msg.setAsynchronous(true);  
       }  
       return queue.enqueueMessage(msg, uptimeMillis);  
}

enqueueMessage方法中msg.target賦值為this,在Looper的loop方法會(huì)取出每個(gè)msg然后交給msg.target.dispatchMessage(msg)去處理消息匠抗,所以當(dāng)前的handler就是msg的target故源。最終調(diào)用queue的enqueueMessage的方法,也就是說(shuō)handler發(fā)出的消息汞贸,最終會(huì)保存到消息隊(duì)列中去绳军。不斷從MessageQueue中讀取Handler發(fā)來(lái)的消息,然后再回調(diào)創(chuàng)建這個(gè)消息的handler中的dispathMessage方法矢腻,在dispatchMessage方法中調(diào)用了handleMessage(msg)方法门驾,方法內(nèi)部是空實(shí)現(xiàn),因?yàn)橄⒌淖罱K回調(diào)是由我們控制的多柑,我們?cè)趧?chuàng)建handler的時(shí)候都是復(fù)寫handleMessage方法奶是,然后根據(jù)msg.what進(jìn)行消息處理

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

public void handleMessage(Message msg) {  
} 

注意:在Activity中,我們并沒(méi)有調(diào)用Looper.prepare()和Looper.loop()方法,但是Handler可以成功創(chuàng)建聂沙,這是因?yàn)樵贏ctivity的啟動(dòng)代碼中秆麸,已經(jīng)在當(dāng)前UI線程調(diào)用了Looper.prepare()和Looper.loop()方法

HandlerThread是Android API提供的一個(gè)便捷的類,使用它我們可以快速的創(chuàng)建一個(gè)帶有Looper的線程逐纬,有了Looper這個(gè)線程蛔屹,我們又可以生成Handler。HandlerThread本質(zhì)上是一個(gè)線程類豁生,它繼承了Thread兔毒;
HandlerThread有自己的內(nèi)部Looper對(duì)象,可以進(jìn)行l(wèi)ooper循環(huán)甸箱;
通過(guò)獲取HandlerThread的looper對(duì)象傳遞給Handler對(duì)象育叁,可以在handleMessage方法中執(zhí)行異步任務(wù)。
創(chuàng)建HandlerThread后必須先調(diào)用HandlerThread.start()方法芍殖,Thread會(huì)先調(diào)用run方法豪嗽,創(chuàng)建Looper對(duì)象

使用普通的Thread來(lái)創(chuàng)建一個(gè)Handler的過(guò)程:

Handler mHandler;
private void createThreadWithHandler() {
  new Thread() {
      @Override
        public void run() {
            super.run();
            Looper.prepare();
            mHandler = new Handler(Looper.myLooper());
            Looper.loop();
        }
    }.start();
}

調(diào)用Looper.prepare 創(chuàng)建與當(dāng)前線程綁定的Looper實(shí)例
使用上面創(chuàng)建的Looper生成Handler實(shí)例
調(diào)用Looper.loop()實(shí)現(xiàn)消息循環(huán)

HandlerThread使用:
//傳入?yún)?shù)的作用主要是標(biāo)記當(dāng)前線程的名字,可以任意字符串
HandlerThread workerThread = new HandlerThread("LightTaskThread");
workerThread.start();
mHandler = new Handler(workerThread.getLooper());
注意:上面的workerThread.start()必須要執(zhí)行豌骏。
在activity的onDestory方法中調(diào)用workerThread.quit()方法結(jié)束當(dāng)前的Looper循環(huán)

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    /**
     * Constructs a HandlerThread.
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from 
     * {@link android.os.Process} and not from java.lang.Thread.
     */
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }

    /**
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     */
    protected void onLooperPrepared() {
}

從源碼可以看出HandlerThread繼續(xù)自Thread,構(gòu)造函數(shù)的傳遞參數(shù)有兩個(gè)龟梦,一個(gè)是name指的是線程的名稱,一個(gè)是priority指的是線程優(yōu)先級(jí)窃躲,我們根據(jù)需要調(diào)用即可计贰。其中成員變量mLooper就是HandlerThread自己持有的Looper對(duì)象。onLooperPrepared()該方法是一個(gè)空實(shí)現(xiàn)蒂窒,是留給我們必要時(shí)可以去重寫的躁倒,但是注意重寫時(shí)機(jī)是在Looper循環(huán)啟動(dòng)前

看看run方法:

@Override
public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll(); 
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
}

前面我們?cè)贖andlerThread的常規(guī)使用中分析過(guò),在創(chuàng)建HandlerThread對(duì)象后必須調(diào)用其start()方法才能進(jìn)行其他操作洒琢,而調(diào)用start()方法后相當(dāng)于啟動(dòng)了線程秧秉,也就是run方法將會(huì)被調(diào)用,而我們從run源碼中可以看出其執(zhí)行了Looper.prepare()代碼衰抑,這時(shí)Looper對(duì)象將被創(chuàng)建象迎,當(dāng)Looper對(duì)象被創(chuàng)建后將綁定在當(dāng)前線程(也就是當(dāng)前異步線程),這樣我們才可以把Looper對(duì)象賦值給Handler對(duì)象停士,進(jìn)而確保Handler對(duì)象中的handleMessage方法是在異步線程執(zhí)行的挖帘。

最近看到的一篇詳細(xì)分析線程池的文章,學(xué)習(xí)一下:
https://mp.weixin.qq.com/s/rHnzqlzJusIVNO7X7mBDkA

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末恋技,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子逻族,更是在濱河造成了極大的恐慌蜻底,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異薄辅,居然都是意外死亡要拂,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門站楚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)脱惰,“玉大人,你說(shuō)我怎么就攤上這事窿春±唬” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵旧乞,是天一觀的道長(zhǎng)蔚润。 經(jīng)常有香客問(wèn)我,道長(zhǎng)尺栖,這世上最難降的妖魔是什么嫡纠? 我笑而不...
    開(kāi)封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮延赌,結(jié)果婚禮上除盏,老公的妹妹穿的比我還像新娘。我一直安慰自己挫以,他們只是感情好者蠕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著屡贺,像睡著了一般蠢棱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上甩栈,一...
    開(kāi)封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天泻仙,我揣著相機(jī)與錄音,去河邊找鬼量没。 笑死玉转,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的殴蹄。 我是一名探鬼主播究抓,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼袭灯!你這毒婦竟也來(lái)了刺下?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤稽荧,失蹤者是張志新(化名)和其女友劉穎橘茉,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡畅卓,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年擅腰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片翁潘。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡趁冈,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拜马,到底是詐尸還是另有隱情渗勘,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布一膨,位于F島的核電站呀邢,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏豹绪。R本人自食惡果不足惜价淌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瞒津。 院中可真熱鬧蝉衣,春花似錦、人聲如沸巷蚪。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)屁柏。三九已至啦膜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間淌喻,已是汗流浹背僧家。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留裸删,地道東北人八拱。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像涯塔,于是被迫代替她去往敵國(guó)和親肌稻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355