淺談Handler消息處理機制

1.什麼是Handler

咱們平時在開發中,常常用到Handler,用來發送消息,處理消息。 或者作一些延遲發送消息,跨線程發消息,或者更新UI,或者去實現一些定時輪詢的操做。 安卓發展至今,已經有不少框架,能夠代替這樣原生Handler的通訊方式。 好比Eventbus, Rxjava,AyncTask...等等。可是實際上底層依然是對Handler的封裝。那麼Handler到底是什麼?java

簡而言之:面試

Handler就是Android系統提供給咱們,用來更新UI的一套機制,也是一套消息處理機制。經過Handler,能夠用來發送消息,處理消息。若是不遵循這樣的機制,就沒有辦法更新UI,會拋出異常。框架

2. Handler消息機制的五個重要角色

1.Handler: 消息的發送和處理者async

2.Message: 消息ide

3.MessageQueue: 消息存放的隊列oop

4.Looper : 從消息隊列裏面一條一條取消息的消息偵聽器,或者消息泵post

5.線程: 當前是在哪一個線程學習

3.通俗的理解這個消息機制

也許不少人在網上都看過不少資料去解釋這樣一個消息機制,可是若是向別人闡述這樣一個原理的時候,或者面試的時候,我相信不少人仍是模棱兩可,弄不太清楚。 那麼,經過一個簡單的生活案例,來幫助理解一下:ui

eg: 咱們都知道讀書的時候,咱們常常須要向老師請假之類。this

好比:報告老師,我要上洗手間。而後老師說:容許,快去吧。
過了一會。
又給老師報告:報告老師,後面有同窗踢我凳子。老師說:你先坐前面來,不要理他。

固然,這只是比喻。咱們經過這個比喻,來比較生動形象的去理解handler就容易多了。

1.handler:學生。

2.message:報告老師的內容

3.Looper :老師本身

4.messagequeue: 老師的耳朵和聽力記憶
解釋:

*   學生(handler)向老師(Looper)舉手

*   說 「我要上洗手間」這個報告(message),

*   而後老師的耳朵(messagequeue)聽到了這個報告,

*   先是反饋了學生的請求(dispatchHandle),告訴這個同窗,贊成他的請求,

*   因而學生本身就上洗手間去了(handleMessage)

        ps:      老師:     在這個過程當中,就是一個消息的接受者,源源不斷的接受學生的各類報告或者消息,存在老師的記憶裏,         老師又沒有分身術,只有一張嘴,因此同一時間,只能根據記憶中的消息,一條一條的反饋給學生,處理他們的請求。       學生:       在這個過程當中,咱們能夠發現,學生既是消息的發出者,又是消息的處理者。      收到老師的贊成後,因而就高高興興的去上洗手間去了。    

整個過程如圖:

由圖能夠看出:
1.handler負責發送消息,接收處理消息
2.Looper負責接收handler發過來的消息
3.MessageQueue就作爲Looper消息泵內部的消息容器 3.Looper在內部,將發送過來的消息,交給自身的消息隊列,並按時間順序加入其中 4.Looper在內部,經過不斷循環的方式,將消息從隊列中一個一個取出,回傳給handler

此時你有可能會問:那麼Handler它怎麼知道應該往哪發消息,而且發給哪一個Looper,加入這個Looper的消息隊列呢?答案在構造方法中。

4.Handler的建立和Looper的關聯關係

Handler的建立,調用Handler的構造方法便可。 new Handler(). 那麼咱們看下Handler的構造方法都作了些什麼呢?Handler的構造方法有好幾個,咱們先從空構造看起:

    //從這開始看起,空構造  
    public Handler() {  
        this(null, false);  
    }  
    public Handler(Callback callback) {  
        this(callback, false);  
    }

    public Handler(Looper looper) {  
        this(looper, null, false);  
    }

    public Handler(Looper looper, Callback callback) {  
        this(looper, callback, false);  
    }

    public Handler(boolean async) {  
        this(null, async);  
    }

   //而後看到這個構造方法  
    public Handler(Callback callback, boolean async) {  
        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());  
            }  
        }  
        //構造方法內部,有一個默認的looper對象,而且這個對象是經過myLooper獲得,也就是當前線程的looper。  
        //當前線程:默認狀況就是主線程,爲何會是主線程,後面會講  
        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;  
        mAsynchronous = async;  
    }

    public Handler(Looper looper, Callback callback, boolean async) {  
        mLooper = looper;  
        mQueue = looper.mQueue;  
        mCallback = callback;  
        mAsynchronous = async;  
    }

上面代碼能夠看出,在handler的默認構造器中,有一句:mLooper=Looper.myLooper(). 這說明Handler 默認的時候,有一個Looper對象。可是這個Looper對象是怎麼建立得來的呢?點擊去發現:就是根據當前線程返回一個looper對象。當前線程是什麼呢?默認狀況下,就是主線程,也就是MainLooper.

 /**  
     \* Return the Looper object associated with the current thread.  Returns  
     \* null if the calling thread is not associated with a Looper.  
     */  
     //解釋一下:根據當前線程,返回looper對象。若是線程沒有與之關聯的Looper,那麼返回空  
    public static @Nullable Looper myLooper() {  
        return sThreadLocal.get();  
    }

好了,看到這裏,咱們就看到了Handler和一個默認looper對象如何關聯的。至於looper對象的如何建立,後面再詳細講解。
咱們能夠得出結論:

*   默認狀況下:new Handler()中的looper對象是主線程的looper對象 ,那麼消息就是發給主線程的handler進行處理。

*   須要和特定線程的Handler通訊,咱們就須要調用new Handler(Looper looper) ,傳入特定線程的Looper對象便可。

*   Looper對象是屬於什麼線程,那麼handler,就是往哪一個線程進行發送消息和處理消息。

5. 發送消息的過程

搞清楚Handler和Looper如何進行關聯的關係之後,咱們從消息的發送開始理解。
Handler 如何發送消息,消息從哪裏產生? 要搞清楚消息的產生,咱們首先要知道handler發送一條空消息是sendEmptyMessage().那麼咱們就跟蹤這個方法: 方法的參數what ,就是一個標記位,先無論它。

  /**  
    public final boolean sendEmptyMessage(int what)  
    {  
        return sendEmptyMessageDelayed(what, 0);  
    }

再繼續往下跟蹤

//這裏能夠看到,sendEmptyMessage()默認會調用sendEmptyMessageDelayed()方法  
 public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {  
 //內部,經過Message.obtain(),產生了一條消息Message  
        Message msg = Message.obtain();  
        msg.what = what;  
        //而後再調用sendMessageDelayed()方法  
        return sendMessageDelayed(msg, delayMillis);  
    }
 public final boolean sendMessageDelayed(Message msg, long delayMillis)  
    {  
        if (delayMillis < 0) {  
            delayMillis = 0;  
        }  
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);  
    }

這裏能夠看到發送空消息時,Message是在方法內部,經過Message.obtain產生的。 而且會層層調用,最終是這麼一個方法路徑:

sendEmptyMessage-->sendMessageDelay-->sendMessageAtTime ,最終都是調用sendMessageAtTime()來發送消息的。

6.消息發出後的過程

經過上面sendMessageAtTime()發送消息,消息產生後,按照前面的圖的理解,它是會發送出一個Messgae,而且發給Looper,由Looper加入到消息隊列的。那麼代碼上是怎樣的呢?

 public boolean sendMessageAtTime(Message msg, long uptimeMillis) {  
        //1.首先獲取到queue對象,  
        MessageQueue queue = mQueue;  
        if (queue == null) {  
            RuntimeException e = new RuntimeException(  
                    this + " sendMessageAtTime() called with no mQueue");  
            Log.w("Looper", e.getMessage(), e);  
            return false;  
        }  
        //2\\. 將消息,插入到這個消息queue隊列中去,而且附帶了一個時間值  
        return enqueueMessage(queue, msg, uptimeMillis);  
    }

咱們能夠看出:
在最終發送消息的地方,拿到了Queue隊列對象,而後就把消息加入到了這個Queue隊列中去了。 這個Queue對象的獲取,咱們能夠觀察handler的構造看到:每一次Handler建立的時候,會拿到looper對象,再經過looper對象來獲取到內部的這個Queue隊列對象。回顧handler的建立可發現,以下:

  public Handler(Callback callback, boolean async) {  
        ....  
        ....  
        //1先拿到線程的looper  
        mLooper = Looper.myLooper();  
        if (mLooper == null) {  
            throw new RuntimeException(  
                "Can't create handler inside thread that has not called Looper.prepare()");  
        }  
        //2經過looper獲取到消息queue隊列  
        mQueue = mLooper.mQueue;  
        mCallback = callback;  
        mAsynchronous = async;  
    }

再看下消息加入queue隊列的過程

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {  
        //這裏能夠發現 消息msg的target屬性:是this,也就是handler 自身當前對象  
        msg.target = this;  
        if (mAsynchronous) {  
            msg.setAsynchronous(true);  
        }  
        return queue.enqueueMessage(msg, uptimeMillis);  
    }

在加入隊列的時候,能夠看到消息msg的地址,也就是處理者target(由誰來最終處理消息),賦值爲this。
這也就說明了,handler默認狀況下,發送消息的是它本身,處理消息的也是它本身。
總結一下上面消息的發送過程:

*   發送出去 sendEmptyMessage(),消息的產生在這個裏面

*   調用sendEmptyMessageDelay()

*   內部調用sendMessageDelay()

*   內部調用sendMessageAtTime()

*   內部經過當前線程,首先獲取到mQueue對象,也就是 消息隊列對象MessageQueue

*   當queue不爲空的狀況下,設置 target,發送給誰, 默認是this --handler本身

*   而後把消息message,放到消息隊列中。queue.enqueueMessage(消息,時間);

####7. 偵聽消息和取消息的過程
咱們知道,消息發送到Looper中後,就進入到了隊列中,而後等待Looper的循環取出進行分發。那麼Looper.loop() 這個循環的過程是在何時觸發的呢?因爲Android默認的線程是主線程,因此在應用進程啓動以後,就會進入ActivityThread這個主線程中,而在這個ActivityThread中的main()方法,就會觸發Looper.loop()開始偵聽主線程的消息。
 //main方法
 public static void main(String[] args) {

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

        Looper.prepareMainLooper();

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

        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);
        
        //1. 觸發loop
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    
能夠看到,在主線程的ActivityThread main()方法內部,觸發Looper.loop()。學過java的人都知道,main方法是一個類的主入口.
那麼loop是如何進行取消息的呢?點進去繼續往下看

 public static void loop() {  
        // 1.拿到當前線程looper對象  
        final Looper me = myLooper();  
        if (me == null) {  
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
        }  
        2\. 獲得 looper對象內部的消息隊列queue  
        final MessageQueue queue = me.mQueue;  
 ...  
 ...  
        //3.死循環,配合隊列進行取消息  
        for (;;) {  
        //4.取出一條消息  
            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  
            final Printer logging = me.mLogging;  
            if (logging != null) {  
                logging.println(">>>>> Dispatching to " + msg.target + " " +  
                        msg.callback + ": " + msg.what);  
            }

            final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

            final long traceTag = me.mTraceTag;  
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {  
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));  
            }  
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();  
            final long end;  
            try {  
             //4.分發消息  
                msg.target.dispatchMessage(msg);  
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();  
            } finally {  
                if (traceTag != 0) {  
                    Trace.traceEnd(traceTag);  
                }  
            }  
            
        }  
    }

能夠看到:

Looper.loop()方法中

2.經過myLooper,拿到一個Looper對象

3.取出looper中的消息隊列 mQueue

4.而後經過一個死循環,裏面經過mQueue.next來取消息
若是消息爲空了,就return掉,

5.若是消息不爲空, 就會調用
msg.target.dispatchMessage(消息)。將消息發出去
msg.target:就是handler本身
handler.dispatchMessage(消息)方法,將消息回調到dispatch中去

消息如何分發給handler處理的呢

經過上面Looper取消息的過程。咱們看到了。消息在最終取出後,會經過 dispatchMsg進行回傳,交給處理者。  這個處理者,被賦值在Msg.target中. Msg的target本質上就是handler。
在前面發送消息,將消息加入隊列的時候,咱們看到過,msg.target=this,是在那個時候將消息的處理者進行了賦值。
因此這裏,當取出消息後,就又經過handler,經過它的dispatchMessage(msg)進行回傳的。

     }  
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();  
            final long end;  
            try {  
            //消息分發,回傳給處理者 。也就是handler本身。  
                msg.target.dispatchMessage(msg);  
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();  
            } finally {  
                if (traceTag != 0) {  
                    Trace.traceEnd(traceTag);  
                }  
            }

繼續往下跟蹤能夠發現:

  /**  
     \* Handle system messages here.  
     */  
    public void dispatchMessage(Message msg) {  
        //若是消息的callback 處理者不爲空,就經過這個callback進行處理。這個callback是什麼呢? 後續再講解  
        if (msg.callback != null) {  
            handleCallback(msg);  
        } else {  
        //默認狀況下,走這個分支  
            if (mCallback != null) {  
            //若是全局的callback不爲空,就會執行這裏,再也不執行handlemessgae  
                if (mCallback.handleMessage(msg)) {  
                    return;  
                }  
            }  
            // 回傳給消息處理者,處理消息,  
            handleMessage(msg);  
        }  
    }

分發消息後,默認會進入第二個分支。一般狀況下callback是沒有進行設置,因此直接就會回調handleMessage(msg).走完整個流程.進入處理消息流程
但是這個msg.callback是什麼呢? 還有全局的mCallback又是什麼呢? 分發消息的時候,若是這些不爲空。又會觸發什麼呢?繼續日後看

handler中的Callback又是什麼

因爲咱們知道通知UI刷新的機制有4種方式:

view.post(runnable)  
handler.post(runnable)  
runOnUiThread(runnable)  
handler.sendMsg(runnable)

咱們跟蹤一下view.post(runnable).進入view源碼:

//傳入一個runnable  
   public boolean post(Runnable action) {  
        final AttachInfo attachInfo = mAttachInfo;  
        if (attachInfo != null) {  
        //而後將runnable,交給handler的post執行  
            return attachInfo.mHandler.post(action);  
        }

        // Postpone the runnable until we know on which thread it needs to run.  
        // Assume that the runnable will be successfully placed after attach.  
        getRunQueue().post(action);  
        return true;  
    }

而後進入到handler的內部中post

public final boolean post(Runnable r)  
    {  
       return  sendMessageDelayed(getPostMessage(r), 0);  
    }  
      
複製代碼能夠看到runnable,被包裝進入到了發送消息的getPostMessage(r)方法中。跟進去:  
   private static Message getPostMessage(Runnable r) {  
        Message m = Message.obtain();  
        //看到沒!!! callback,原來就是指 咱們使用view.post(runnbale)的這個對象  
        //這個runnable,會賦值給msg.callback。  
        //因此當咱們使用別的方式進行更新UI或者消息通訊的時候,在handleMessage前,就會進入callback不爲空的判斷  
        m.callback = r;  
        return m;  
    }

也就是這裏msg.callback: 再貼一遍代碼

  /**  
     \* Handle system messages here.  
     */  
    public void dispatchMessage(Message msg) {  
        //若是消息的callback 處理者不爲空,就經過這個callback進行處理。這裏就是使用別的方式進行更新UI或者消息的時候,進入這裏處理,再也不進入handleMessage  
        if (msg.callback != null) {  
            handleCallback(msg);  
        } else {  
        //默認狀況下,走這個分支  
            if (mCallback != null) {  
            //若是全局的callback不爲空,就會執行這裏,再也不執行handlemessgae  
                if (mCallback.handleMessage(msg)) {  
                    return;  
                }  
            }  
            // 回傳給消息處理者,處理消息,  
            handleMessage(msg);  
        }  
    }

而mCallback 則是咱們初始化構造handler的時候,傳入的callback,進行攔截消息處理使用。

總結

咱們能夠發現,最終都是進入到了handler的post方法中,經過handler機制來實現。
未完待續....
繼續講解更深刻的細節...

8Looper的建立過程

這裏發現有一個sThreadLocal對象,咱們的looper對象就是從這個對象中獲取的。這個ThreadLocal對象,其實就是一個與線程相關的對象,保存了線程的相關變量,狀態等等。再繼續往下看:發現最終就是從這個線程相關的對象中,內部有一個map對象,從裏面獲取得來。這個地方只是一個單純的get

    public T get() {  
        Thread t = Thread.currentThread();  
        ThreadLocalMap map = getMap(t);  
        if (map != null) {  
            ThreadLocalMap.Entry e = map.getEntry(this);  
            if (e != null) {  
                @SuppressWarnings("unchecked")  
                T result = (T)e.value;  
                return result;  
            }  
        }  
        return setInitialValue();  
    }

這個地方依然不是咱們想要的答案,咱們想看的是Looper對象是在哪裏建立的。

今年金九銀十我花一個月的時間收錄整理了一套知識體系,若是有想法深刻的系統化的去學習的,能夠點擊傳送門,我會把我收錄整理的資料都送給你們,幫助你們更快的進階。

相關文章
相關標籤/搜索