Thread、Handler和HandlerThread關(guān)系詳解
前言
前幾天看到一道面試題:Thread、Handler和HandlerThread有什么區(qū)別?,這個(gè)題目有點(diǎn)意思,對(duì)于很多人來(lái)說(shuō),可能對(duì)Thread和Handler很熟悉,主要涉及到Android的消息機(jī)制(Handler、Message、Looper、MessageQueue),詳見《 從Handler.post(Runnable r)再一次梳理Android的消息機(jī)制(以及handler的內(nèi)存泄露)》
但是這個(gè)HandlerThread是拿來(lái)做什么的呢?它是Handler還是Thread?我們知道Handler是用來(lái)異步更新UI的,更詳細(xì)的說(shuō)是用來(lái)做線程間的通信的,更新UI時(shí)是子線程與UI主線程之間的通信。那么現(xiàn)在我們要是想子線程與子線程之間的通信要怎么做呢?當(dāng)然說(shuō)到底也是用Handler+Thread來(lái)完成(不推薦,需要自己操作Looper),Google官方很貼心的幫我們封裝好了一個(gè)類,那就是剛才說(shuō)到的:HandlerThread。(類似的封裝對(duì)于多線程的場(chǎng)景還有AsyncTask)
使用方法
還是先來(lái)看看HandlerThread的使用方法:
首先新建HandlerThread并且執(zhí)行start()
private HandlerThread mHandlerThread; ...... mHandlerThread = new HandlerThread("HandlerThread"); handlerThread.start();
創(chuàng)建Handler,使用mHandlerThread.getLooper()生成Looper:
final Handler handler = new Handler(mHandlerThread.getLooper()){ @Override public void handleMessage(Message msg) { System.out.println("收到消息"); } };
然后再新建一個(gè)子線程來(lái)發(fā)送消息:
new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1000);//模擬耗時(shí)操作 handler.sendEmptyMessage(0); } catch (InterruptedException e) { e.printStackTrace(); } } }).start();
最后一定不要忘了在onDestroy釋放,避免內(nèi)存泄漏:
@Override protected void onDestroy() { super.onDestroy(); mHandlerThread.quit(); }
執(zhí)行結(jié)果很簡(jiǎn)單,就是在控制臺(tái)打印字符串:收到消息
原理
整個(gè)的使用過(guò)程我們根本不用去關(guān)心Handler相關(guān)的東西,只需要發(fā)送消息,處理消息,Looper相關(guān)的東西交給它自己去處理,還是來(lái)看看源碼它是怎么實(shí)現(xiàn)的,先看構(gòu)造方法:
public class HandlerThread extends Thread {}
HandlerThread其實(shí)還是一個(gè)線程,它跟普通線程有什么不同?
public class HandlerThread extends Thread { int mPriority; int mTid = -1; Looper mLooper; public HandlerThread(String name) { super(name); mPriority = Process.THREAD_PRIORITY_DEFAULT; } ...... }
答案是多了一個(gè)Looper,這個(gè)是子線程獨(dú)有的Looper,用來(lái)做消息的取出和處理。繼續(xù)看看HandlerThread這個(gè)線程的run方法:
protected void onLooperPrepared() { } @Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper();//生成Looper notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared();//空方法,在Looper創(chuàng)建完成后調(diào)用,可以自己重寫邏輯 Looper.loop();//死循環(huán),不斷從MessageQueue中取出消息并且交給Handler處理 mTid = -1; }
主要就是做了一些Looper的操作,如果我們自己使用Handler+Thread來(lái)實(shí)現(xiàn)的話也要進(jìn)行這個(gè)操作,再來(lái)看看getLooper()方法:
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; }
方法很簡(jiǎn)單,就是加了個(gè)同步鎖,如果已經(jīng)創(chuàng)建了(isAlive()返回true)但是mLooper為空的話就繼續(xù)等待,直到mLooper創(chuàng)建成功,最后看看quit方法,值得一提的是有兩個(gè):
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; }
quitSafely是針對(duì)在消息隊(duì)列中還有消息或者是延遲發(fā)送的消息沒(méi)有處理的情況,調(diào)用這個(gè)方法后都會(huì)被停止掉。
總結(jié)
HandlerThread的使用方法還是比較簡(jiǎn)單的,但是我們要明白一點(diǎn)的是:如果一個(gè)線程要處理消息,那么它必須擁有自己的Looper,并不是Handler在哪里創(chuàng)建,就可以在哪里處理消息的。
如果不用HandlerThread的話,需要手動(dòng)去調(diào)用Looper.prepare()和Looper.loop()這些方法。
以上就是對(duì)Thread、Handler和HandlerThread關(guān)系 的資料整理,后續(xù)繼續(xù)補(bǔ)充相關(guān)資料,謝謝大家對(duì)本站的支持!
相關(guān)文章
Android編程實(shí)現(xiàn)帶有圖標(biāo)的ListView并帶有長(zhǎng)按菜單效果示例
這篇文章主要介紹了Android編程實(shí)現(xiàn)帶有圖標(biāo)的ListView并帶有長(zhǎng)按菜單效果,結(jié)合實(shí)例形式分析了Android帶圖標(biāo)的ListView及菜單功能相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-06-06Android中Glide獲取圖片Path、Bitmap用法詳解
這篇文章主要介紹了Android中Glide獲取圖片Path、Bitmap用法以及代碼分析,需要的朋友們參考一下吧。2017-12-12Android實(shí)現(xiàn)自定義滑動(dòng)式抽屜菜單效果
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)自定義滑動(dòng)式抽屜效果菜單,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-05-05Android PopupWindow全屏詳細(xì)介紹及實(shí)例代碼
這篇文章主要介紹了 Android PopupWindow全屏詳細(xì)介紹及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2016-12-12Android仿新浪微博自定義ListView下拉刷新(4)
這篇文章主要為大家詳細(xì)介紹了Android仿新浪微博自定義ListView下拉刷新,重點(diǎn)介紹了Adapter的詳細(xì)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11Android開發(fā)之資源文件用法實(shí)例總結(jié)
這篇文章主要介紹了Android開發(fā)之資源文件用法,結(jié)合實(shí)例形式總結(jié)分析了Android開發(fā)過(guò)程中針對(duì)資源文件的常見操作技巧,需要的朋友可以參考下2016-02-02Android 7.0以上版本實(shí)現(xiàn)應(yīng)用內(nèi)語(yǔ)言切換的方法
本篇文章主要介紹了Android 7.0以上版本實(shí)現(xiàn)應(yīng)用內(nèi)語(yǔ)言切換的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-02-02