Handler是個老生常談的問題,我相信幾乎全部的Android開發者都會使用Handler,那關於Handler還有什麼好講的嗎?Handler若是僅僅是使用的話,確實沒什麼好講的,可是Handler倒是一個幾乎全部面試官都會問的問題,不一樣的要求問的深度也不同,今天我就帶你們學習一下關於Handler你所必需要掌握的知識。程序員
首先有四個對象咱們必需要了解一下Handler、Looper、ThreadLocal還有MessageQueue。面試
首先咱們要明白Handler消息機制是用來幹嗎的?Handler是把其餘線程切換到Handler所在的線程,注意,這裏不是UI線程。雖然咱們的大多數使用Handler的場景,都是咱們在子線程作了一下耗時的操做(IO或者數據庫),當子線程執行完之後咱們可能須要更新UI,這個時候咱們用Handler來處理(sendMessage()或者post())就把子線程切換到了UI線程了。假如說,咱們如今有這麼一個需求,線程A發個信息給線程B(線程A、線程B都不是主線程),這個時候咱們用Handler依然能夠作,只須要在線程B中建立好Handler就能夠了(關於如何在子線程建立Handler我會在下面詳細說明)。因此,Handler並非把其餘線程切換到主線程(UI線程),而是切換到它所在的線程,這一點必定要弄清楚。數據庫
Handler消息機制裏面最核心的類,消息輪詢。Handler要想切換線程(或者說發送消息),必需要先得到當前線程的Looper,而後調用Looper.loop()方法把MessageQueue裏面的message輪詢出來"交"給Handler來處理,至於如何「交」給Handler的,下面我會經過源碼帶你們分析。緩存
ThreadLocal是Looper內部的一個,它雖然前面有個「Thread」,但其實它並非線程,它至關於一個線程內部的存儲類。剛纔在講Looper的時候咱們說到,「Handler要想切換線程(或者說發送消息),必需要先得到當前線程的Looper」,那如何得到當前線程的Looper呢?正是經過ThreadLocal,它能夠在不一樣線程之間互不干擾地存儲數據。ThreadLocal裏面有一個內部靜態類對象ThreadLocalMap,經過其set()和get()方法對數據對象進行保存和讀取。數據結構
MessageQueue——消息隊列,雖然它叫消息隊列,但實際上的結構並非一個隊列,而是一種單鏈表的數據結構,只是使用列隊的形式對數據進場作添加或者移除。MessageQueue是在Looper被建立的時候由Looper生成的。同一線程下,Handler發送的全部message都會被加入到MessageQueue裏面,當Message被loop()之後,就會從消息隊列中移除。Message我沒有單獨拿出來,由於確實比較簡單,就是消息自己,它能夠攜帶兩個int型參數,若是要傳比較複雜的參數就用obj屬性,what屬性用來區分是哪一個Handler發送的信息。架構
Handler是把其餘線程切換到它所在的線程,使用Handler發消息以前要先建立Looper對象,建立和讀取Looper都須要使用ThreadLocal,它能夠在不一樣線程之間互不干擾地保存數據。在建立Looper對象的同時也把MessageQueue建立好了,Handler所發的message會被添加到MessageQueue對象裏面,Looper.loop()方法之後會把MessageQueue上的Message輪詢出來。鏈接這幾個對象的還有一條很重要的線,那就是線程,記住,以上的過程都要保證Handler、Looper、MessageQueue在同一線程,這樣,才能保證Handler的消息機制被正確使用。ide
注意:咱們這裏是爲了讓你們更好地學習Handler,因此要在子線程建立Handler,現實使用場景,不多會在子線程建立Handleroop
在主線程(UI線程)建立Handler,我相信全部人都會post
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mainHandler = new Handler(); mainHandler.post(new Runnable() { @Override public void run() { Log.e("qige_test", "thread_name=" + Thread.currentThread().getName()); } }); }
咱們先按照主線程的方式在子線程建立一個Handler試一下,看看會有什麼樣的結果學習
new Thread(){ @Override public void run() { childHandler=new Handler(); childHandler.post(new Runnable() { @Override public void run() { Log.e("qige_test","thread_name="+Thread.currentThread().getName()); } }); } }.start();
沒錯,如圖所示尚未建立Looper,那麼如何建立Looper呢?圖中有句話已經給出了答案——Looper.prepare(),同時爲了讓咱們發送的消息被輪詢到,還必需要調用Looper.loop(); 因此在完整的在子線程建立Handler的代碼以下:
new Thread(){ @Override public void run() { //建立Looper,同時建立MessageQueue Looper.prepare(); childHandler=new Handler(); childHandler.post(new Runnable() { @Override public void run() { Log.e("qige_test","thread_name="+Thread.currentThread().getName()); } }); //輪詢 Looper.loop(); } }.start();
這樣就能夠在子線程裏面建立Handler了,看到這裏,你可能會問,爲何我在主線程建立Handler的時候,沒有調用Looper.prepare()和Looper.loop()?這個問題咱們先放一下,咱們先從源碼角度把Handler消息機制分析一下。
先看一下建立Looper和MessageQueue的方法Looper.prepare()
public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { //ThreadLocal來了 if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
這裏就出現了我剛纔說的ThreadLocal對象。Android規定每一個線程有且僅有一個Looper,因此爲了防止不一樣的線程之間的Looper相互影響,使用ThreadLocal對象來存儲Looper。
再看一眼new Looper()的源碼
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
MessageQueue也出來了,建立Looper的同時建立MessageQueue。
下面咱們看Handler.post()或者是Handler.sendMessage()他們本質是同樣的,把消息加入到消息隊列當中
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
post()或者sendMessagexxx()最終都會調用sendMessageAtTime(Message msg, long uptimeMillis)方法,咱們直接看這個方法的源碼
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); }
都很簡單,最後一步enqueueMessage(queue, msg, uptimeMillis);是把消息加入隊列,我們再點開看一下
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { //把當前的Handler賦值給msg.target msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
關鍵要看的地方是 msg.target = this剛纔講Message的時候沒有提,Message的target屬性就是一個Handler對象,這裏直接把當前的Handler對象賦值過去了。最後一行:queue.enqueueMessage(msg, uptimeMillis)是把message加入到消息隊列裏面,具體的實現我抓不到了,知道這句話是把message加入到MessageQueue裏面就行。
下面分析* Looper.loop() 方法的關鍵部分源碼
public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } **標註1** final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. ........................ ...................... **標註2** for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } .................................... ................................... try { **標註3** msg.target.dispatchMessage(msg); dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } ........................................ ........................................ **標註4** msg.recycleUnchecked(); } }
看源碼的時候注意看一下標註,一個個地來說;
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
這裏面msg.callback是個Runnable對象,也就是當咱們使用handler.post(Runnable r)的方法的話,這時候就直接去調用Runnable對象裏面的東西了,若是使用的是handler.sendMessagexxx(),就是去調用咱們重寫的handleMessage(msg)方法了。
若是調用post()方法發送消息
mainHandler.post(new Runnable() { @Override public void run() { //這裏會被調用 Log.e("qige_test","thread_name="+Thread.currentThread().getName()); } });
若是調用sendMessagexxx()
mainHandler.sendEmptyMessage(0); mainHandler=new Handler(){ @Override public void handleMessage(Message msg) { //這裏會被調用 Log.e("qige_test","what="+msg.what); } };
咱們再回到上一級源碼
以上基本就是一個完整的Handler消息機制的過程,我再帶你們好好回顧一下:
1.Looper.prepare();//這一步建立了Looper和MessageQueue
2.handler.sendMessagexxxx(); // 把message添加到MessageQueue上
3.Looper.loop();//輪詢消息
4.handler.dispatchMessage(msg);//處理消息
好了,到了該解決歷史遺留問題的時候了,爲何咱們在主線程建立handler不須要調用Looper.prepare()和Looper.loop()呢?
這是由於,主線程已經給咱們建立好了,在哪裏建立好的呢?
在Java裏面,有一個程序的主入口,就是靜態main方法
public class Test { //程序入口 public static void main(String... args){ }
在Android裏面呢,一樣有這麼一個main方法入口,它在ActivityThread類中。在咱們App啓動過程當中,會調用到ActivityTread的main()方法(因爲篇幅問題,該過程沒有辦法詳細講,後期會推文章單獨說一下),下面咱們直接看ActivityTread的main()方法的源碼
爲了方便理解,仍是隻看關鍵部分代碼
public static void main(String[] args) { .... //建立Looper和MessageQueue對象,用於處理主線程的消息 Looper.prepareMainLooper(); //建立ActivityThread對象 ActivityThread thread = new ActivityThread(); //創建Binder通道 (建立新線程) thread.attach(false); //消息輪詢 Looper.loop(); }
看到這裏,我想你應該明白了爲啥在主線程裏建立Handler不須要調用Looper.prepare()和Looper.loop()方法了吧,由於在App啓動的時候,ActivityThread已經給咱們建立好了。不過,須要注意的是,我剛纔在講Looper.loop()源碼的時候說過這裏面會有一個無限死循環,那麼程序運行到這裏豈不是要永遠卡在這了呢,那咱們的Activity、Service都是咋執行的呢? 看關鍵的源碼這一句thread.attach(false),註釋上其實解釋的很清楚,經過Binder建立了新的線程,在這個新的線程裏面運行了Activity、Service等組件,而之因此Android爲何採用這種方式,讓咱們留在之後再說,你只須要知道,Looper.loop()不會形成卡頓,主線程已經給咱們建立好了Looper和MessageQueue對象就能夠了。
開篇我就說過,Handler幾乎全部人都會用,但僅僅會用是不夠,要知其然更知其因此然。不少面試官願意問Handler相關的問題,好好閱讀這篇文章,它會讓你在面試的時候事半功倍。
有些東西你不只要懂,並且要可以很好地表達出來,可以讓面試官承認你的理解,例如Handler機制,這個是面試必問之題。有些晦澀的點,或許它只活在面試當中,實際工做當中你壓根不會用到它,可是你要知道它是什麼東西。
最後文末我爲你們準備了一套精品Android架構師教程,保證你學了之後保證薪資上升一個臺階。(如下是一小部分,獲取更多其餘精講進階架構視頻資料能夠加我wx:X1524478394 免費獲取)
一下是今天給你們分享的一些獨家乾貨:
①Android開發核心知識點筆記
②對標「阿里 P7」 40W+年薪企業資深架構師成長學習路線圖
③面試精品集錦彙總
④全套體系化高級架構視頻
Android精講視頻領取學習後更加是如虎添翼!進軍BATJ大廠等(備戰)!如今都說互聯網寒冬,其實無非就是你上錯了車,且穿的少(技能),要是你上對車,自身技術能力夠強,公司換掉的代價大,怎麼可能會被裁掉,都是淘汰末端的業務Curd而已!現現在市場上初級程序員氾濫,這套教程針對Android開發工程師1-6年的人員、正處於瓶頸期,想要年後突破本身漲薪的,進階Android中高級、架構師對你更是如魚得水,趕快領取吧!
上述【高清技術腦圖】以及【配套的架構技術PDF】能夠 加我wx:X1524478394 免費獲取