Android Looper和Handler分析

來源:互聯網
上載者:User

 第一次接觸android應用程式(這裡指的是JAVA層的UI程式,也難怪了,Google放出的API就只支援JAVA應用程式了),很難搞明白內部是如何?的。但是,從原理上分析,應該是有一個訊息迴圈,一個訊息佇列,然後主線程不斷得從訊息佇列中取得訊息並處理之。

然而,google封裝得太厲害了,所以一時半會還是搞不清楚到底是怎麼做的。本文將分析android內的looper,這個是用來封裝訊息迴圈和訊息佇列的一個類,handler其實可以看做是一個工具類,用來向訊息佇列中插入訊息的。好比是Windows API的SendMessage中的HANDLE,這個handle是視窗控制代碼。

//Looper類分析<br />//沒找到合適的分析代碼的辦法,只能這麼來了。每個重要行的上面都會加上注釋<br />//功能方面的代碼會在代碼前加上一段分析<br />public class Looper {<br /> //static變數,判斷是否列印調試資訊。<br /> private static final boolean DEBUG = false;<br /> private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;</p><p> // sThreadLocal.get() will return null unless you've called prepare().<br />//執行緒區域儲存功能的封裝,TLS,thread local storage,什麼意思呢?因為儲存要麼在棧上,例如函數內定義的內部變數。要麼在堆上,例如new或者malloc出來的東西<br />//但是現在的系統比如Linux和windows都提供了執行緒區域儲存空間,也就是這個儲存空間是和線程相關的,一個線程內有一個內部儲存空間,這樣的話我把線程相關的東西就儲存到<br />//這個線程的TLS中,就不用放在堆上而進行同步操作了。<br /> private static final ThreadLocal sThreadLocal = new ThreadLocal();<br />//訊息佇列,MessageQueue,看名字就知道是個queue..<br /> final MessageQueue mQueue;<br /> volatile boolean mRun;<br />//和本looper相關的那個線程,初始化為null<br /> Thread mThread;<br /> private Printer mLogging = null;<br />//static變數,代表一個UI Process(也可能是service吧,這裡預設就是UI)的主線程<br /> private static Looper mMainLooper = null;</p><p> /** Initialize the current thread as a looper.<br /> * This gives you a chance to create handlers that then reference<br /> * this looper, before actually starting the loop. Be sure to call<br /> * {@link #loop()} after calling this method, and end it by calling<br /> * {@link #quit()}.<br /> */<br />//往TLS中設上這個Looper對象的,如果這個線程已經設過了looper的話就會報錯<br />//這說明,一個線程只能設一個looper<br /> public static final void prepare() {<br /> if (sThreadLocal.get() != null) {<br /> throw new RuntimeException("Only one Looper may be created per thread");<br /> }<br /> sThreadLocal.set(new Looper());<br /> }</p><p> /** Initialize the current thread as a looper, marking it as an application's main<br /> * looper. The main looper for your application is created by the Android environment,<br /> * so you should never need to call this function yourself.<br /> * {@link #prepare()}<br /> */<br /> //由framework設定的UI程式的主訊息迴圈,注意,這個主訊息迴圈是不會主動退出的<br />//<br /> public static final void prepareMainLooper() {<br /> prepare();<br /> setMainLooper(myLooper());<br />//判斷主訊息迴圈是否能退出....<br />//通過quit函數向looper發出退出申請<br /> if (Process.supportsProcesses()) {<br /> myLooper().mQueue.mQuitAllowed = false;<br /> }<br /> }</p><p> private synchronized static void setMainLooper(Looper looper) {<br /> mMainLooper = looper;<br /> }</p><p> /** Returns the application's main looper, which lives in the main thread of the application.<br /> */<br /> public synchronized static final Looper getMainLooper() {<br /> return mMainLooper;<br /> }</p><p> /**<br /> * Run the message queue in this thread. Be sure to call<br /> * {@link #quit()} to end the loop.<br /> */<br />//訊息迴圈,整個程式就在這裡while了。<br />//這個是static函數喔!<br /> public static final void loop() {<br /> Looper me = myLooper();//從該線程中取出對應的looper對象<br /> MessageQueue queue = me.mQueue;//取訊息佇列對象...<br /> while (true) {<br /> Message msg = queue.next(); // might block取訊息佇列中的一個待處理訊息..<br /> //if (!me.mRun) {//是否需要退出?mRun是個volatile變數,跨線程同步的,應該是有地方設定它。<br /> // break;<br /> //}<br /> if (msg != null) {<br /> if (msg.target == null) {<br /> // No target is a magic identifier for the quit message.<br /> return;<br /> }<br /> if (me.mLogging!= null) me.mLogging.println(<br /> ">>>>> Dispatching to " + msg.target + " "<br /> + msg.callback + ": " + msg.what<br /> );<br /> msg.target.dispatchMessage(msg);<br /> if (me.mLogging!= null) me.mLogging.println(<br /> "<<<<< Finished to " + msg.target + " "<br /> + msg.callback);<br /> msg.recycle();<br /> }<br /> }<br /> }</p><p> /**<br /> * Return the Looper object associated with the current thread. Returns<br /> * null if the calling thread is not associated with a Looper.<br /> */<br />//返回和線程相關的looper<br /> public static final Looper myLooper() {<br /> return (Looper)sThreadLocal.get();<br /> }</p><p> /**<br /> * Control logging of messages as they are processed by this Looper. If<br /> * enabled, a log message will be written to <var>printer</var><br /> * at the beginning and ending of each message dispatch, identifying the<br /> * target Handler and message contents.<br /> *<br /> * @param printer A Printer object that will receive log messages, or<br /> * null to disable message logging.<br /> */<br />//設定調試輸出對象,looper迴圈的時候會列印相關資訊,用來調試用最好了。<br /> public void setMessageLogging(Printer printer) {<br /> mLogging = printer;<br /> }</p><p> /**<br /> * Return the {@link MessageQueue} object associated with the current<br /> * thread. This must be called from a thread running a Looper, or a<br /> * NullPointerException will be thrown.<br /> */<br /> public static final MessageQueue myQueue() {<br /> return myLooper().mQueue;<br /> }<br />//建立一個新的looper對象,<br />//內部分配一個訊息佇列,設定mRun為true<br /> private Looper() {<br /> mQueue = new MessageQueue();<br /> mRun = true;<br /> mThread = Thread.currentThread();<br /> }</p><p> public void quit() {<br /> Message msg = Message.obtain();<br /> // NOTE: By enqueueing directly into the message queue, the<br /> // message is left with a null target. This is how we know it is<br /> // a quit message.<br /> mQueue.enqueueMessage(msg, 0);<br /> }</p><p> /**<br /> * Return the Thread associated with this Looper.<br /> */<br /> public Thread getThread() {<br /> return mThread;<br /> }<br /> //後面就簡單了,列印,異常定義等。<br /> public void dump(Printer pw, String prefix) {<br /> pw.println(prefix + this);<br /> pw.println(prefix + "mRun=" + mRun);<br /> pw.println(prefix + "mThread=" + mThread);<br /> pw.println(prefix + "mQueue=" + ((mQueue != null) ? mQueue : "(null"));<br /> if (mQueue != null) {<br /> synchronized (mQueue) {<br /> Message msg = mQueue.mMessages;<br /> int n = 0;<br /> while (msg != null) {<br /> pw.println(prefix + " Message " + n + ": " + msg);<br /> n++;<br /> msg = msg.next;<br /> }<br /> pw.println(prefix + "(Total messages: " + n + ")");<br /> }<br /> }<br /> }</p><p> public String toString() {<br /> return "Looper{"<br /> + Integer.toHexString(System.identityHashCode(this))<br /> + "}";<br /> }</p><p> static class HandlerException extends Exception {</p><p> HandlerException(Message message, Throwable cause) {<br /> super(createMessage(cause), cause);<br /> }</p><p> static String createMessage(Throwable cause) {<br /> String causeMsg = cause.getMessage();<br /> if (causeMsg == null) {<br /> causeMsg = cause.toString();<br /> }<br /> return causeMsg;<br /> }<br /> }<br />}

那怎麼往這個訊息佇列中發送訊息呢??調用looper的static函數myQueue可以獲得訊息佇列,這樣你就可用自己往裡邊插入訊息了。不過這種方法比較麻煩,這個時候handler類就發揮作用了。先來看看handler的代碼,就明白了。

class Handler{<br />..........<br />//handler預設建構函式<br />public Handler() {<br />//這個if是幹嘛用的暫時還不明白,涉及到java的深層次的內容了應該<br /> if (FIND_POTENTIAL_LEAKS) {<br /> final Class<? extends Handler> klass = getClass();<br /> if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&<br /> (klass.getModifiers() & Modifier.STATIC) == 0) {<br /> Log.w(TAG, "The following Handler class should be static or leaks might occur: " +<br /> klass.getCanonicalName());<br /> }<br /> }<br />//擷取本線程的looper對象<br />//如果本線程還沒有設定looper,這回拋異常<br /> mLooper = Looper.myLooper();<br /> if (mLooper == null) {<br /> throw new RuntimeException(<br /> "Can't create handler inside thread that has not called Looper.prepare()");<br /> }<br />//無恥啊,直接把looper的queue和自己的queue搞成一個了<br />//這樣的話,我通過handler的封裝機制加訊息的話,就相當於直接加到了looper的訊息佇列中去了<br /> mQueue = mLooper.mQueue;<br /> mCallback = null;<br /> }<br />//還有好幾種建構函式,一個是帶callback的,一個是帶looper的<br />//由外部設定looper<br /> public Handler(Looper looper) {<br /> mLooper = looper;<br /> mQueue = looper.mQueue;<br /> mCallback = null;<br /> }<br />// 帶callback的,一個handler可以設定一個callback。如果有callback的話,<br />//凡是發到通過這個handler發送的訊息,都有callback處理,相當於一個總的集中處理<br />//待會看dispatchMessage的時候再分析<br />public Handler(Looper looper, Callback callback) {<br /> mLooper = looper;<br /> mQueue = looper.mQueue;<br /> mCallback = callback;<br /> }<br />//<br />//通過handler發送訊息<br />//調用了內部的一個sendMessageDelayed<br />public final boolean sendMessage(Message msg)<br /> {<br /> return sendMessageDelayed(msg, 0);<br /> }<br />//FT,又封裝了一層,這回是調用sendMessageAtTime了<br />//因為延時時間是基於當前調用時間的,所以需要獲得絕對時間傳遞給sendMessageAtTime<br />public final boolean sendMessageDelayed(Message msg, long delayMillis)<br /> {<br /> if (delayMillis < 0) {<br /> delayMillis = 0;<br /> }<br /> return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);<br /> }</p><p>public boolean sendMessageAtTime(Message msg, long uptimeMillis)<br /> {<br /> boolean sent = false;<br /> MessageQueue queue = mQueue;<br /> if (queue != null) {<br />//把訊息的target設定為自己,然後加入到訊息佇列中<br />//對於隊列這種資料結構來說,操作比較簡單了<br /> msg.target = this;<br /> sent = queue.enqueueMessage(msg, uptimeMillis);<br /> }<br /> else {<br /> RuntimeException e = new RuntimeException(<br /> this + " sendMessageAtTime() called with no mQueue");<br /> Log.w("Looper", e.getMessage(), e);<br /> }<br /> return sent;<br /> }<br />//還記得looper中的那個訊息迴圈處理嗎<br />//從訊息佇列中得到一個訊息後,會調用它的target的dispatchMesage函數<br />//message的target已經設定為handler了,所以<br />//最後會轉到handler的msg處理上來<br />//這裡有個處理流程的問題<br />public void dispatchMessage(Message msg) {<br />//如果msg本身設定了callback,則直接交給這個callback處理了<br /> if (msg.callback != null) {<br /> handleCallback(msg);<br /> } else {<br />//如果該handler的callback有的話,則交給這個callback處理了---相當於集中處理<br /> if (mCallback != null) {<br /> if (mCallback.handleMessage(msg)) {<br /> return;<br /> }<br /> }<br />//否則交給派生處理,基類預設處理是什麼都不幹<br /> handleMessage(msg);<br /> }<br /> }<br />..........<br />}

 講了這麼多,該怎麼建立和使用一個帶訊息迴圈的線程呢?

//假設在onCreate中建立一個線程<br />//不花時間考慮代碼的完整和嚴謹性了,以講述原理為主。<br />....</p><p>... onCreate(...){</p><p>//痛點是如何把android中的looper和java的thread弄到一起去。<br />//而且還要把隨時取得這個looper用來建立handler<br />//最簡單的辦法就是從Thread派生一個<br />class ThreadWithMessageHandle extends Thread{<br /> //重載run函數<br /> Looper myLooper = null;<br /> run(){<br /> Looper.prepare();//將Looper設定到這個線程中<br /> myLooper = Looper.myLooper();<br /> Looper.loop();開啟訊息迴圈<br />}</p><p> ThreadWithMessageHandle threadWithMgs = new ThreadWithMessageHandle();<br /> threadWithMsg.start();<br /> Looper looper = threadWithMsg.myLooper;//<br />//這裡有個問題.threadWithMgs中的myLooper可能此時為空白<br />//需要同步處理一下<br />//或者像API文檔中的那樣,把handler定義到ThreadWithMessageHandle到去。<br />//外線程獲得這個handler的時候仍然要注意同步的問題,因為handler的建立是在run中的<br /> Handler threadHandler = new Handler(looper);<br /> threadHandler.sendMessage(...)<br />}</p><p>}</p><p>...

好了,handler和looper的分析就都這了,其實原理挺簡單的。

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.