1. 程式人生 > >Android Handler消息機制源碼解析

Android Handler消息機制源碼解析

靜態變量 loop final leg indicate called 好記性不如爛筆頭 nal efault

好記性不如爛筆頭,今天來分析一下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的消息機制源碼就分析到這了

Android Handler消息機制源碼解析