淺談Handler消息處理機制

1.什麼是Handler

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

簡而言之:面試

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

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

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

2.Message: 消息async

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

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

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

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);
                }
            }
          
        }
    }

複製代碼

能夠看到:

    1. 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對象是在哪裏建立的。

相關文章
相關標籤/搜索