您好,登錄后才能下訂單哦!
本篇內容主要講解“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、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是主線程的LoopermQueue = 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相關面試題有哪些”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。