Handler和Looper詳解

關於這個話題,其實很早以前就想撰寫相關來的妥當來加加深映像,或者當作技術筆記,可是一直都不知道從哪裏開始比較合適.先看看網上給出的相關的定. 
Hanlder做用: 
1)執行計劃任務,你能夠再預約的實現執行某些任務,能夠模擬定時器 
2)線程間通訊。在Android的應用啓動時,會建立一個主線程,主線程會建立一個消息隊列來處理各類消息。當你建立子線程時,你能夠再你的子線程中拿到父線程中建立的Handler對象,就能夠經過該對象向父線程的消息隊列發送消息了。因爲Android要求在UI線程中更新界面,所以,能夠經過該方法在其它線程中更新界面。 


 
1、    角色描述 
1.Looper:(至關於隧道) 一個線程能夠產生一個Looper 對象,由它來管理此線程裏的Message Queue( 車隊,消息隧道) 。 
2.Handler: 你能夠構造Handler 對象來與Looper 溝通,以便push 新消息到Message Queue 裏;或者接收Looper( 從Message Queue 取出) 所送來的消息。 
3.Message Queue( 消息隊列): 用來存放線程放入的消息。 
4.線程:UI thread 一般就是main thread ,而Android 啓動程序時會替它創建一個Message Queue 。 
每個線程裏可含有一個Looper 對象以及一個MessageQueue 數據結構。在你的應用程序裏,能夠定義Handler 的子類別來接收Looper 所送出的消息。 

2、 執行過程 


當咱們在子線程建立一個Handler的時候,目的就是就能夠經過該對象向父線程的 
消息隊列發送消息了,那麼這個handler對象是怎麼實現的呢? 其實,當建立好一個Handler的時候,在Handler的構造方法中會得到一個主線程的消息隊列監聽器 —— Looper.這裏簡單說一下Looper,,咱們能夠理解它爲一個隧道或者是一直循環的監聽器,咱們一般說的Looper都是主線程Looper,當主線程啓動的時候,它會經過Looper的prepareMainLooper()方法,得到當前線程的Looper實例,而後讓Looper調用loop方法不斷地循環來處理message方法.看下面 



代碼 

public static final void main(String[] args) { 
        SamplingProfilerIntegration.start(); 

        Process.setArgV0("<pre-initialized>"); 

        Looper.prepareMainLooper(); 
        if (sMainThreadHandler == null) { 
            sMainThreadHandler = new Handler(); 
        } 

        ActivityThread thread = new ActivityThread(); 
        thread.attach(false); 

        if (false) { 
            Looper.myLooper().setMessageLogging(new 
                    LogPrinter(Log.DEBUG, "ActivityThread")); 
        } 

        Looper.loop(); 



好吧,可能這裏很難理解,因此咱們一點兒一點兒地來分析.而後再說handler怎麼 
發揮做用的. 

先看 Looper.prepareMainLooper(),這裏發生了什麼? 

public static final void prepareMainLooper() { 
        prepare(); 
        setMainLooper(myLooper()); 
        if (Process.supportsProcesses()) { 
            myLooper().mQueue.mQuitAllowed = false; 
        } 
    } 

看上面的代碼能夠知道,首先Looper的prepareMainLooper()方法中調用了 prepare()方法,這個方法做用是賦予當前線程一個新的Looper對象,看代碼: 

public static final void prepare() { 
        if (sThreadLocal.get() != null) { 
            throw new RuntimeException("Only one Looper may be created  
per thread"); 
        } 
        sThreadLocal.set(new Looper()); 
    } 

好了,明白了這個方法,那下一個方法setMainLooper(myLooper())呢? 繼續看源代碼!咱們先來看myLooper()方法. 

public static final Looper myLooper() { 
        return (Looper)sThreadLocal.get(); 
    } 

這個方法很簡單,它的做用就是得到當前線程的Looper對象,前面咱們剛剛賦予了 一個Looper對象給當前線程,這裏便獲取了它,是否是有種遙相呼應的感受.. (再次聲明,這裏的當前線程指的是主線程或是UI線程),咱們再來看看 setMainLooper()方法:

private synchronized static void setMainLooper(Looper looper) { 
        mMainLooper = looper; 
    } 

也很簡單,和以前的myLooper()方法就是把從當前線程得到Looper賦給一個mMainLooper的變量. 

好了,總結一下,Looper.prepareMainLooper();的做用就是咱們以前說的得到當前線程的Looper實例.下面再到 Looper.loop();看看 

public static final void loop() { 
        Looper me = myLooper(); 
        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. 
        Binder.clearCallingIdentity(); 
        final long ident = Binder.clearCallingIdentity(); 
        
        while (true) { 
            Message msg = queue.next(); // might block 
            //if (!me.mRun) { 
            //    break; 
            //} 
            if (msg != null) { 
                if (msg.target == null) { 
                    // No target is a magic identifier for the quit  
message. 
                    return; 
                } 
                if (me.mLogging!= null) me.mLogging.println( 
                        ">>>>> Dispatching to " + msg.target + " " 
                        + msg.callback + ": " + msg.what 
                        ); 
                msg.target.dispatchMessage(msg); 
                if (me.mLogging!= null) me.mLogging.println( 
                        "<<<<< Finished to    " + msg.target + " " 
                        + msg.callback); 
                
                // Make sure that during the course of dispatching the 
                // identity of the thread wasn't corrupted. 
                final long newIdent = Binder.clearCallingIdentity(); 
                if (ident != newIdent) { 
                    Log.wtf("Looper", "Thread identity changed from  
0x" 
                            + Long.toHexString(ident) + " to 0x" 
                            + Long.toHexString(newIdent) + " while  
dispatching to " 
                            + msg.target.getClass().getName() + " " 
                            + msg.callback + " what=" + msg.what); 
                } 
                
                msg.recycle(); 
            } 
        } 
    } 

這個方法看起來很複雜,其實咱們只須要關注裏面關鍵的幾個點就好了. 
1. Looper me = myLooper();剛纔說過,就是得到當前線程(即主線程)的Looper. 

2. MessageQueue queue = me.mQueue;這個2.MessageQueue在前面已經作了詳細 的介紹,因此就不作解釋了,可是忽然出如今這裏,有點莫名其妙......其實非也, 咱們能夠看到queue是來至於me.mQueue,就是Looper中的一個成員變量,這個 queue是何時產生的呢? 其實還記得剛纔的prepare() 方法麼.這個方法中 實例化了一個Looper——new Looper().queue就是來自於Looper的構造方法,請 看代碼就會明白了. 

private Looper() { 
        mQueue = new MessageQueue(); 
        mRun = true; 
        mThread = Thread.currentThread(); 
    } 

因此不是莫名其妙,是有根據的. 

3. Message msg = queue.next(),在循環體中看到的這段代碼固然就是不斷地將queue中的message一個一個地取出來啦! 

4. msg.target.dispatchMessage(msg);通過以前一系列的判斷處理以後,來到了loop()方法中很是核心的地方,這裏做用就是把這個從queue中得到的message分發給相應的地方來進行處理.好了。。。說了半天,終於能回到咱們開始說的Handler了. 

當時Handler怎麼作才能和以前的步驟產生聯繫呢? Handler的使用咱們必須先實例化並實現它的handleMessage方法.呵呵,其實這個 只是一個引子......當咱們實例化Handler的時候其實就是和以前說過的Looper還有什麼queue,產生聯繫的時候,繼續看代碼: 


  public Handler(Callback callback) { 
        if (FIND_POTENTIAL_LEAKS) { 
            final Class<? extends Handler> klass = getClass(); 
            if ((klass.isAnonymousClass() || klass.isMemberClass() ||  
klass.isLocalClass()) && 
                    (klass.getModifiers() & Modifier.STATIC) == 0) { 
                Log.w(TAG, "The following Handler class should be  
static or leaks might occur: " + 
                    klass.getCanonicalName()); 
            } 
        } 

        mLooper = Looper.myLooper(); 
        if (mLooper == null) { 
            throw new RuntimeException( 
                "Can't create handler inside thread that has not  
called Looper.prepare()"); 
        } 
        mQueue = mLooper.mQueue; 
        mCallback = callback; 
    } 

這下看Handler的構造方法是否是感受很輕鬆?那就好.....以前那麼長的鋪墊沒有白費,若是還不懂,再結合者前面的講解看幾遍.總結下,當實例化一個Handler的時候,這個handler會得到主線程中的Looper對象,和Looper中的成員變量mQueue,好了,全部的拼圖碎片已經籌齊咱們開始拼圖了.咱們日常實例化了Handler是否是就可使用它的sendMessage方法呢?咱們看看sendMessage到底幹了什麼,它其實調用了一個sendMessageAtTime()方法. 

public boolean sendMessageAtTime(Message msg, long uptimeMillis) 
    { 
        boolean sent = false; 
        MessageQueue queue = mQueue; 
        if (queue != null) { 
            msg.target = this; 
            sent = queue.enqueueMessage(msg, uptimeMillis); 
        } 
        else { 
            RuntimeException e = new RuntimeException( 
                this + " sendMessageAtTime() called with no mQueue"); 
            Log.w("Looper", e.getMessage(), e); 
        } 
        return sent; 
    } 

哦.... 相信你們已經能明白了. 
首先msg.target = this;是否是很熟悉,我幫大家回憶一下.在Looper的looper的講解中,第四個重點. 

msg.target.dispatchMessage(msg) 

是否是有種醍醐灌頂的感受.這下全部的拼圖組成一張圖了,從這句核心代碼就明白了,主線程Looper負責把全部的Message分發給相對應的handler來處理,而後把這個Message再次放入隊列.而target就是指的Handler.最後指定的Handler的HandleMessage來處理這個消息, 

這個就是整個Handler和Looper工做的流程....
相關文章
相關標籤/搜索