HandlerThread原理與應(yīng)用

HandlerThread


?在理解了Handler的原理之后借嗽,我們知道在一個子線程中創(chuàng)建一個Handler不能缺少了Looper.prepare()和Looper.loop()兩個方法走敌,具體的原因這里不再贅述呢灶,不熟悉原理的可以先看下另一篇文章Handler的原理解析.
本篇文章主要是講解HandlerThread的使用的胚迫。
?首先HandlerThread是繼承于Thread類的,所以本質(zhì)上HandlerThread就是一個線程,接下來就詳細(xì)的去看一看抬虽,這是怎樣的一個線程略号?
?首先刑峡,先看下它的構(gòu)造函數(shù):

public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
//很簡單洋闽,構(gòu)造函數(shù)里只是設(shè)置了該Thread的名稱和優(yōu)先級。

?既然是線程突梦,那么最重要的當(dāng)然是run方法來诫舅,看完了run方法,相信你也就明白HandlerThread的用途了阳似!

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

?怎么樣骚勘,這是不是和我們之前在子線程中創(chuàng)建Handler一樣,首先是Looper.prepare(),接著是new Handler(), 最后是Looper.loop()撮奏。等等俏讹,這里并沒有創(chuàng)建Handler啊畜吊!別急泽疆,我們先一步一步地看看run方法再說為什么沒有創(chuàng)建Handler。

  1. 首先呢是調(diào)用了Looper.prepaer()玲献,該方法為我們的線程創(chuàng)建了一個唯一的Looper和MessageQueue對象殉疼,具體的創(chuàng)建過程看另一篇文章Handler的原理解析
  2. 接下來有一個同步鎖的代碼塊,里面獲取到了創(chuàng)建好的Looper對象將其賦值給當(dāng)前的mLooper捌年,然后喚醒了鎖瓢娜。注意這里有一個喚醒線程的操作,既然有喚醒鎖的操作礼预,那么必定有有個地方使線程處于了阻塞的狀態(tài)眠砾,我們看下出現(xiàn)阻塞的地方。
public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

?通過查找發(fā)現(xiàn)到一個getLooper()的方法托酸,該方法返回了當(dāng)前線程的mLooper對象褒颈,還記得Looper是在哪里進(jìn)行賦值的嗎?在線程的run方法里励堡,所以當(dāng)線程啟動之后才能創(chuàng)建Looper并賦值給mLooper谷丸,這里的阻塞就是為了等待Looper的創(chuàng)建成功。同時該方法是用Public修飾的应结,說明該方法是提供外部調(diào)用的刨疼,Looper創(chuàng)建成功提供給外部使用。

  1. 接著我們回到run方法鹅龄,Looper和MessageQueue已經(jīng)創(chuàng)建成功了币狠,接下來就是啟動Looper循環(huán)了(即Looper.loop()),別忘了只有Looper循環(huán)啟動后我們才能時刻觀察著MessageQueue砾层,只要有Message了才能立馬將Message取出來進(jìn)行分發(fā)處理漩绵。
  2. 在Looper.loop()之前還調(diào)用了一個onLooperPrepared()方法,這個方法是干嘛的呢肛炮? 看代碼可知止吐,只是一個空方法宝踪,在使用HandlerThread時重寫該方法,方便在Looper輪詢消息之前做一些初始化的操作碍扔。
/**
    * Call back method that can be explicitly overridden if needed to execute some
    * setup before Looper loops.
    */
   protected void onLooperPrepared() {
   }

?最后在對象銷毀前瘩燥,調(diào)用下面的方法退出Looper循環(huán)

public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }
public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

?quit方法實(shí)際是調(diào)用MessagQueue的removeAllMessagesLocked,移除所有延遲和非延遲的消息不同,
?quitSafely方法調(diào)用的是removeAllFutureMessagesLocked方法厉膀,該方法只清除延遲的消息,非延遲的消息
還是會進(jìn)行分發(fā)處理二拐。


?HandlerThread分析完啦服鹅,是不是有點(diǎn)蒙,自始至終都沒有出現(xiàn)Handler百新,HandlerThread要怎么用呢企软?
?下面我們就通過一個Demo來說明下HandlerThread是怎么用的?

private WorkHandler mHandler;
    private HandlerThread mHandlerThread;
    /*Handler存在一個構(gòu)造函數(shù)饭望,傳入一個Looper對象仗哨,Handler的handleMessage獲取的是Looper的MessageQueue中的Message
      因此,handleMessage的調(diào)用與Looper對象同屬于一個線程铅辞,這里我們在構(gòu)造時傳入HandlerThread的Looper對象厌漂,
      handleMessage運(yùn)行于HandlerThread線程(也就是一個子線程),所以Handler雖然是在住線程創(chuàng)建斟珊,但是它的
      handleMessage接收到消息是在HandlerThread線程桩卵,執(zhí)行下代碼可以看到打印出如下log:
      D/HandlerThreadDemo: HandlerThread/Demo thread receiver the message from thread: main
      log也說明,message由主線程傳遞到了HandlerThread中倍宾。
    */
    private class WorkHandler extends Handler {
        WorkHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            String str = (String) msg.obj;
            Log.d(TAG, Thread.currentThread().getName() + " thread receiver the message from thread: " + msg.obj);
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handlerthread_demo);
        mHandlerThread = new HandlerThread("HandlerThread/Demo");
        mHandlerThread.start();
        mHandler = new WorkHandler(mHandlerThread.getLooper());
        Message msg = mHandler.obtainMessage();
        msg.obj = Thread.currentThread().getName();
        mHandler.sendMessage(msg);
        
    }

總結(jié):

  1. HandlerThread繼承于Thread,本質(zhì)上也是一個線程胜嗓。
  2. HandlerThread的run方法為本線程創(chuàng)建提供了Looper和MessageQueue對象高职,并開啟了Looper輪詢消息。
  3. 通過在需要發(fā)送Message的線程中創(chuàng)建Handler辞州,為Handler提供來自HandlerThread的Looper對象怔锌。Handler則能
    將消息發(fā)送到HandlerThread上去進(jìn)行處理。
    注意:這里Handler不僅僅能在主線程創(chuàng)建变过,在子線程同樣能夠創(chuàng)建埃元,只需要將對應(yīng)的Looper提供給Handler即可,所以HandlerThread
    不僅適用于和主線程通信媚狰,同樣適用于和其他子線程通信岛杀。
  4. 最后需要注意的是在我們不需要這個looper線程的時候需要手動停止掉,即調(diào)用quit()或者quitSafely()崭孤。
  5. 最后補(bǔ)充一個在實(shí)際開發(fā)過程中使用到HandlerThread的場景:
    存在多個耗時的任務(wù)需要放到開啟子線程依次去處理(串行處理任務(wù)),首先类嗤,HandlerThread是一個子線程糊肠,
    適合處理耗時的任務(wù),其次遗锣,Handler分發(fā)消息是通過MessageQueue頂部的Message不斷的通過Message的next依次取出
    Message货裹,符合任務(wù)的按順序串行處理的要求,所以使用HandlerThread就能完美的解決此需求精偿。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末弧圆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子笔咽,更是在濱河造成了極大的恐慌搔预,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拓轻,死亡現(xiàn)場離奇詭異斯撮,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)扶叉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門勿锅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人枣氧,你說我怎么就攤上這事溢十。” “怎么了达吞?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵张弛,是天一觀的道長。 經(jīng)常有香客問我酪劫,道長吞鸭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任覆糟,我火速辦了婚禮刻剥,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘滩字。我一直安慰自己造虏,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布麦箍。 她就那樣靜靜地躺著漓藕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪挟裂。 梳的紋絲不亂的頭發(fā)上享钞,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天,我揣著相機(jī)與錄音诀蓉,去河邊找鬼嫩与。 笑死寝姿,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的划滋。 我是一名探鬼主播饵筑,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼处坪!你這毒婦竟也來了根资?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤同窘,失蹤者是張志新(化名)和其女友劉穎玄帕,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體想邦,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡裤纹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了丧没。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鹰椒。...
    茶點(diǎn)故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖呕童,靈堂內(nèi)的尸體忽然破棺而出漆际,到底是詐尸還是另有隱情,我是刑警寧澤夺饲,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布奸汇,位于F島的核電站,受9級特大地震影響往声,放射性物質(zhì)發(fā)生泄漏擂找。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一浩销、第九天 我趴在偏房一處隱蔽的房頂上張望贯涎。 院中可真熱鬧,春花似錦撼嗓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至礁遣,卻和暖如春斑芜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背祟霍。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工杏头, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盈包,地道東北人。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓醇王,卻偏偏與公主長得像呢燥,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子寓娩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評論 2 354

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