從源碼角度淺析Android中的Handler

Handler是Android消息通訊當(dāng)中最常用的方式之一。
本篇小文將會(huì)從Handler的源碼角度去淺析Handler。

總結(jié)

因?yàn)镠andler這個(gè)東西其實(shí)大家都會(huì)用,源碼也多多少少地了解過,所以直接將最關(guān)鍵的話,寫到前面,對(duì)源碼感興趣的看官可以在看完總結(jié)后再往下瀏覽源碼:

  1. 創(chuàng)建Handler
    Handler handler = new Handler();

在主線程當(dāng)中,可以直接進(jìn)行Handler的創(chuàng)建。如果是在子線程當(dāng)中,在創(chuàng)建之前必須先初始化Looper,否則會(huì)RuntimeException:

    Looper.prepare();
    Handler handler = new Handler();
    handler.sendEmptyMessage(8);
    Looper.loop();

在初始化Looper的同時(shí),一定要調(diào)用Looper.loop()來啟動(dòng)循環(huán),否則Handler仍然無法正常接收。
并且因?yàn)?code>Looper.loop()有死循環(huán)的存在,Looper.loop()之后的代碼將無法執(zhí)行,所以需要將Looper.loop()放在代碼的最后,這點(diǎn)在下面源碼解析會(huì)解釋。
并且Looper在每個(gè)線程只能存在一個(gè),如果再去手動(dòng)創(chuàng)建Looper也會(huì)拋出RuntimeException。

  1. 發(fā)送Handler
    Handler的發(fā)送有很多方法,包括發(fā)送延時(shí)Handler、即時(shí)Handler、MessageHandler等等,但是查看這些發(fā)送方法的源碼,就會(huì)發(fā)現(xiàn)這些發(fā)送Message的方法最終都會(huì)調(diào)用MessageQueue.enqueueMessage()方法。
    這個(gè)方法其實(shí)就是將我們發(fā)送的Message入隊(duì)到MessageQueue隊(duì)列中,這樣,我們的消息就已經(jīng)發(fā)送成功,等待執(zhí)行了。
  2. 取出Handler
    Looper.prepare()的同時(shí),總會(huì)執(zhí)行looper.loop()語句與之對(duì)應(yīng)。
    查看loop()源碼會(huì)發(fā)現(xiàn),這個(gè)方法中有一個(gè)for(;;)的死循環(huán),會(huì)無限執(zhí)行MessageQueue.next(),而MessageQueue就是我們上一步將Meesage入隊(duì)的對(duì)象。
    也就是說在創(chuàng)建Looper時(shí),就會(huì)啟動(dòng)MessageQueue的無限遍歷。如果MessageQueue為空,Looper.loop()就會(huì)進(jìn)入休眠,直到再有Message插入到MessageQueue中。
    如果取到Message則會(huì)調(diào)用message.target.dispatchMessage(),將消息分發(fā)給對(duì)應(yīng)的Handler。
  3. 如何在loop()休眠之后喚醒loop()
    在Meesage入隊(duì)的時(shí)候,也就是執(zhí)行MessageQueue.enqueueMessage()方法時(shí),enqueueMessage()有一個(gè)nativeWeak()的native方法,如果有消息進(jìn)入,并且Looper是休眠狀態(tài),則會(huì)執(zhí)行該方法喚醒Looper:
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
    nativeWake(mPtr);
}
  1. 整體流程
    先調(diào)用Looper.prepare()創(chuàng)建Looper,在創(chuàng)建的同時(shí)會(huì)自動(dòng)調(diào)用Looper.loop()執(zhí)行死循環(huán)loop()。注意Looper.loop()一定放到代碼的最后一行。
    死循環(huán)中會(huì)執(zhí)行MessageQueue.next()方法去取出隊(duì)列中的消息,當(dāng)消息為空時(shí),MessageQueue.next()方法中會(huì)執(zhí)行nativePollOnce()的native方法休眠Looper.loop()死循環(huán)。當(dāng)有新的消息插入到MessageQueue中,也就是調(diào)用MessageQueue.enqueueMessage()方法,這個(gè)方法當(dāng)中會(huì)判斷Looper是否是休眠狀態(tài),如果是休眠狀態(tài)會(huì)執(zhí)行nativeWeak()的native方法來喚醒Looper()。

Handler的使用

  1. 主線程
    主線程當(dāng)中,Handler可以作為一個(gè)成員變量直接進(jìn)行創(chuàng)建:
    //注意,Handler屬于android.os包
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            System.out.println("主線程的Handler");
        }
    };
    

接著我們?cè)囍l(fā)送Handler:

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

        //發(fā)送一個(gè)空Handler,what為888,延遲3秒到達(dá)
        handler.sendEmptyMessageDelayed(888,3000);
    }

發(fā)送Handler有很多方法:發(fā)送空Message、發(fā)送延遲的空Message、發(fā)送Message、發(fā)送延遲的Message等:


接著我們運(yùn)行項(xiàng)目,就會(huì)發(fā)現(xiàn)3s后控制臺(tái)有Log的打印。

  1. 子線程
    子線程中創(chuàng)建Handler的流程,和在主線程基本一致,只是多了一步而已,但這一步非常關(guān)鍵。
    我們先按照主線程的步驟,看看會(huì)有什么問題。
    子線程中創(chuàng)建Handler:
new Thread(){
    @Override
    public void run() {
        super.run();
        Handler handler = new Handler(){
             @Override
            public void handleMessage(Message msg) {
                System.out.println("子線程的Handler");
            }
        };
    }
}.start();

接著我們運(yùn)行項(xiàng)目,會(huì)發(fā)現(xiàn)項(xiàng)目崩潰了:

java.lang.RuntimeException: Can''t create handler inside thread that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:200)
at android.os.Handler.<init>(Handler.java:114)
at com.my.oo.MainActivity$2$1.<init>(MainActivity.java:0)

很明顯,錯(cuò)誤信息說的是在當(dāng)前線程中創(chuàng)建Handler失敗因?yàn)闆]有執(zhí)行Looper.prepare()
那我們按照錯(cuò)誤原因,在創(chuàng)建Handler之前,加上Looper.prepare():

//子線程
new Thread(){
    @Override
    public void run() {
        super.run();
        //添加Looper.prepare();
        Looper.prepare();
        //之后再創(chuàng)建Handler
        Handler handler = new Handler(){
             @Override
            public void handleMessage(Message msg) {
                System.out.println("子線程的Handler");
            }
        };
        //發(fā)送消息
        handler.sendEmptyMessage(111);
    }
}.start();

這次再運(yùn)行項(xiàng)目,我們就會(huì)發(fā)現(xiàn)項(xiàng)目正常運(yùn)行沒有問題。但是發(fā)送Handler仍然無法接收,那是因?yàn)槲覀儧]有啟動(dòng)Looper的遍歷:

//子線程
new Thread(){
    @Override
    public void run() {
        super.run();
        //添加Looper.prepare();
        Looper.prepare();
        //之后再創(chuàng)建Handler
        Handler handler = new Handler(){
             @Override
            public void handleMessage(Message msg) {
                System.out.println("子線程的Handler");
            }
        };
        //發(fā)送消息
        handler.sendEmptyMessage(111);
        //啟動(dòng)Looper的遍歷功能
        Looper.loop();
    }
}.start();

這里一定要注意,Looper.loop()必須放到代碼的最后。因?yàn)?code>Looper.loop()中有死循環(huán),會(huì)導(dǎo)致之后的代碼無法執(zhí)行。這里可以等到查看主線程創(chuàng)建過程的源碼時(shí)證實(shí)。

  1. 使用總結(jié)
    Handler的使用就是這么簡(jiǎn)單,要注意的就是子線程當(dāng)中使用Handler時(shí),一定要先調(diào)用Looper.prepare(),最后調(diào)用Looper.loop(),否則項(xiàng)目會(huì)崩潰或無法接收Handler。至于為什么會(huì)這樣,我們?cè)谠创a里面找原因。

源碼解析

  1. Handler創(chuàng)建
    我們先看Handler的構(gòu)造方法:
public Handler(Callback callback, boolean async) {
    //省略部分代碼
    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;
}

我們可以看到,Handler的構(gòu)造方法當(dāng)中是去獲取了一個(gè)叫做Looper的類對(duì)象,如果該對(duì)象為空,就會(huì)拋出剛才我們上面發(fā)生的異常。所以我們需要在創(chuàng)建Handler之前,一定要先執(zhí)行Looper.prepare()
那么問題來了,為什么主線程就不需要執(zhí)行Looper.prepare()就可以直接創(chuàng)建Handler呢?
我們可以隨意根據(jù)代碼猜測(cè)一下:
這里Handler的構(gòu)造方法的代碼已經(jīng)很明顯了,Looper類是必要的,那么主線程可以成功創(chuàng)建Handler,是不是就代表著主線程的Looper不為空呢?


是不是主線程在初始化的時(shí)候Looper也跟著初始化了呢!?帶著看破一切(瞎猜)的思路,我們來看主線程的初始化源碼。
經(jīng)過了長(zhǎng)時(shí)間的Google,我們知道了主線程類叫做:ActivityThread。
該類當(dāng)中有main方法:

public static void main(String[] args) {
    //....省略部分代碼
    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);
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

我們很自然(驚奇)地發(fā)現(xiàn),在主線程創(chuàng)建的過程中,果然(真的)有與Looper類相關(guān)的內(nèi)容。
這里有重點(diǎn)(敲黑板):之前說的Looper.loop()后的代碼不會(huì)執(zhí)行,這里得到了證實(shí):

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

下面異常的意思是:主線程的Looper意外退出。
也就是當(dāng)Looper.loop()執(zhí)行失敗的意思,但是當(dāng)Looper.loop()執(zhí)行成功時(shí),是不會(huì)執(zhí)行下面的代碼的!因?yàn)?strong>Looper.loop()必須放到方法的最后,否則會(huì)導(dǎo)致后面的代碼無法執(zhí)行。
好的,接著往下看,點(diǎn)進(jìn)prepareMainLooper()會(huì)發(fā)現(xiàn),其實(shí)內(nèi)部就是調(diào)用了prepare()

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

所以到現(xiàn)在,我們解決了我們的第一個(gè)問題:為什么主線程當(dāng)中不需要執(zhí)行Looper.prepare()。
接著,我們?nèi)g覽Looper.prepare()

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //new Looper()并設(shè)置給當(dāng)前線程
    sThreadLocal.set(new Looper(quitAllowed));
}

沒什么東西,前面對(duì)線程當(dāng)中的Looper進(jìn)行了判空,如果不為空則會(huì)拋出RuntimeEception。
這也就是說,每個(gè)線程當(dāng)中只能有一個(gè)Looper,當(dāng)你嘗試去創(chuàng)建第二時(shí),就會(huì)發(fā)生異常,所以Looper.prepare()每個(gè)線程中只能調(diào)用一次。
后面則new了Looper并且設(shè)置給當(dāng)前線程。
new Looper()中初始化了MessageQueue

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

到這里,Handler的創(chuàng)建就完成了!

  1. Handler發(fā)送Meesage
    Handler的發(fā)送方法有很多,包括發(fā)送延時(shí)Handler、及時(shí)Handler、空Hanlder等等。
    查看源碼會(huì)發(fā)現(xiàn),所有發(fā)送方法最后調(diào)用的都是同一個(gè)方法:MessageQueueenqueueMessage()。
    有的看官就會(huì)問:Handler中怎么會(huì)有MeesageQueue
    這個(gè)在上面new Looper()的源碼中已經(jīng)體現(xiàn)了:
    new Handler()new Looper()
    new Looper()new MessageQueue()。
    所以其實(shí)初始化Handler的同時(shí),LooperMeesageQueue都已經(jīng)初始化完成了。
    下面我們來看消息入隊(duì)方法MessageQueue.enqueueMessage()的全部源碼:
boolean enqueueMessage(Message msg, long when) {
        //Meesage是否可用
        //這里的msg.target指的就是發(fā)送該Message的Handler
        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) {
            //判斷是否調(diào)用了quit()方法,即取消信息
            //如果調(diào)用了,則其實(shí)Handler的Looper已經(jīng)銷毀,無法發(fā)送消息
            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;
            }

            //將消息添加到MessageQueue的具體操作
            //每來一個(gè)新的消息,就會(huì)按照延遲時(shí)間的先后重新進(jìn)行排序
            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 {
                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;
            }

            //如果Looper.loop()是休眠狀態(tài)
            //則調(diào)用native方法喚醒loop()
            //---重點(diǎn)---Looper的喚醒
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

將Message入隊(duì)到MeesageQueue的核心代碼,就是這些。
根據(jù)注釋也基本能理解該方法的作用。

  1. 取出Message
    取出Meesage想必大家都知道在哪里取出: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.");
    }
    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();
    //取出Message,死循環(huán)
    for (;;) {
        //取出Meesage的核心代碼
        //在當(dāng)next()返回為空時(shí),next()中會(huì)休眠loop()
        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 traceTag = me.mTraceTag;
        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
        try {
            msg.target.dispatchMessage(msg);
        } 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.
        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);
        }
        msg.recycleUnchecked();
    }
}

Looper.loop()的核心就是取出Message,而取出Message的核心就是MeesageQueue.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();
        }
        //喚醒Looper.loop()的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 
                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 
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.M
                } 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 hand
            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.
                mBlocked = true;
                continue;
            }
            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCo
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }
        // Run the idle handlers.
        // We only ever reach this code block during the first iteration.
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; // release the reference to the handler
            boolean keep = false;
            try {
                keep = idler.queueIdle();
            } 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.
        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;
    }
}

取出Meesage的代碼有些多,大部分都是一些優(yōu)化邏輯:next() 方法還做了其他一些事情,這些其它事情是為了提高系統(tǒng)效果,利用消息隊(duì)列在空閑時(shí)通過 idle handler 做一些事情,比如 gc 等等。

結(jié)語

到這里Handler源碼的淺析就結(jié)束了,總結(jié)在最上方,建議各位看官再去看一下總結(jié)加深印象。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容