本篇內(nèi)容主要講解“Handler相關(guān)面試題有哪些”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“Handler相關(guān)面試題有哪些”吧!
創(chuàng)新互聯(lián)成立以來(lái)不斷整合自身及行業(yè)資源、不斷突破觀念以使企業(yè)策略得到完善和成熟,建立了一套“以技術(shù)為基點(diǎn),以客戶需求中心、市場(chǎng)為導(dǎo)向”的快速反應(yīng)體系。對(duì)公司的主營(yíng)項(xiàng)目,如中高端企業(yè)網(wǎng)站企劃 / 設(shè)計(jì)、行業(yè) / 企業(yè)門戶設(shè)計(jì)推廣、行業(yè)門戶平臺(tái)運(yùn)營(yíng)、app軟件開發(fā)公司、移動(dòng)網(wǎng)站建設(shè)、微信網(wǎng)站制作、軟件開發(fā)、遂寧托管服務(wù)器等實(shí)行標(biāo)準(zhǔn)化操作,讓客戶可以直觀的預(yù)知到從創(chuàng)新互聯(lián)可以獲得的服務(wù)效果。
1.獲取Message實(shí)例的方式有哪些?哪一種更好?
獲取Message實(shí)例的方法主要有兩種,一種是直接創(chuàng)建,Message msg = new Message
。另一種是通過(guò)Message.obtain()
或者Handler.obtatinMessage()
來(lái)得到一個(gè)Message對(duì)象。更推薦使用后一種方式,這種方式得到的對(duì)象是從對(duì)象回收池中得到,復(fù)用已經(jīng)處理完的Message對(duì)象,而不是重新生成一個(gè)新對(duì)象。Message.obtain()
和Handler.obtatinMessage()
最終都是調(diào)用了Message類的obtain()
方法,查看方法內(nèi)容,就知道是從對(duì)象回收池里得到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.當(dāng)Activity有多個(gè)Handler的時(shí)候,Message消息是否會(huì)混亂?怎么樣區(qū)分當(dāng)前消息由哪個(gè)Handler處理?
不會(huì)混亂,哪個(gè)Handler發(fā)送的消息,到時(shí)候也是這個(gè)handler處理。在發(fā)送消息的時(shí)候,會(huì)綁定target,這個(gè)target就是Handler本身,當(dāng)需要handler調(diào)用dispatchMessage(msg)
處理消息的時(shí)候,這個(gè)Handler就是發(fā)送消息時(shí)綁定的handler。
無(wú)論用哪一種方法發(fā)送消息,最終都會(huì)調(diào)用enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
來(lái)發(fā)送消息
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
這里的this,就是當(dāng)前的handler。在來(lái)看需要Handler處理消息的時(shí)候,取的是哪一個(gè)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(); } }
這是循環(huán)消息時(shí)的部分代碼,處理消息代碼是msg.target.dispatchMessage(msg);
,這里的target就是當(dāng)時(shí)發(fā)送消息的handler。
3.在子線程發(fā)送消息,卻能夠在主線程接收消息,主線程和子線程是怎么樣切換的?
子線程用handler發(fā)送消息,發(fā)送的消息被送到與主線程相關(guān)聯(lián)的MessageQueue,也是主線程相關(guān)聯(lián)的Looper在循環(huán)消息,handler所關(guān)聯(lián)的是主線程的Looper和MessageQueue,所以最后消息的處理邏輯也是在主線程。只有發(fā)送消息是在子線程,其它都是在主線程,Handler與哪個(gè)線程的Looper相關(guān)聯(lián),消息處理邏輯就在與之相關(guān)的線程中執(zhí)行,相應(yīng)的消息的走向也就在相關(guān)聯(lián)的MessageQueue中。所以子線程切換到主線程是很自然的過(guò)程,并沒(méi)有想象中的復(fù)雜。
4.能不能在子線程中創(chuàng)建Handler?
可以,但是在創(chuàng)建前先調(diào)用prepare()方法創(chuàng)建Looper。Handler創(chuàng)建的時(shí)候,會(huì)去檢查是否有創(chuàng)建Looper,如果沒(méi)有創(chuàng)建就會(huì)拋出異常。相關(guān)源碼如下:
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; }
所以我們?cè)谧泳€程需要先調(diào)用prepare()方法創(chuàng)建Looper。這里還多提一點(diǎn),在主線程創(chuàng)建就不需要自己創(chuàng)建Looper,因?yàn)樵贏ctivityTread類里面,已經(jīng)為我們創(chuàng)建好了,相關(guān)源碼如下:
public static void main(String[] args) { ...... Looper.prepareMainLooper();// 為主線程創(chuàng)建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();
這句代碼就是為主線程創(chuàng)建Looper。 所以在主線程中直接創(chuàng)建一個(gè)Handler,就直接可以循環(huán)消息,因?yàn)榘沧康闹骶€程已經(jīng)為我們準(zhǔn)備好了Looper。
5.一個(gè)線程可以有幾個(gè)Handler?幾個(gè)Looper?
一個(gè)線程可以有多個(gè)Handler,但是只有一個(gè)Looper。創(chuàng)建Handler之前,需要?jiǎng)?chuàng)建Looper,否則會(huì)報(bào)錯(cuò)。源碼里面已經(jīng)做了說(shuō)明。
public Handler(Callback callback, boolean async) { ...... mLooper = Looper.myLooper(); if (mLooper == null) {//判斷Looper是否被創(chuàng)建 throw new RuntimeException( "Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
在來(lái)看Looper的創(chuàng)建,是在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)); }
在創(chuàng)建之前去判斷l(xiāng)ooper是否存在,存在就會(huì)拋出Only one Looper may be created per thread
異常,這是在告訴我們一個(gè)線程只能有一個(gè)Looper。而TreadLocal的作用就是線程間隔離,確保一個(gè)線程對(duì)應(yīng)一個(gè)Looper。還可以看看Looperg構(gòu)造方法的源碼
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
MessageQueue的創(chuàng)始化是在Looper的構(gòu)造方法里。不管一個(gè)線程有多少個(gè)Handler,相關(guān)聯(lián)的都是同一個(gè)Looper和MessageQueue。
關(guān)于Handler,可以問(wèn)的問(wèn)題有很多,以上只是抽出一些我認(rèn)為比較重要的問(wèn)題。在尋找答案以后,我將Handler機(jī)制的整個(gè)過(guò)程在腦海中過(guò)了一遍,并且畫了個(gè)草圖。
Handler機(jī)制原理涉及幾個(gè)重要的類:Handler、Message、MessageQueue、Looper。
就用子線程向主線程發(fā)送消息來(lái)說(shuō)明整個(gè)過(guò)程。
首先在主線程創(chuàng)建一個(gè)Handler,在Handler類里面會(huì)創(chuàng)建Looper以及MessageQueue的對(duì)象,并且在Handler構(gòu)造方法里面賦值
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構(gòu)造方法里面創(chuàng)建的MessageQueue。
創(chuàng)建好了Handler實(shí)例,我們就會(huì)在子線程調(diào)用handler.sendMessage(msg);
發(fā)送消息,將message放到MessageQueue里面。在enqueueMessage()
里面就給每個(gè)message設(shè)置target,這個(gè)target就是當(dāng)前的handler。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
再然后調(diào)用Looper.loop();
(在ActivityThread的main()里面已經(jīng)幫我們寫好)開始循環(huán)消息,拿到消息以后就會(huì)用handler取出消息進(jìn)行處理,重點(diǎn)代碼是msg.target.dispatchMessage(msg);
,這里的handler就和一開始我們?yōu)閙essage設(shè)置的Handler對(duì)應(yīng)。
/** * 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); } }
最后我們?cè)贖andler的handleMessage(msg)
里面去處理我們的消息。
這就是子線程給主線程發(fā)送消息的整個(gè)過(guò)程,源碼很多,我只是截取了部分重點(diǎn)。這里多提一點(diǎn)就是線程處理消息。 在線程中處理消息需要做三件事情
1. 先創(chuàng)建一個(gè)Looper (Looper.prepare()
) 2. 再創(chuàng)建Handler,默認(rèn)是和當(dāng)前線程的Looper關(guān)聯(lián)起來(lái) 3. 循環(huán)消息(Looper.loop()
)
這三個(gè)步驟的順序不能調(diào)換。因?yàn)橹骶€程已經(jīng)幫我們創(chuàng)建了Looper,所以我們不需要寫,如果是在子線程創(chuàng)建Looper就需要了。
Handler機(jī)制的理解要靠自己去琢磨,不斷的看源碼,去理解,理清它們之間的互相調(diào)用。只有比較深入的理解了Handler,才能在面試中回答面試官的問(wèn)題,靠死記硬背是不可取的。
到此,相信大家對(duì)“Handler相關(guān)面試題有哪些”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
文章名稱:Handler相關(guān)面試題有哪些
當(dāng)前網(wǎng)址:http://chinadenli.net/article34/jiegse.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供關(guān)鍵詞優(yōu)化、企業(yè)網(wǎng)站制作、網(wǎng)站導(dǎo)航、微信小程序、網(wǎng)頁(yè)設(shè)計(jì)公司、品牌網(wǎng)站制作
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)