Handler源碼學習筆記

Handler是我們在Android開發過程中經常會使用到或者與之打交道的一個類,比如我們會在子線程發起網絡請求,然後到主線程進行UI的刷新。雖然現在有很多能夠自由進行線程切換的相關庫,比如RxJava,但瞭解Handler的原理還是很有必要的,也能夠幫助我們更好的去使用相關庫,其中的設計思想也特別重要。
1、Handler是如何創建的?
What?這也太簡單了吧,看我們平常經常寫的一段代碼

	private Handler handler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
        }
    };

這不是就創建了一個Handler麼?但是,我們所寫的這段代碼究竟幹了什麼事呢?我們看Handler的構造函數

	//當我們new Handler()的時候,調用的是這個無參的構造方法,其最終會調用下面一個構造方法
	public Handler() {
        this(null, false);
    }
    public Handler(@Nullable 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對象
        mLooper = Looper.myLooper();
        if (mLooper == null) {//注意這個異常,點1
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

以上代碼就創建了一個Handler,其中需要注意的是點1,獲取一個Looper對象,並且如果mLooper == null的時候會拋出異常。這就尷尬了啊,我們好像沒有對Looper實例化啊,那這個Looper是在什麼時候創建的呢?其實我們如果在主線程創建Handler的時候,其實得到的是主線程的Looper。如果我們將創建Handler的時候,如果我們在子線程創建Handler,如果沒有調用Looper.prepare(),那麼就會拋出上面點1的那個異常。所以如果我們是在子線程創建Handler需要這樣創建。

//準備Looper
Looper.prepare();
//創建Handler
Handler handler = new Handler(){
   @Override
   public void handleMessage(@NonNull Message msg) {
       super.handleMessage(msg);
   }
};
//開啓消息循環
Looper.loop();

通過以上我們就創建出了我們的Handler。那麼問題點又來了,我們在主線程創建Handler的時候好像並沒有調用Looper.prepare();Looper.loop();這2個方法啊,爲什麼沒拋異常呢?其實答案就是並不是沒有調用,而是不需要我們手動去調用,系統已經自動幫我們調用了這2個方法。在ActivityThread這個類的main方法,也是整個應用程序的主入口處。我們其實都知道java代碼的運行是需要main函數作爲主入口的,而我們自己寫的時候也沒有寫這個方法,而能運行起來的關鍵也是系統幫我們做了這件事。代碼如下:

public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

       	............//此部分忽略很多代碼

        Looper.prepareMainLooper();

        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
        // It will be in the format "seq=114"
        long startSeq = 0;
        if (args != null) {
            for (int i = args.length - 1; i >= 0; --i) {
                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                }
            }
        }
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        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);
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

好了,我們的Handler已經被創建了,那麼消息是怎麼放入,又是怎麼取出的呢?接下來我們先來看消息的放入。
一、消息如何放入
我們平常使用Handler發消息有2個系列,sendMessage和post系列,其不同點在於send是直接發送一個Message,回調在我們 new Handler重寫handleMessage的地方。而post系列需要傳入一個Runnable,會回調到此Runnable裏進行消息處理。sendMeage有很多不同參數的形態,我們以sendMessageDelayed作爲例子看下代碼。

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

爲什麼要看這個方法呢?其實看源碼我們發現就算我們調用sendMessage其實它也是調用sendMessageDelayed這個方法。這段代碼唯一需要注意的是SystemClock.uptimeMillis(),這個方法得到的是開機到現在所經歷的時間毫秒數,然後加上我們所希望延遲的毫秒數,不就實現了一個延時執行的效果麼。然後最終又調用了sendMessageAtTime這個方法,我們看下這個方法又做了什麼。

	public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
		//mQueue是我們創建Handler的時候得到的
        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);
    }
    //上面的方法調用了此方法 傳入queue message time
    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    //這方法是MessageQueue裏的哈
    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標記爲可用狀態
            msg.markInUse();
            //希望在何時執行
            msg.when = when;
            //mMessages可用理解爲上一次入隊的Message
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
            	//p == null MessageQueue的Message爲空 說明存儲Message的鏈表是空的,我們的Message應該放在第一個
            	//when == 0 希望立即執行 需要放在第一個
            	//when < p.when 希望執行的時間比mMessage的時間小,也是要放在第一個
                // New head, wake up the event queue if blocked.
                //將當前Message的next指向mMessage
                msg.next = p;
                //更新mMessage
                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();
                //if裏條件不滿足的時候,說明消息需要做插隊處理
                Message prev;
                for (;;) {
                	//先將mMessage指向prev 注意此時的mMessage就是上次入隊的Message並且一定不爲空
                    prev = p;
                    //將p指向p的下一個Message
                    p = p.next;
                    //p==null 說明後面沒有Message了,可用插入此位置
                    //when < p.when 說明希望執行的時間比p的時間小,就應該插入此位置
                    //當滿足以上2個條件的任意一個,說明已經找到了要插入的位置,跳出循環
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                //將msg的next執向p
                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;
    }

當執行完MessageQueue.enqueueMessage(Message msg, long when)之後,就完成了我們Message的入隊操作。裏面的核心步驟也做了相應的註釋,有的步驟理解起來有點傲,需要耐心的去慢慢推導。
二、消息如何取出
消息的取出其實就是Looper.loop()開啓之後,然後進入一個死循環,去嘗試取出我們的Message。我們Looper.loop()的代碼。

	public static void loop() {
		//得到Looper自己
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //得到queue
        final MessageQueue queue = me.mQueue;
        .................//此部分代碼省略
        for (;;) {
        	//去queue裏面獲取Message  具體看後面的代碼next()方法
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            ............//此部分代碼省略
            try {
            	//dispatchMessage 調用的是Handler的方法,在下面的代碼講解
                msg.target.dispatchMessage(msg);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } catch (Exception exception) {
                if (observer != null) {
                    observer.dispatchingThrewException(token, msg, exception);
                }
                throw exception;
            } finally {
                ThreadLocalWorkSource.restore(origWorkSource);
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
           	..............//此部分代碼省略
            msg.recycleUnchecked();
        }
    }
    //MessageQueue裏面的方法
    Message next() {
        .........//此部分代碼省略
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
			//這是本地方法,主要是做休眠喚醒,節約資源
            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;
                //msg不爲空且msg.target爲空 這個target其實就是Handler  說明msg不可用,要重新去拿
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    //開啓循環去拿消息,直到拿到滿足我們條件的消息
                    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();
                        //最終會將我們的msg返回回去
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }
			...............//此部分代碼省略
            // Reset the idle handler count to 0 so we do not run them again.
            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.
            nextPollTimeoutMillis = 0;
        }
    }
    //msg.target.dispatchMessage(msg); 調用的就是此方法
    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {//msg.callback其實是通過post系列進來的  callback其實是一個Runnable
            handleCallback(msg);
        } else {//這部分是send系列的回調處理
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //是不是特熟悉?不就是new Handler我們會重新的接收消息的方法麼
            handleMessage(msg);
        }
    }

以上貼出的爲關鍵部分的代碼,通過以上的流程就可以成功的取到消息了。
以上我們明確了Handler的創建,消息如何入隊、出隊。那麼Handler、Message、MessageQueue、Looper在其中他們各自扮演了什麼角色?還有我們常說的Handler導致內存泄漏又是怎麼一回事呢?我們先看下這個幾個類各自的持有對象情況。
Handler:
Looper mLooper;//Looper對象
MessageQueue mQueue;//MessageQueue對象
Callback mCallback;//回調 在new Handler中可以傳入
Message:
Handler target;//持有的Handler
Runnable callback;//post系列傳進來的Runnable
Message next;//當前Message的下一個Message
MessageQueue:
Message mMessages;//上一次入隊的Message
//MessageQueue最主要負責了入隊、出隊的方法,以及一些Native方法
Looper:
Looper sMainLooper;//主線程Looper
MessageQueue mQueue;//MessageQueue對象
Thread mThread;//當前的線程對象
ThreadLocal sThreadLocal;//可以理解爲Map存儲,線程數據隔離

Handler通過send或者post系列,發送Message,MessageQueue負責Message的入隊、出隊操作,Looper負責震整個消息調度,最核心的爲loop()方法,不斷的從裏面取出消息,發送到負責處理消息的Handler。

關於會引起內存泄漏,我們看下對象的引用線:Looper->MessageQueue->Message->Handler(message.target)->Activity 如果Handler非靜態類,那麼就是這樣一條引用線,也就是說如果不需要的Message不移除,那麼所持有的Handler就不會移除,而Handler又持有了Activity的引用,直到消息被出隊。所以我們需要在Activity的onDestory中調用handler.removeCallbacksAndMessages(null);將Message移除。

關於Message的創建,new Message()和Message.obtain() ,前一種是直接申請一個Message內存空間,而後一種會在廢棄Message池中複用Message對象,而好處就是可以內存複用,減少gc次數。這種內存複用池的方法在很多框架設計中都會用到,屬於內存優化的範疇。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章