Handler相關面試題你答對多少?怎樣清晰表達拿下面試官?
Handler機制是面試官非常喜歡問的知識點,原始碼我看完幾遍,還是會覺得不清晰,裡面的程式碼非常繞。
後來我決定放棄探究細節,先把相關的類和呼叫的方法畫一個草圖,然後理清互相呼叫的關係,再結合關於Handler的高頻面試題,去尋找答案,這樣一輪下來,會對Handler有更深的認識。
現在把這個過程,面試題尋找的答案以及相關的解釋整理成這篇文章,如有不對,歡迎大家予以指正。
高頻面試題
1.獲取Message例項的方式有哪些?哪一種更好?
獲取Message例項的方法主要有兩種,一種是直接建立,
Message msg = new Message
。另一種是透過
Message.obtain()
或者
Handler.obtatinMessage()
來得到一個Message物件。更推薦使用後一種方式,這種方式得到的物件是從物件回收池中得到,複用已經處理完的Message物件,而不是重新生成一個新物件。
Message.obtain()
和
Handler.obtatinMessage()
最終都是呼叫了Message類的
obtain()
方法,檢視方法內容,就知道是從物件回收池裡得到Message。
public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); }
2.當Activity有多個Handler的時候,Message訊息是否會混亂?怎麼樣區分當前訊息由哪個Handler處理?
不會混亂,哪個Handler傳送的訊息,到時候也是這個handler處理。在傳送訊息的時候,會繫結target,這個target就是Handler本身,當需要handler呼叫
dispatchMessage(msg)
處理訊息的時候,這個Handler就是傳送訊息時繫結的handler。
無論用哪一種方法傳送訊息,最終都會呼叫
enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
來傳送訊息
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
這裡的this,就是當前的handler。在來看需要Handler處理訊息的時候,取的是哪一個handler,下面貼出主要原始碼。
public static void loop() { ...... for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger ...... if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0; final long dispatchEnd; try { msg.target.dispatchMessage(msg); dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } ...... msg.recycleUnchecked(); } }
這是迴圈訊息時的部分程式碼,處理訊息程式碼是
msg.target.dispatchMessage(msg);
,這裡的target就是當時傳送訊息的handler。
3.在子執行緒傳送訊息,卻能夠在主執行緒接收訊息,主執行緒和子執行緒是怎麼樣切換的?
子執行緒用handler傳送訊息,傳送的訊息被送到與主執行緒相關聯的MessageQueue,也是主執行緒相關聯的Looper在迴圈訊息,handler所關聯的是主執行緒的Looper和MessageQueue,所以最後訊息的處理邏輯也是在主執行緒。只有傳送訊息是在子執行緒,其它都是在主執行緒,Handler與哪個執行緒的Looper相關聯,訊息處理邏輯就在與之相關的執行緒中執行,相應的訊息的走向也就在相關聯的MessageQueue中。所以子執行緒切換到主執行緒是很自然的過程,並沒有想象中的複雜。
4.能不能在子執行緒中建立Handler?
可以,但是在建立前先呼叫prepare()方法建立Looper。Handler建立的時候,會去檢查是否有建立Looper,如果沒有建立就會丟擲異常。相關原始碼如下:
public Handler(Callback callback, boolean async) { ...... mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
所以我們在子執行緒需要先呼叫prepare()方法建立Looper。這裡還多提一點,在主執行緒建立就不需要自己建立Looper,因為在ActivityTread類裡面,已經為我們建立好了,相關原始碼如下:
public static void main(String[] args) { ...... Looper.prepareMainLooper();// 為主執行緒建立looper // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line. // It will be in the format "seq=114" long startSeq = 0; if (args != null) { for (int i = args.length - 1; i >= 0; --i) { if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) { startSeq = Long.parseLong( args[i].substring(PROC_START_SEQ_IDENT.length())); } } } ActivityThread thread = new ActivityThread(); thread.attach(false, startSeq); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
Looper.prepareMainLooper();
這句程式碼就是為主執行緒建立Looper。 所以在主執行緒中直接建立一個Handler,就直接可以迴圈訊息,因為安卓的主執行緒已經為我們準備好了Looper。
5.一個執行緒可以有幾個Handler?幾個Looper?
一個執行緒可以有多個Handler,但是隻有一個Looper。建立Handler之前,需要建立Looper,否則會報錯。原始碼裡面已經做了說明。
public Handler(Callback callback, boolean async) { ...... mLooper = Looper.myLooper(); if (mLooper == null) {//判斷Looper是否被建立 throw new RuntimeException( "Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
在來看Looper的建立,是在prepare()方法裡。
// sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
在建立之前去判斷looper是否存在,存在就會丟擲
Only one Looper may be created per thread
異常,這是在告訴我們一個執行緒只能有一個Looper。而TreadLocal的作用就是執行緒間隔離,確保一個執行緒對應一個Looper。還可以看看Looperg構造方法的原始碼
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
MessageQueue的創始化是在Looper的構造方法裡。不管一個執行緒有多少個Handler,相關聯的都是同一個Looper和MessageQueue。
關於Handler,可以問的問題有很多,以上只是抽出一些我認為比較重要的問題。在尋找答案以後,我將Handler機制的整個過程在腦海中過了一遍,並且畫了個草圖。
Handler機制中重要類的相互關聯圖
Handler機制原理涉及幾個重要的類:Handler、Message、MessageQueue、Looper。
就用子執行緒向主執行緒傳送訊息來說明整個過程。
首先在主執行緒建立一個Handler,在Handler類裡面會建立Looper以及MessageQueue的物件,並且在Handler構造方法裡面賦值
final Looper mLooper; final MessageQueue mQueue; public Handler(Callback callback, boolean async) { ...... mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
mLooper = Looper.myLooper();
得到的Looper是主執行緒的Looper
mQueue = mLooper.mQueue;
得到的MessageQueue就是在Looper構造方法裡面建立的MessageQueue。
建立好了Handler例項,我們就會在子執行緒呼叫
handler.sendMessage(msg);
傳送訊息,將message放到MessageQueue裡面。在
enqueueMessage()
裡面就給每個message設定target,這個target就是當前的handler。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
再然後呼叫
Looper.loop();
(在ActivityThread的main()裡面已經幫我們寫好)開始迴圈訊息,拿到訊息以後就會用handler取出訊息進行處理,重點程式碼是
msg.target.dispatchMessage(msg);
,這裡的handler就和一開始我們為message設定的Handler對應。
/** * Handle system messages here. */ public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
最後我們在Handler的
handleMessage(msg)
裡面去處理我們的訊息。
這就是子執行緒給主執行緒傳送訊息的整個過程,原始碼很多,我只是擷取了部分重點。這裡多提一點就是執行緒處理訊息。 線上程中處理訊息需要做三件事情
1. 先建立一個Looper (
Looper.prepare()
) 2. 再建立Handler,預設是和當前執行緒的Looper關聯起來 3. 迴圈訊息(
Looper.loop()
)
這三個步驟的順序不能調換。因為主執行緒已經幫我們建立了Looper,所以我們不需要寫,如果是在子執行緒建立Looper就需要了。
Handler機制的理解要靠自己去琢磨,不斷的看原始碼,去理解,理清它們之間的互相呼叫。只有比較深入的理解了Handler,才能在面試中回答面試官的問題,靠死記硬背是不可取的。
最後
多花時間去看,多動腦,不偷懶,總能把Handler拿下。
而且,有些東西你不僅要懂,而且要能夠很好地表達出來,能夠讓面試官認可你的理解,就像Handler機制,這個是面試必問之題。
不管怎麼樣,不論是什麼樣的大小面試,要想不被面試官虐的不要不要的,只有刷爆面試題題做好全面的準備,當然除了這個還需要在平時把自己的基礎打紮實,這樣不論面試官怎麼樣一個知識點裡往死裡鑿,你也能應付如流啊~
在這裡貢獻我準備頭條時的面試內容,可以全部免費分享給大家。
注意:需要Android學習PDF大全、Android進階之光、高階Android開發強化實戰、深入探索Android熱修復技術原理,還有演算法題的朋友,可以直接私信我【核心】
這些都是我閒暇還會反覆翻閱的精品資料!
Android學習PDF大全
這份Android學習PDF大全真的包含了方方面面了,內含Java基礎知識點、Android基礎、Android進階延伸、演算法合集等等
Android進階之光
第 1章 Android新特性
.第 2章 Material Design
第 3章 View體系與自定義 View
第 4章 多執行緒程式設計
第 5章 網路程式設計與網路框架
第 6章 設計模式
第 7章 事件匯流排
第 8章 函式響應式程式設計
第 9章 註解與依賴注入框架
第 10章 應用架構設計
第 11章 系統架構與 MediaPlayer框架
高階Android開發強化實戰
1.進階基礎
2高階控制元件
3.專案架構
4.晌應式程式設計
5.炫酷功能
6.精美動畫
7.Katlin SVG
8.測試與最佳化
深入探索Android熱修復技術原理
介紹了 Android 熱修復的核 技術原理 結合 ophix 熱修復開發實踐過程,
從程式碼修復、資源修復、 so 庫修復 大方向進行了詳細的技術剖析與解讀,業內少有的深度講解 Android 系統熱修復技術的書籍,對於原理、程式碼講解得非常清晰和深入,值得我們 Android工程師研讀。
我的這份學習合集,可以有效的幫助大家掌握知識點。
總之也是在這裡幫助大家學習提升進階,也節省大家在網上搜尋資料的時間來學習,也可以分享給身邊好友一起學習
獲取方式:轉發+關注,私信我【核心】即可或者直接
面試:如果不準備充分的面試,完全是浪費時間,更是對自己的不負責!
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69952849/viewspace-2678752/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 面試被問懵?帶你一步一步深入Handler原始碼,不信還拿不下面試官?面試原始碼
- 面試官問你陣列和ArrayList怎麼答?面試陣列
- Android 高階面試-1:Handler 相關Android面試
- 面試官:我們來聊一聊Redis吧,你瞭解多少就答多少面試Redis
- 關於Tomcat的13道面試題,你能答對幾個?Tomcat面試題
- 精選的這19道C/C+面試題,你能答對多少呢?面試題
- 面試官“你的期望薪資是多少?”聰明的程式設計師都是這樣答的!面試程式設計師
- FLume相關面試題面試題
- 集合相關面試題面試題
- ES相關面試題面試題
- 常見Linux運維面試題,你答對了嗎?Linux運維面試題
- 2019年一線大廠最全JVM面試100問!你能答對多少?JVM面試
- 6道常見的python面試題,你答對了嗎?Python面試題
- 面試官:說說你之前負責的系統,QPS 能達到多少?面試
- 面試官帶你學Android——面試中Handler 這些必備知識點你都知道嗎?面試Android
- 面試:Handler 的工作機制是怎樣的呢?面試
- 面試題(五)常見vue相關面試題總結面試題Vue
- Linux常見面試題,你會多少?Linux面試題
- 面試時,面試官問:你以後的規劃是怎樣的 如何回答呢面試
- 如何拿下面試官?2019的一些面試實戰小結告訴你!面試
- springboot+springcloud相關面試題Spring BootGCCloud面試題
- 【Java】幾道讓你拿offer的面試題Java面試題
- 網易JAVA面試你能答對幾題?(文末附答案解析)Java面試
- 答面試官問:怎麼實現介面冪等性面試
- 騰訊這套SpringMvc面試題你懂多少(面試必備)SpringMVC面試題
- 邦芒支招:如何判斷面試官對你有好感表現面試
- 吊打面試官!MySQL靈魂100問,你能答出多少?面試MySql
- 面試-關於Http協議你瞭解多少,有多少說多少面試HTTP協議
- 對於單例模式面試官會怎樣提問呢?你又該如何回答呢?單例模式面試
- shell有哪些面試簡答題技巧?Linux面試題Linux面試題
- 10道網路安全基礎面試題,你答對了幾道?面試題
- 答面試官問:怎麼設計標準通訊介面面試
- 系統設計 相關面試題面試題
- Java中JVM相關面試題-整理JavaJVM面試題
- Unity3D相關面試題Unity3D面試題
- 面試遇到的redis相關問題面試Redis
- 怎樣回答技術面試題?面試題
- 面試官問你 - 自定義View跟繪製流程相關知識點??面試View