Android HandlerThread詳解(源碼分析)

目錄:

目錄

1. 前言

本篇文章是對(duì) Android HandlerThread 類(lèi)的學(xué)習(xí)邻寿,通過(guò)簡(jiǎn)單的例子槐沼,及分析源碼來(lái)深入學(xué)習(xí)壁晒。同時(shí)例子將以 Java & Kotlin 兩種代碼形式展示。

1.1 定義

HandlerThread: 一個(gè)擁有 Looper 對(duì)象的線(xiàn)程坛怪。

繼承于 Thread 類(lèi)墅拭,并擁有一個(gè) Looper 對(duì)象活玲,可以利用該 Looper 對(duì)象來(lái)創(chuàng)建 Handler 對(duì)象,就像正常的線(xiàn)程一樣谍婉,通過(guò)調(diào)用 start() 方法來(lái)開(kāi)啟線(xiàn)程翼虫。

1.2 使用場(chǎng)景

適用于子線(xiàn)程多任務(wù)的工作場(chǎng)景。如:多個(gè)網(wǎng)絡(luò)請(qǐng)求屡萤,多個(gè)I/O操作

2. 使用方法

其使用方法很簡(jiǎn)單:

  1. 在主線(xiàn)程中新建一個(gè) HandlerThread 對(duì)象,然后調(diào)用 start() 方法啟動(dòng)該線(xiàn)程掸宛。
  2. 創(chuàng)建一個(gè)主線(xiàn)程Handler 對(duì)象死陆,關(guān)聯(lián)APP主Looper對(duì)象,用于子線(xiàn)程與主線(xiàn)程之間的溝通唧瘾。
  3. 創(chuàng)建一個(gè)工作線(xiàn)程Handler 對(duì)象措译,關(guān)聯(lián) HandlerThread 的 Looper 對(duì)象。
  4. 發(fā)送消息給工作線(xiàn)程饰序,工作線(xiàn)程Handler 接收消息领虹,并處理消息,然后調(diào)用主線(xiàn)程求豫。Handler塌衰,發(fā)送信息給主線(xiàn)程诉稍,通知主線(xiàn)程做UI工作。
  5. 當(dāng)對(duì)應(yīng)的 Activity 銷(xiāo)毀時(shí)最疆,退出 HandlerThread杯巨,終止消息循環(huán)。

比如:我們用兩個(gè) ProgressBar 來(lái)模擬在子線(xiàn)程中進(jìn)行下載任務(wù)努酸,點(diǎn)擊按鈕開(kāi)始下載服爷,ProgressBar 的進(jìn)度代表著下載進(jìn)度。
如下所示:


HandlerThread子線(xiàn)程執(zhí)行多任務(wù).gif

2.1 Java版本

具體代碼如下:

public class HandlerThreadActivity extends AppCompatActivity {
    private final int SET_PROGRESS_BAR_1 = 1;
    private final int SET_PROGRESS_BAR_2 = 2;
    private HandlerThread myHandlerThread;
    private Handler mWorkHandler, mMainHandler;
    private ProgressBar progressBar1, progressBar2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_thread);

        TextView titleTv = findViewById(R.id.title_tv);
        titleTv.setText("HandlerThread");

        progressBar1 = findViewById(R.id.progress_bar_1);
        progressBar2 = findViewById(R.id.progress_bar_2);
        Button startBtn1 = findViewById(R.id.start_progress_bar_1_btn);
        Button startBtn2 = findViewById(R.id.start_progress_bar_2_btn);

        //創(chuàng)建HandlerThread對(duì)象
        myHandlerThread = new HandlerThread("myHandlerThread");
        //啟動(dòng)線(xiàn)程
        myHandlerThread.start();

        //創(chuàng)建主線(xiàn)程Handler获诈,關(guān)聯(lián)APP的主Looper對(duì)象
        mMainHandler = new Handler(getMainLooper());

        //創(chuàng)建工作線(xiàn)程Handler仍源,關(guān)聯(lián)HandlerThread的Looper對(duì)象,所以它無(wú)法與主線(xiàn)程通訊
        mWorkHandler = new Handler(myHandlerThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);

                switch (msg.what) {
                    case SET_PROGRESS_BAR_1:
                        //設(shè)置Progress Bar 1
                        for (int i = 1; i <= 4; i++) {
                            try {
                                //模擬耗時(shí)工作
                                Thread.sleep(1000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }

                            final int progressSchedule = I;
                            //在工作線(xiàn)程中舔涎,通過(guò)主線(xiàn)程Handler, 傳遞信息給主線(xiàn)程笼踩,通知主線(xiàn)程處理UI工作。
                            mMainHandler.post(new Runnable() {
                                @Override
                                public void run() {
                                    progressBar1.setProgress(progressSchedule);
                                }
                            });
                        }
                        break;
                    case SET_PROGRESS_BAR_2:
                        //設(shè)置Progress Bar 2
                        for (int i = 1; i <= 4; i++) {
                            try {
                                //模擬耗時(shí)工作
                                Thread.sleep(1300);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }

                            final int progressSchedule2 = I;
                            //在工作線(xiàn)程中终抽,通過(guò)主線(xiàn)程Handler, 傳遞信息給主線(xiàn)程戳表,通知主線(xiàn)程處理UI工作。
                            mMainHandler.post(new Runnable() {
                                @Override
                                public void run() {
                                    progressBar2.setProgress(progressSchedule2);
                                }
                            });
                        }
                        break;
                    default:
                        break;
                }
            }
        };

        startBtn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //通過(guò)工作線(xiàn)程Handler昼伴,mWorkHandler發(fā)送處理 progress bar 1 的信息給工作線(xiàn)程匾旭。
                Message msg = new Message();
                msg.what = SET_PROGRESS_BAR_1;
                mWorkHandler.sendMessage(msg);
            }
        });

        startBtn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //通過(guò)工作線(xiàn)程Handler mWorkHandler發(fā)送處理 progress bar 2 的信息給工作線(xiàn)程。
                Message msg = new Message();
                msg.what = SET_PROGRESS_BAR_2;
                mWorkHandler.sendMessage(msg);
            }
        });

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        myHandlerThread.quit();
    }
}

2.2 Kotlin版本

Kotlin 版本代碼如下:

class TestHandlerThreadActivity : AppCompatActivity(), View.OnClickListener {
    var mHandlerThread: HandlerThread? = null
    var mMainHandler: Handler? = null
    var mWorkHandler: WorkHandler? = null

    override fun onClick(v: View?) {
        when (v?.id) {
            R.id.handlerThreadStart1Btn -> mWorkHandler?.obtainMessage(1)?.sendToTarget()
            R.id.handlerThreadStart2Btn -> mWorkHandler?.obtainMessage(2)?.sendToTarget()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test_handler_thread)

        mMainHandler = Handler(Looper.getMainLooper())

        mHandlerThread = HandlerThread("JereTest")
        mHandlerThread!!.start()
        mWorkHandler = WorkHandler(mHandlerThread!!.looper)

        handlerThreadStart1Btn.setOnClickListener(this)
        handlerThreadStart2Btn.setOnClickListener(this)
    }

    inner class WorkHandler(looper: Looper) : Handler(looper) {

        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            when (msg.what) {
                1 -> {
                    for (i in 1..5) {
                        Thread.sleep(1000)
                        mMainHandler?.post { handlerThreadProgressBar1.progress = I }
                    }
                }
                2 -> {
                    for (j in 1..5) {
                        Thread.sleep(1000)
                        mMainHandler?.post { handlerThreadProgressBar2.progress = j }
                    }
                }
            }
        }

    }

    override fun onDestroy() {
        super.onDestroy()
        mHandlerThread?.quit()
    }
    
}

3. 源碼分析

按使用步驟來(lái)分析源碼:

HandlerThread使用方法

步驟一:在主線(xiàn)程中新建一個(gè) HandlerThread 對(duì)象圃郊,然后調(diào)用 start() 方法啟動(dòng)該線(xiàn)程价涝。

//創(chuàng)建 HandlerThread 對(duì)象
myHandlerThread = new HandlerThread("myHandlerThread");
//啟動(dòng)線(xiàn)程,執(zhí)行 run() 方法
myHandlerThread.start();

//創(chuàng)建 HandlerThread 對(duì)象持舆,看其構(gòu)造函數(shù)
/**
 * HandlerThread的構(gòu)造函數(shù)
 * @param name色瘩,線(xiàn)程名字是我們傳入的,用于標(biāo)記改線(xiàn)程
 */
public HandlerThread(String name) {
    super(name);
    //指定該線(xiàn)程的優(yōu)先級(jí)為標(biāo)準(zhǔn)線(xiàn)程優(yōu)先級(jí)
    mPriority = Process.THREAD_PRIORITY_DEFAULT;
}

//啟動(dòng)線(xiàn)程
@Override
public void run() {
    //獲得線(xiàn)程標(biāo)識(shí)符
    mTid = Process.myTid();
    //為該線(xiàn)程創(chuàng)建一個(gè) Looper 對(duì)象逸寓,具體見(jiàn)下:
    Looper.prepare();
    synchronized (this) {
        //獲取通過(guò) Looper.prepare() 方法創(chuàng)建的 Looper 對(duì)象居兆。
        mLooper = Looper.myLooper();
        //喚醒那些為創(chuàng)建Looper對(duì)象而阻塞的線(xiàn)程
        notifyAll();
    }
    //設(shè)置該線(xiàn)程的優(yōu)先級(jí),比如設(shè)置為標(biāo)準(zhǔn)線(xiàn)程優(yōu)先級(jí)
    Process.setThreadPriority(mPriority);
    //消息循環(huán)前做的處理竹伸,即:如果想在消息循環(huán)前做一些處理泥栖,則需要復(fù)寫(xiě)onLooperPreopared()方法。
    onLooperPrepared();
    //循環(huán)該Looper對(duì)象中的消息隊(duì)列
    Looper.loop();
    mTid = -1;
}

步驟二:創(chuàng)建一個(gè)主線(xiàn)程Handler 對(duì)象勋篓,關(guān)聯(lián)APP主Looper對(duì)象吧享,用于子線(xiàn)程與主線(xiàn)程之間的溝通。

//創(chuàng)建主線(xiàn)程Handler譬嚣,關(guān)聯(lián)APP的主Looper對(duì)象
mMainHandler = new Handler(getMainLooper());

其具體源碼分析內(nèi)容請(qǐng)看另外一篇文章Android Handler 深入學(xué)習(xí)及源碼分析

步驟三. 創(chuàng)建一個(gè)工作線(xiàn)程Handler 對(duì)象钢颂,關(guān)聯(lián) HandlerThread 的 Looper 對(duì)象。
其具體實(shí)現(xiàn)方法如下:

//創(chuàng)建工作線(xiàn)程Handler拜银,關(guān)聯(lián)HandlerThread的Looper對(duì)象殊鞭,所以它無(wú)法與主線(xiàn)程通訊
mWorkHandler = new Handler(myHandlerThread.getLooper()) {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
            ···略···
    }
};

通過(guò) getLooper() 方法獲取 HandlerThread 的 Looper 對(duì)象遭垛,其源碼如下:

/**
 * 該方法返回該線(xiàn)程所關(guān)聯(lián)的Looper對(duì)象。
 * 如果這個(gè)線(xiàn)程沒(méi)有啟動(dòng)钱豁,或者由于任何原因isAlive()返回false耻卡,那么這個(gè)方法將返回null。
 * 如果這個(gè)線(xiàn)程已經(jīng)啟動(dòng)牲尺,這個(gè)方法將阻塞卵酪,直到looper被初始化。
 * @return The looper.
 */
public Looper getLooper() {
    //線(xiàn)程未激活谤碳,返回 null
    if (!isAlive()) {
        return null;
    }
    
    //如果該線(xiàn)程已經(jīng)啟動(dòng)溃卡,則阻塞該方法,直到創(chuàng)建好Looper對(duì)象蜒简。
    synchronized (this) {
        while (isAlive() && mLooper == null) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
    }
    return mLooper;
}

步驟四:發(fā)送消息給工作線(xiàn)程瘸羡,工作線(xiàn)程Handler 接收消息,并處理消息搓茬,然后調(diào)用主線(xiàn)程Handler犹赖,發(fā)送信息給主線(xiàn)程,通知主線(xiàn)程做UI工作卷仑。
Handler 通過(guò) sendMessage(Msg) 及 post(Runnable) 方法傳遞消息峻村,本篇博客不展開(kāi)具體分析,具體源代碼分析見(jiàn)上一篇博客Android Handler 深入學(xué)習(xí)及源碼分析

步驟五:當(dāng)對(duì)應(yīng)的 Activity 銷(xiāo)毀時(shí)锡凝,退出 HandlerThread粘昨,終止消息循環(huán)。
當(dāng)當(dāng)前 Activity 退出銷(xiāo)毀時(shí)窜锯,復(fù)寫(xiě) onDestry() 方法张肾,退出 HandlerThread,如:

@Override
protected void onDestroy() {
    super.onDestroy();
    myHandlerThread.quit();
}

通過(guò) quit() 方法退出 HandlerThread锚扎,終止消息循環(huán)吞瞪,具體源代碼分析,見(jiàn)下:

/**
 * 終止 Handler 關(guān)聯(lián)的 looper 消息循環(huán)驾孔,不在處理消息隊(duì)列中的任何消息尸饺。
 * 當(dāng) looper 被停止后再?lài)L試請(qǐng)求消息入消息隊(duì)列,會(huì)失敗助币,比如:sendMessage(Message) 會(huì)返回 false。
 * 使用該方法可能不安全螟碎,因?yàn)?looper 在沒(méi)有循環(huán)分發(fā)完所有消息時(shí)就被停止了眉菱。
 * 考慮使用quitsafe() 方法來(lái)代替,以確保所有未完成的工作以有序的方式完成掉分。
 *
 * @return 如果Looper停止消息循環(huán)俭缓,返回True; 如果線(xiàn)程尚未開(kāi)始運(yùn)行克伊,則返回false。
 */
public boolean quit() {
    //獲取 Handler 所關(guān)聯(lián)的 Looper 對(duì)象华坦。
    Looper looper = getLooper();
    //如果 Looper 不為空愿吹,停止其消息循環(huán),返回true
    if (looper != null) {
        looper.quit();
        return true;
    }
    //如果 Looper 為空惜姐,即現(xiàn)場(chǎng)還未開(kāi)始運(yùn)行犁跪,返回 false。
    return false;
}

/**
 * 安全的終止 Handler 關(guān)聯(lián)的 Looper 消息循環(huán)歹袁,處理消息隊(duì)列中所有已到期的消息坷衍,而未到期的掛起的延遲消息將不被傳遞
 * 處理完消息隊(duì)列中的所有消息后立即停止 Looper 消息循環(huán)
 * 當(dāng) looper 被停止后再?lài)L試請(qǐng)求消息入消息隊(duì)列,會(huì)失敗条舔,比如:sendMessage(Message) 會(huì)返回 false枫耳。
 * 如果線(xiàn)程還未開(kāi)始或已經(jīng)結(jié)束(getLooper 返回 null),返回false孟抗;另外迁杨,當(dāng) Looper 被要求 quit() 停止消息循環(huán)時(shí),返回true凄硼。
 *
 * @return 如果Looper停止消息循環(huán)铅协,返回True; 如果線(xiàn)程尚未開(kāi)始運(yùn)行,則返回false帆喇。
 */
public boolean quitSafely() {
    Looper looper = getLooper();
    if (looper != null) {
        //
        looper.quitSafely();
        return true;
    }
    return false;
}

至此 HandlerThread 的源碼也就分析結(jié)束了警医。
其實(shí)分享文章的最大目的正是等待著有人指出我的錯(cuò)誤,如果你發(fā)現(xiàn)哪里有錯(cuò)誤坯钦,請(qǐng)毫無(wú)保留的指出即可预皇,虛心請(qǐng)教。

另外婉刀,如果你覺(jué)得文章不錯(cuò)吟温,對(duì)你有所幫助,請(qǐng)給我點(diǎn)個(gè)贊突颊,就當(dāng)鼓勵(lì)鲁豪,謝謝~Peace~!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末律秃,一起剝皮案震驚了整個(gè)濱河市爬橡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌棒动,老刑警劉巖糙申,帶你破解...
    沈念sama閱讀 223,207評(píng)論 6 521
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異船惨,居然都是意外死亡柜裸,警方通過(guò)查閱死者的電腦和手機(jī)缕陕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,455評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)疙挺,“玉大人扛邑,你說(shuō)我怎么就攤上這事☆砣唬” “怎么了蔬崩?”我有些...
    開(kāi)封第一講書(shū)人閱讀 170,031評(píng)論 0 366
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)锦爵。 經(jīng)常有香客問(wèn)我舱殿,道長(zhǎng),這世上最難降的妖魔是什么险掀? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,334評(píng)論 1 300
  • 正文 為了忘掉前任沪袭,我火速辦了婚禮,結(jié)果婚禮上樟氢,老公的妹妹穿的比我還像新娘冈绊。我一直安慰自己,他們只是感情好埠啃,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,322評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布死宣。 她就那樣靜靜地躺著肉津,像睡著了一般儒溉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上边灭,一...
    開(kāi)封第一講書(shū)人閱讀 52,895評(píng)論 1 314
  • 那天潦牛,我揣著相機(jī)與錄音眶掌,去河邊找鬼。 笑死巴碗,一個(gè)胖子當(dāng)著我的面吹牛朴爬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播橡淆,決...
    沈念sama閱讀 41,300評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼召噩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了逸爵?” 一聲冷哼從身側(cè)響起具滴,我...
    開(kāi)封第一講書(shū)人閱讀 40,264評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎师倔,沒(méi)想到半個(gè)月后构韵,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,784評(píng)論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,870評(píng)論 3 343
  • 正文 我和宋清朗相戀三年贞绳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片致稀。...
    茶點(diǎn)故事閱讀 40,989評(píng)論 1 354
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡冈闭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出抖单,到底是詐尸還是另有隱情萎攒,我是刑警寧澤,帶...
    沈念sama閱讀 36,649評(píng)論 5 351
  • 正文 年R本政府宣布矛绘,位于F島的核電站耍休,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏货矮。R本人自食惡果不足惜羊精,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,331評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望囚玫。 院中可真熱鬧喧锦,春花似錦、人聲如沸抓督。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,814評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)铃在。三九已至阵具,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間定铜,已是汗流浹背阳液。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,940評(píng)論 1 275
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留宿稀,地道東北人趁舀。 一個(gè)月前我還...
    沈念sama閱讀 49,452評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像祝沸,于是被迫代替她去往敵國(guó)和親矮烹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,995評(píng)論 2 361

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