1. 程式人生 > >Android Handler訊息機制原始碼解析

Android Handler訊息機制原始碼解析

好記性不如爛筆頭,今天來分析一下Handler的原始碼實現

Handler機制是Android系統的基礎,是多執行緒之間切換的基礎。下面我們分析一下Handler的原始碼實現。

Handler訊息機制有4個類合作完成,分別是Handler,MessageQueue,Looper,Message
Handler : 獲取訊息,傳送訊息,以及處理訊息的類
MessageQueue:訊息佇列,先進先出
Looper : 訊息的迴圈和分發
Message : 訊息實體類,分發訊息和處理訊息的就是這個類

主要工作原理就是:
Looper 類裡面有一個無限迴圈,不停的從MessageQueue佇列中取出訊息,然後把訊息分發給Handler進行處理

先看看在子執行緒中發訊息,去在主執行緒中更新,我們就在主執行緒中列印一句話。

第一步:
在MainActivity中有一個屬性uiHandler,如下:

    Handler uiHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if(msg.what == 100){
                Log.d("TAG","我是執行緒1 msg.what=" + msg.what + " msg.obj=" + msg.obj.toString());
            }else if(msg.what == 200){
                Log.d("TAG","我是執行緒2 msg.what=" + msg.what + " msg.obj=" + msg.obj.toString());
            }
        }
    };

建立一個Handler例項,重寫了handleMessage方法。根據message中what的標識來區別不同執行緒發來的資料並列印

第二步:
在按鈕的點選事件中開2個執行緒,分別在每個執行緒中使用 uiHandler獲取訊息,併發送訊息。如下


        findViewById(R.id.tv_hello).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //執行緒1
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //1 獲取訊息
                        Message message = uiHandler.obtainMessage();
                        message.what = 100;
                        message.obj = "hello,world";
                        
                        //2 分發訊息
                        uiHandler.sendMessage(message);
                    }
                }).start();

                //執行緒2
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //1 獲取訊息
                        Message message = uiHandler.obtainMessage();
                        message.what = 200;
                        message.obj = "hello,android";
                        
                        //2 分發訊息
                        uiHandler.sendMessage(message);
                    }
                }).start();
            }
        });

使用很簡單,兩步就完成了從子執行緒把資料傳送到主執行緒並在主執行緒中處理
我們來先分析Handler的原始碼

Handler 的原始碼分析

Handler的建構函式

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

呼叫了第兩個引數的建構函式,如下

 public Handler(Callback callback, boolean async) {
        //FIND_POTENTIAL_LEAKS 為 false, 不走這塊
        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 " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

主要是下面幾句:
mLooper = Looper.myLooper(); 呼叫Looper的靜態方法獲取一個Looper
如果 mLooper == null ,就會丟擲異常
Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()";
說明我們的執行緒中如果沒有一個looper的話,直接 new Handler() 是會丟擲這個異常的。必須首先呼叫 Looper.prepare(),這個等下講Looper的原始碼時就會清楚了。

接下來,把 mLooper中的 mQueue賦值給Handler中的 mQueue,callback是傳出來的值,為null
這樣我們的Handler裡面就儲存了一個Looper變數,一個MessageQueue訊息佇列.

接下來就是 Message message = uiHandler.obtainMessage();

obtainMessage()的原始碼如下:

  public final Message obtainMessage()
    {
        //注意傳的是一個 this, 其實就是 Handler本身
        return Message.obtain(this);
    }

又呼叫了Message.obtain(this);方法,原始碼如下:

public static Message obtain(Handler h) {
        //1 呼叫obtain()獲取一個Message例項m
        Message m = obtain();
        
        //2 關鍵的這句,把 h 賦值給了訊息的 target,這個target肯定也是Handler了
        m.target = h;

        //3 返回 m
        return m;
    }

這樣,獲取的訊息裡面就儲存了 Handler 的例項。
我們隨便看一下 obtain() 方法是如何獲取訊息的。如下

  public static Message obtain() {
        //sPoolSync同步物件用的
        synchronized (sPoolSync) {
            //sPool是Message型別,靜態變數
            if (sPool != null) {
                //就是個單鏈表,把表頭返回,sPool再指向下一個
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        
        //如果sPool為空,則直接 new 一個
        return new Message();
    }

obtain()獲取訊息就是個享元設計模式,享元設計模式用大白話說就是:
池中有,就從池中返回一個,如果沒有,則新建立一個,放入池中,並返回。

使用這種模式可以節省過多的建立物件。複用空閒的物件,節省記憶體。

最後一句傳送訊息uiHandler.sendMessage(message);原始碼如下:

 public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

sendMessageDelayed(msg, 0) 原始碼如下

   public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

又呼叫了sendMessageAtTime() 原始碼如下:

   public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        // Handler中的mQueue,就是前面從Looper.get
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

呼叫enqueueMessage()

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        //注意這句,如果我們傳送的訊息不是 uiHandler.obtainMessage()獲取的,而是直接 new Message()的,這個時候target為null
        //在這裡,又把this 給重新賦值給了target了,保證不管怎麼獲取的Message,裡面的target一定是傳送訊息的Handler例項
        msg.target = this;
        
        // mAsynchronous預設為false,不會走這個
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

最後呼叫queue.enqueueMessage(msg, uptimeMillis)原始碼如下:

    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

enqueue單詞的英文意思就是 排隊,入隊的意思。所以enqueueMessage()就是把訊息進入插入單鏈表中,上面的原始碼可以看出,主要是按照時間的順序把msg插入到由單鏈表中的第一個位置中,接下來我們就需要從訊息佇列中取出msg並分了處理了。這時候就呼叫Looper.loop()方法了。

Looper.loop()的原始碼我簡化了一下,把主要的流程留下,方法如下:

  public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;


        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
        
            msg.target.dispatchMessage(msg);
           

            msg.recycleUnchecked();
        }
    }

可以看到,loop()方法就是在無限迴圈中不停的從queue中拿出下一個訊息
然後呼叫 msg.target.dispatchMessage(msg) , 上文我們分析過,Message的target儲存的就是傳送的Handler例項,這我們的這個demo中,就是uiHandler物件。

說白了就是不停的從訊息佇列中拿出一個訊息,然後發分給Handler的dispatchMessage()方法處理。

Handler的dispatchMessage()方法原始碼如下:

   public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

可以看到,一個訊息分發給dispatchMessage()之後
1 首先看看訊息的callback是否為null,如果不為null,就交給訊息的handleCallback()方法處理,如果為null

2 再看看Handler自己的mCallback是否為null,如果不為null,就交給mCallback.handleMessage(msg)進行處理,並且如果返回true,訊息就不往下分發了,如果返回false

3 就交給Handler的handleMessage()方法進行處理。

有三層攔截,注意,有好多外掛化在攔截替換activity的時候,就是通過反射,把自己例項的Handler例項賦值通過hook賦值給了ActivityThread相關的變數中,並且mCallback不為空,返回了false,這樣不影響系統正常的流程,也能達到攔截的目的。說多了。

前面分析了handler處理訊息的機制,也提到了Looper類的作用,下面我們看看Looper的原始碼分析

Looper原始碼分析

我們知道,APP程序的也就是我們應用的入口是ActivityThread.main()函式。
對這塊不熟悉的需要自己私下補課了。

ActivityThread.main()的原始碼同樣經過簡化,如下:

檔案位於 /frameworks/base/core/java/android/app/ActivityThread.java

public static void main(String[] args) {
    //1 建立一個looper
    Looper.prepareMainLooper();
        
    //2 建立一個ActivityThread例項並呼叫attach()方法
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

    //3 訊息迴圈
    Looper.loop();
}

可以看到,主執行緒中第一句就是建立一個looper,並呼叫了Looper.loop()進行訊息迴圈,因為執行緒只有有了一個looper,才能訊息迴圈,才能不停的從訊息佇列中取出訊息,分發訊息,並處理訊息。沒有訊息的時候就阻塞在那,等待訊息的到來並處理,這樣的我們的app就是通過這種訊息驅動的方式執行起來了。

我們來看下 Looper.prepareMainLooper() 的原始碼,如下

    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

第一句,呼叫了prepare(false)
原始碼如下:

    private static void prepare(boolean quitAllowed) {
        //1 檢視當前執行緒中是否有looper存在,有就拋個異常
        //這表明,一個執行緒只能有一個looper存在 
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        
        //2 建立一個Looper並存放在sThreadLocal中
        sThreadLocal.set(new Looper(quitAllowed));
    }

sThreadLocal的定義如下
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

是一個靜態的變數。整個APP程序中只有一個sThreadLocal,sThreadLocal是執行緒獨有的,每個執行緒都呼叫sThreadLocal儲存,關於sThreadLocal的原理,其實就是類似HashMap(當然和HashMap是有區別的),也是key,value儲存資料,只不過key就是sThreadLocal本身 ,但是對映的陣列卻是每個執行緒中獨有的,這樣就保證了sThreadLocal儲存的資料每個執行緒獨有一份,關於ThreadLocal的原始碼分析,後面幾章會講。

既然Looper和執行緒有關,那麼我們來看下Looper類的定義,原始碼如下:

/**
  * Class used to run a message loop for a thread.  Threads by default do
  * not have a message loop associated with them; to create one, call
  * {@link #prepare} in the thread that is to run the loop, and then
  * {@link #loop} to have it process messages until the loop is stopped.
  *
  * <p>Most interaction with a message loop is through the
  * {@link Handler} class.
  *
  * <p>This is a typical example of the implementation of a Looper thread,
  * using the separation of {@link #prepare} and {@link #loop} to create an
  * initial Handler to communicate with the Looper.
  *
  * <pre>
  *  class LooperThread extends Thread {
  *      public Handler mHandler;
  *
  *      public void run() {
  *          Looper.prepare();
  *
  *          mHandler = new Handler() {
  *              public void handleMessage(Message msg) {
  *                  // process incoming messages here
  *              }
  *          };
  *
  *          Looper.loop();
  *      }
  *  }</pre>
  */
public final class Looper {
    .......
}

我們看上面的註釋

  * <pre>
  *  class LooperThread extends Thread {
  *      public Handler mHandler;
  *
  *      public void run() {
  *          Looper.prepare();
  *
  *          mHandler = new Handler() {
  *              public void handleMessage(Message msg) {
  *                  // process incoming messages here
  *              }
  *          };
  *
  *          Looper.loop();
  *      }
  *  }</pre>

這就是經典的Looper的用法 ,可以在一個執行緒中開始處呼叫 Looper.prepare();
然後在最後呼叫 Looper.loop();進行訊息迴圈,可以把其它執行緒中的Handler實傳進來,這樣,一個Looper執行緒就有了,可以很方便的切換執行緒了。
下章節我們來自己設計一個Looper執行緒,做一些後臺任務。

Handler的訊息機制原始碼就分析到這了