Android線程間消息機制-Handler源碼分析(FrameWork)

本文基於android 7.1.1的源代碼,來對Handler的消息機制進行分析java

1、概述

Android線程間的消息機制最經常使用的就是用Handler來實現,而Handler的實現還須要藉助Looper、MessageQueue、Message這三個類,下圖爲這4個類之間的關係android

1.1 UML類圖關係


消息的機制主要包含
Handler :主要功能是向消息池發送消息( sendMessage)和處理相應的消息( handleMessage);
Message :主要功能是做爲消息的載體,包括軟件消息和硬件消息;
MessageQueue :主要功能是負責將消息儲存到消息池裏( enqueueMessage)和從消息池取出消息( next);
Looper :主要功能是不斷的循環執行( loop),按分發機制將消息分發給目標處理者;

在正文開始前,再來普及一些概念
Process : 進程(Process)是計算機中的程序關於某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位,是操做系統結構的基礎。進程之間內存是不一樣享的。
Thread :線程,有時被稱爲輕量級進程(Lightweight Process,LWP),是程序執行流的最小單元。同一進程下,線程共享內存。app

1.2 Demo

public class MainActivity extends AppCompatActivity {

    private static Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            //自定義處理
            Log.i("TAG","收到的消息:"+msg.what);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new Thread(new Runnable() {
            @Override
            public void run() {
                Message message = Message.obtain();
                message.what = 1;
                mHandler.sendMessage(message);
            }
        }).start();
    }
}複製代碼

上面這段代碼應該是最簡單的Handler使用例子了。這段代碼裏面只涉及到了Handler以及Message,並無看到Looper以及MessageQueue。我再給段僞代碼讓你們好理解。less

1.3.1 僞代碼

public class ActivityThread extends Thread{
    private Handler mHandler;

    @Override
    public void run() {

        Looper.prepare(); //【見2.1】

        ActivityThread thread = new ActivityThrad();

        //創建Binder通道(建立新線程)
        thread.attach(false);

        mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                //TODO 自定義處理消息【見3.2】
            }
        };

        Looper.loop(); 【見2.2】
    }
}複製代碼

上面的代碼大體上就至關於咱們demo給出的例子的Looper缺失部分。通常若是咱們在Activity建立前就會把Looper實例好,因此,咱們平常使用Handler時,都沒有發覺到Looper的存在。MessageQueue這類咱們放在後面再來跟你們講。咱們先來看下這個段僞代碼作了哪些操做。異步

2、Looper

2.1 prepare()

/** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */
    public static void prepare() {
        prepare(true);
    }

    //quitAllowed 爲true的話,在looper運行退出,爲false的話,則是Looper不運行退出
    private static void prepare(boolean quitAllowed) {
        //每一個線程只容許執行一次該方法,第二次執行時線程的TLS已有數據,會拋出異常。
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //給線程的TLS賦值
        sThreadLocal.set(new Looper(quitAllowed));
    }複製代碼

這裏的sThreadLocal實際上是ThreadLocal類型async

ThreadLocal 線程本地存儲區(Thread Local Storage ,簡稱TLS),每一個線程都有本身的私有的本地存儲區域,不一樣線程之間彼此不能訪問對方的TLS區域。 下面來看下ThreadLocal的set方法和get方法。ide

public void set(T value) {
        //獲取當前線程
        Thread t = Thread.currentThread();
        //獲取當前線程的TLS
        ThreadLocalMap map = getMap(t);
        if (map != null)
            //從新賦值
            map.set(this, value);
        else
            //保存value到當前線程的this
            createMap(t, value);
    }複製代碼
public T get() {
        //獲取當前線程
        Thread t = Thread.currentThread();
        //獲取當前線程的TLS
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                //將存儲的值返回
                return (T)e.value;
        }
        //不存在這個值,則從新實例ThreadLocalMap,並將this做爲key,value爲null
        return setInitialValue();
    }複製代碼

這兩段代碼都涉及ThreadLocalMap這個類,其實這個類是ThreadLocal的內部類。而這個類裏面又有內部類Entry,Entry就是做爲存儲的類型。
ThreadLocal的get()和set()方法操做的類型都是泛型,接着回到前面提到的sThreadLocal變量,其定義以下:函數

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>()oop

可見sThreadLocal的get()和set()操做的類型都是Looper類型。post

咱們如今再回到上面的prepare的方法,在調用set方法傳入的是Looper的構造方法。

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);  //建立MessageQueue對象. 【見4.1】
    mThread = Thread.currentThread();  //記錄當前線程.
}複製代碼

Looper.prepare()方法到此結束,到這裏咱們能夠了解到,Looper到這裏就跟MessageQueue以及Thread創建關係。

2.2 loop

/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */
    public static void loop() {
        //獲取當前線程的looper實例
        final Looper me = myLooper();
        //調用loop前必定要先調用prepare()方法,不然會報錯
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //將當前線程的Looper中的消息隊列MessageQueue
        final 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();

        for (;;) {//進入loope的主循環
            //從消息隊列取消息
            Message msg = queue.next(); // 可能會阻塞 【見4.1】
            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 traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
                //調用Message自帶的Handler的dispatchMessage()進行分發消息
                msg.target.dispatchMessage(msg);//【見3.1】
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            // 確保分發過程當中identity不會損壞
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "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);
            }
            //將Message放入消息池
            msg.recycleUnchecked();
        }
    }複製代碼

到這裏主線程就進行死循環,這個循環就是不停的取消息。也就是說當一個Activity實例好,Looper這塊就已是處於取消息階段,只要有消息進來,立馬觸發next取出消息。並且這裏的消息不僅僅侷限於咱們本身建立的Handler發送過來的消息。其實整個Activity的生命週期也是經過Handler的消息機制維護的,具體的能夠去學下Activity的啓動源碼。這裏有個問題,就是這個死循環運行在UI線程卻不會致使ANR的發生。是由於 Android應用程序的主線程在進入消息循環過程前,會在內部建立一個Linux管道,這個管道的做用就是使得主線程在消息隊列爲空時能夠進入空閒狀態,當有消息來時,再喚醒主線程進行工做。 因此,也能夠這麼說Handler的底層機制實際上是 Linux的管道技術 ,正是因爲這個技術,讓發送消息以及處理消息可以運行於在不一樣的線程上。

當取到消息時,就會調用Message的Handler成員的dispatchMessage方法進行分發消息。

3、Handler

咱們先從Handler的構造方法進行分析

// 通常狀況咱們都是調用無參的構造方法
  public Handler(Callback callback, boolean async) {
        //匿名類、內部類或本地類都必須申明爲static,不然會警告可能出現內存泄露
        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對象
        //必須先執行Looper.prerare(),對能獲取Looper對象,不然會報錯
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        //獲取Looper的消息隊列
        mQueue = mLooper.mQueue;
        //回調方法
        mCallback = callback;
        //設置消息是否爲異步方式
        mAsynchronous = async;
    }複製代碼

這裏能夠看出,Handler的MessageQueue由Looper決定,而且Handler所在的線程也是由Looper決定的。

3.1 dispatchMessage

/** * Handle system messages here. */
    public void dispatchMessage(Message msg) {
        //msg的callback不爲空,就回調msg的callback方法
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            //handler自己的mCallback不爲空,也便是有參構造函數裏面傳入
            //回調mCallbackd的handleMessage方法
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //前二者不知足狀況下,調用handleMessage方法
            handleMessage(msg);
        }
    }複製代碼

從上面分發機制能夠看出,Message自己的回調優先級是最高的,而Handler自己的方法級別是最低的。因此按照優先級從高到低的排序:
一、Message的回調方法:msg.callback.run()
二、Handler的回調方法:Handler.mCallback.handleMessage(msg)
三、Handler的默認方法:Handler.handleMessage(msg)

咱們日常使用最多的方法實際上是Handler自身的HandleMessage方法,到這裏咱們已經大體瞭解到從消息隊列裏面取到消息,而且將消息分發到相應的Handler進行處理。這裏已經講完消息機制的後半段,接着來分析消息機制的前半段-發送消息到消息隊列。

public final boolean sendMessage(Message msg) {
        //延時爲0
        return sendMessageDelayed(msg, 0);
    }複製代碼
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }複製代碼
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        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);
    }複製代碼
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        //將當前的Handler賦值給Message
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);【見4.2】
    }複製代碼

這邊特地列出這麼多個方法,就是爲了說明消息的添加是MessageQueue的enqueueMessage執行的。咱們再來看看Handler其餘一些經常使用的方法,是如何將消息傳遞過去的。

public final boolean post(Runnable r) {
   return  sendMessageDelayed(getPostMessage(r), 0);
}複製代碼
public final boolean postDelayed(Runnable r, long delayMillis) {
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}複製代碼
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();【見5.1】
    m.callback = r;
    return m;
}複製代碼

從最後一個方法裏面能夠看出,利用Handler的post、postDelayed方法,最終消費的是Message的回調方法callback,這個按照3.1的分發機制能夠得出。

咱們再來看下咱們的demo給出的方法又是怎麼實現的

public final boolean sendMessage(Message msg) {
        return sendMessageDelayed(msg, 0);
    }複製代碼

從這裏能夠看出,post跟send方法的區別在於post方法須要傳入Runnable,而send的不須要,最終致使消息的消費位置不同。

4、MessageQueue

4.1 next

Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) { //當消息循環已經退出,則直接返回
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //阻塞操做,當等待nextPollTimeoutMillis時長,或者消息隊列被喚醒,都會返回
            //native方法
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message. Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier. Find the next asynchronous message in the queue.
                    //當消息Handler爲空時,查詢MessageQueue中的下一條異步消息msg,則退出循環。
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready. Set a timeout to wake up when it is ready.
                        //當異步消息觸發時間大於當前時間,則設置下一次輪詢的超時時長
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                //當消息隊列爲空,或者是消息隊列的第一個消息時
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run. Loop and wait some more.
                    //沒有idle handlers 須要運行,則循環並等待。
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            //只有第一次循環時,會運行idle handlers,執行完成後,重置pendingIdleHandlerCount爲0.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                //去掉handler的引用
                mPendingIdleHandlers[i] = null; // release the reference to the handler


                boolean keep = false;
                try {
                    keep = idler.queueIdle(); //idle時執行的方法
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            //重置idle handler個數爲0,以保證不會再次重複運行
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            //當調用一個空閒handler時,一個新message可以被分發,所以無需等待能夠直接查詢pending message.
            nextPollTimeoutMillis = 0;
        }
    }複製代碼

上面的代碼主要的工做就是取出消息,可是真正的工做是在Native層實現的,調用nativePollOnce方法,實現與Native層的通訊。這裏咱們不對Native層的實現進行深究,有興趣的可自行查閱資料瞭解。後期我也會寫關於Native層的實現機制。

4.2 enqueueMessage

boolean enqueueMessage(Message msg, long when) {
        //Message的target必須不能爲空
        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) {//正在退出時,回收msg,加入消息池
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle(); 【見5.2return 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.
                // p爲null(表明MessageQueue沒有消息)或者msg的觸發時間是隊列中最先的
                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.
                //將消息按時間順序插入到MessageQueue。正常狀況不須要喚醒,除非消息隊列存在barrier,而且同時Message是隊列中最先的異步消息
                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.
            //消息沒有退出,咱們認爲此時mPtr != 0
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }複製代碼

看到這裏,咱們能夠了解到消息的產生以及添加到隊列中去。回到上面寫的Demo,handler.sendMessage到這裏就結束了,也就是說這個新建的線程到此就沒了。接下來就要喚醒主線程了,這個喚醒主要是喚醒【4.1】說起到的nativePollOnce方法;當有消息入隊以後,nativePollOnce方法就會被調用,而後就開始分發消息了。那麼整個Handler的消息機制就走完了。

5、Message

5.1 obtain

public static Message obtain() {
        synchronized (sPoolSync) {
            //消息池不爲空
            if (sPool != null) {
                取出隊列頭
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                //消息池數量減1
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }複製代碼

這個方法是不少人都推薦調用的,而不是調試Message的構造方法。由於這個是重複利用消息池的空閒消息,提升性能。

5.2 recycle

public void recycle() {
        if (isInUse()) {
            if (gCheckRecycle) {
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        recycleUnchecked();
    }複製代碼

5.3 recycleUnchecked

void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                //將沒用的Message回收到消息池裏
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }複製代碼

總結

本文從一開始構建循環體Looper、從MessageQueue取出消息到Handler消費消費的過程,而後再對Handler發送消息,緊接着消息入隊列這一過程的分析。
Looper在初始化的時候會跟當前所在的線程綁定,而且實例化消息隊列MessageQueue。而Handler在實例化的時候,若是沒有指定looper的話,默認調用會從當前線程的TLS中取出Looper。因此實例Handler以前,一定是調用了Looper.prepare的。而且結合管道技術,實現一個線程負責取消息,另一個線程負責添加消息。實現了線程間的通訊。

仍是用一張圖來總結整個消息機制吧。

一、Handler的sendMessage將Message送到MessageQueue中,此時觸發管道(Pipe)喚醒另一端開始讀數據
二、MessageQueue的next返回Message,並由Message的Handler成員調用dispatchMessage將Message進行處理(消費)
三、Looper在調用loop後就一直處於等待狀態,一直到有Message到來,並將其喚醒,而後再將Message發出去

相關文章
相關標籤/搜索