Android 源碼分析 - 消息處理機制

??在學習Android過程中,Android的消息機制是必須需要掌握的。樓主也只能算是一個Android的初學者,對Android的消息機制掌握的也不算很好。這里記錄一下我自己對Android消息機制的理解,錯誤之處,希望各位指正。
??本文參考文章:
??1.Android 異步消息處理機制 讓你深入理解 Looper、Handler、Message三者關系
??2.Android應用程序消息處理機制(Looper、Handler)分析

1.概述

??要講消息處理機制,不得不講的就是Handler、Looper、MessageQueue這個重量級的人物。在這里對他們做一個概述,待會會詳細的介紹他們。
??從大體上來說,Handler的主要作用就是發送Message和處理Message;Looper的作用就是不斷從MessageQueue(消息隊列)中取Message,然后交給Handler處理;MessageQueue存放由Handler發送的Message。通常來說,Message里面就是需要主線程執行的操作,比如,我們從網絡獲取的文字內容信息,需要顯示到TextView上面去。

2.Handler

??首先我們從上面走下去,從Handler開始走。
??通常來說,使用Handler來處理異步消息,有基本的步驟。這里做一個簡單的例子,在線程中發送一條String,讓TextView來顯示。

    private Button mButton = null;
    //創建一個Handler的對象,注意我們重寫了handleMessage消息
    //handleMessage方法就是用來處理我們發送的消息
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            mButton.setText(msg.obj.toString());
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mButton = (Button) findViewById(R.id.button);
        mButton.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.button: {
                sendMessage();
                break;
            }
        }
    }

    private void sendMessage() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                //創建一個Message的對象
                Message message = Message.obtain();
                message.obj = "我被點擊了";
                //發送消息
                mHandler.sendMessage(message);
            }
        }).start();
    }

??上面代碼中,我相信不難吧。現在我們需要理解的是Handler是怎么將一個Message從子線程傳遞到主線程中,然后執行的!

(1).sendMessage方法

??我們需要理解其中的緣由,我們來sendMessage方法的代碼。

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

??我們在一步一步的跟蹤下去,最終到了enqueueMessage方法中

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

??其中,這個方法的三個參數,queue表示message需要進入的消息隊列,msg就是我們之前創建的Message對象,uptimeMillis表示延遲時間。延遲時間這里不重要,我們先不看。
??我們從源代碼中可以看出,首先msg.target = this。這句話將一個handler賦值到message的target屬性,這一步非常重要,后面需要用的。這里我們先記住,這里的意思就是,表示當前的這個message屬于哪個handler,因為有可能有很多的handler都在發送消息!
?? mAsynchronous我們不看,這里不重要。最后是調用了queue.enqueueMessage(msg, uptimeMillis)方法,從這個方法的字面意思中,我們就可以看出來,這個方法是將一個message添加到queue的消息隊列。
??我們先不看queue里面的代碼。我們先來總結我們得到的信息。

??1.我們在子線程中發送一個Message,最終會進入到一個queue的消息隊列中去。
??2.在Message進入消息隊列之前,使用target字段來標記了當前的Message是哪個Handler發送的。

??到這里,我們好像沒有收獲,不急,我們來看看在構造一個Handler的時候,給我們創建那些東西!

(2).Handler的構造方法

??Handler的構造方法最終調用到了Handler(Callback callback, boolean async)方法里面。

    public Handler(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());
            }
        }

        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的構造方法中,我們發現了幾個小細節,mLooper、mQueue都被賦值了。Looper我們待會講,我們在調用sendMessage方法的時候,發現其中調用到這一步:

    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);
    }

??我們注意到的是,之前的enqueueMessage里面的queue就是mQueue。
??到這里,我們知道了:

??1.Handler在創建的時候,會創建一個MessageQueue和一個Looper,也就是說,一個Handler同時綁定一個MessageQueue和一個Looper。
??2.我們在調用sendMessage等方法來發送消息的時候,消息進入的消息隊列就是發送消息的Handler自己的MessageQueue。

2.Looper

??記得在概述中描述,Handler是發送消息和處理消息,現在我們對發送消息有了一個簡單的認識,但是我們對處理消息沒有介紹。不急,要想知道處理消息,我們必須先知道,消息是怎么被獲取出來的,難道是Handler直接伸手從MessageQueue中去拿嗎?怎么可能這么簡單嗎?接下來,我們介紹一下Looper,又一位重量級的人物

(1).Looper構造方法

??我們先來看看Looper的構造方法

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

??我們在這個構造方法里面發現,Looper創建一個MessageQueue。但是除了這個信息,似乎沒看其他有用的信息了。
??怎么辦?通常來說使用Looper不會直接的創建,從這里我們也看到它的構造方法是private的,而是調用prepare方法。

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

??在prepare方法中,需要注意的是:sThreadLocal變量是一個ThreadLocal變量。這種類型的變量有什么呢?具體的解釋大家可以到網上去找(其實我也不是很懂,哈哈!!!)。這里簡單的解釋一下,ThreadLocal里面封裝了一個變量,用來存儲相應的信息,關鍵是不同的線程從ThreadLocal里面獲取的變量是不同的,每個線程從ThreadLocal獲取的是自己的變量,線程之間的是不會相互影響。
??從這里,我們可以得到是,每個線程都會有一個自己的Looper對象。
??到這里,我們知道:

??1.Looper的對象不能直接new,而是調用靜態方法prepare方法來創建
??2.同一個線程不能調用兩次prepare方法,因為Looper對象保存在一個ThreadLocal對象。
??3.從之前的Handler中,我們可以看到Handler里面持有了一個Looper對象(通過調用方法mLooper = Looper.myLooper())。這個對象就是TreadLocal封裝的對象。從而得出,如果這個Handler在主線程中創建的,那么Looper肯定在屬于主線程。

??從上面的結論中,我們可以得出,如果我們在一個線程中創建一個Handler,必須先創建當前線程的Looper對象。但是我們,你會發現,之前那個例子中,我們直接在一個Activity中創建一個Handler對象,而沒有創建主線程的Looper,這個為什么沒有報錯呢?我們可以假設,我們在創建Handler時,主線程的Looper早已經被prepare了!但是具體在哪里調用的呢?待會我們會展示!

(2).loop方法

??在之前說過,Looper的作用就是不斷從MessageQueue(消息隊列)中取Message,然后交給Handler處理。但是我們到現在還不知道Looper到底是怎么從MessageQueue里面去消息的,這個就得引出我們的loop方法。
??我們先來看看loop方法的源代碼,沒有貼出完整的代碼,而是刪除了一些自己認為不重要的代碼:

    public static void loop() {
         //取得當前線程的Looper--還記得我們之前的說的ThreadLocal變量,這個變量里面保存的就是當前線程的
         //Looper對象
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //獲取當前Looper的消息隊列對象
        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;
            }

            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
                //將當前的Message分發到Message自己的Handler
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            msg.recycleUnchecked();
        }
    }

??我們先來整理一下,這個方法主要描述內容:

??1.首先獲取當前線程的Looper對象和MessageQueue對象
??2.從MessageQueue里面去獲取Message;如果獲取到Message,則分發到Message自己的Handler去處理;反之,沒有獲取到信息,線程會被阻塞在next方法里面。
??3.通過Handler的dispatchMessage方法將獲取的到Message從MessageQueue傳遞到了Handler,讓Handler去處理。

??到這里,我們來整理一下思路。首先,在主線程創建了一個Handler,表示當前的Looper是放在主線程中的,當然MessageQueue也是屬于主線程的。這里需要說明的是,就是一個線程只能有一個Looper,由于MessageQueue在Looper里面,所以MessageQueue也是只有一個,但是一個線程中Handler可以會被多次創建,所以,屬于同一個線程的Handler中持有的是同一個MessageQueue和Looper。
??我們使用在主線程中創建的Handler通過調用sendMessage等方法來發送消息,最終進入的消息隊列是主線程的MessageQueue。我們在Looper的loop方法中獲取Message,然后將Message分發到Message的target,也就是發送Message的Handler,讓它來處理。
??整個消息傳遞的過程就是這樣的,這個就能解釋,為什么我們在子線程中發送一個Message,在主線程的Handler能接收得到,從而進行對消息處理。因為Handler本身就是屬于主線程,包括Handler里面持有的Looper和MessageQueue都是屬于主線程。
??這里有一個疑問,那就是,我們知道我們在主線程中創建Handler時,其實主線程的Looper早就已經創建好了,這一點是怎么看出來得呢?
??例如:

        new Thread(new Runnable() {
            @Override
            public void run() {
                //這里將Looper的prepare方法注釋了
                //表示當前的線程Looper沒有被初始化
//                Looper.prepare();
                Handler mHandler = new Handler(){
                    @Override
                    public void handleMessage(Message msg) {
                        Log.i("pby123", msg.obj.toString() + " " + Thread.currentThread().getName());
                    }
                };
                Message message = Message.obtain();
                message.obj = "pby123";
                mHandler.sendMessage(message);
            }
        }).start();

??然后我們來運行一下程序,會發現一個臭名昭著的異常!



??這個異常我們應該不陌生,因為凡是要使用Looper的地方,都用check一下Looper是否preapre完成。
??上面的樣例,我們是從一個新的線程里面創建Handler,由于是新的線程,就必須將這個線程的Looper和MessageQueue創建好。
??從而得出,我們在主線程中可以直接創建Handler,表示主線程的Looper和MessageQueue在我們創建Handler之前就已經創建完畢了!
??那到底是在哪里創建主線程的Looper。這個得引出ActivityThread類的main方法,我們都知道main方法是Java程序的入口,我們來看看main方法:

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

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");

        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");
    }

??是不是看到main方法就感覺非常的親切,終于可以回到當初學習Java的時候了。我也是,看到main方法之后,感覺之前有好多的問題都解決了!
??在main方法里面,我們看到:Looper.prepareMainLooper();這一句話的意思就是初始化的主線程的Looper。從這里,我們知道了,為什么我們在主線程里面可以創建Handler,而不用去prepare Looper。
??同時,我們還注意的是,Looper.loop()。我們之前已經分析過了,loop方法主要是在MessageQueue里面去取消息來處理。由于這里是主線程的Looper,所以這里的loop方法是從主線程的MessageQueue里面取Message。
??之前,我們使用一個在主線程創建的Handler在一個子線程發送Message,最后發送到了主線程的MessageQueue。但是什么時候調用的loop呢?其實就是在main方法里面調用了的。
??同樣的道理,如果我們在一個新的線程里面分別創建Handler、Looper、MessageQueue,當調用Handler的sendMessage方法來發送一條Message,在Handler的handleMessage方法是不會接收到這個Message對象,這個是由于Looper的loop方法啟動,當前線程
??我們知道,loop方法是一個死循環,但是為什么不會導致應用ANR呢?(Application Not Response)。其實網絡上有很多的解釋,可能是自己太笨了,理解不了那些思路。下面將來結合Looper的loop方法和MessageQueue的部分方法來解釋一下原因。

4.MessageQueue

??我們知道MessageQueue相當于是一個存放Message的容器,Handler往這個容器里面放入Message,Looper從這個容器取數據。我們先來看看存放數據的部分
??在Looper的loop方法里面有這么一句:

Message msg = queue.next(); // might block

??上面這一句,就是Looper從MessageQueue里面取Message的操作,我們注意一下官方的注釋,這一句可能會導致阻塞!
??那么什么情況下會被阻塞呢?是不是當前MessageQueue為空的時候,會被阻塞呢?如果MessageQueue為空時,會被阻塞,這一句又是什么作用呢?

            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

(1).next方法

??我們先來看看next方法,這里我先將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();
            }

            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.
                    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.
                    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.
            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;
        }
    }

??首先,我們來看看這一句話

            nativePollOnce(ptr, nextPollTimeoutMillis);

??根據Android應用程序消息處理機制(Looper、Handler)分析中介紹,nativePollOnce方法是用來表示當前線程會被阻塞,nextPollTimeoutMillis表達的意思就是當前的線程需需要阻塞多久。其中如果nextPollTimeoutMillis為0,表示不阻塞;-1表示無限期的阻塞,除非有線程喚醒它;其他的正數值表示阻塞多久。
??然后我們來看看下面的代碼

                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;
                }

??其中,msg表示當前的消息隊列中第一個可以用的Message。其中先判斷了當前的時間與消息執行的時間的大小,如果表示當前的時間小于Message執行的時間,也就是說,還沒有到達該Message執行的時間,因此給nextPollTimeoutMillis賦值,表示線程要阻塞多久。
??但是,如果當前的時間符合Message的執行時間了,就得馬上返回這個Message,去執行里面的操作。
??如果當前取得的第一個Message是空的,表示當前的MessageQueue里面沒有了Message了,所以將nextPollTimeoutMills重置為-1,表示當前將無限期的阻塞下去,除非其他的線程將喚醒!
??其中我們還需要注意的是:

                if (mQuitting) {
                    dispose();
                    return null;
                }

??這個return是整個next方法里面第二個return null,還記得我們在loop方法里面的一段代碼嗎?就是如果從next方法里面獲取的方法是null的話,那么直接退出了loop循環。這個return null的返回條件就是就能解釋我們之前的那個疑問--如果MessageQueue為空時,會被阻塞,這一句又是什么作用呢?
??第一個return null的條件是mPtr等于0時,關于mPtr等于0是表示什么情況,我也不是清楚,大概應該說,當前的MessageQueue對頭的地址吧,這個只是我的猜測不一定正確!
??還是來看看第二個return null的條件吧,從中可以知道只有當mQuiting為true,才能返回 null。從這個變量名,我們可以知道,當這個MessageQueue正在quit時,會返回null。也就是說,當MessageQueue要被回收時,Looper會退出它的loop死循環。其他的情況下,loop都是在進行死循環的。
??但是,在哪一種情況下MessageQueue會被回收呢?我們發現MessageQueue的quit方法是默認修飾符,也就是說,只能在相同的包下,這個方法才能被調用!在我們的應用程序代碼中是不能直接調用的,那哪里可以調用了quit方法呢?我們在Looper的quit方法里面找到了答案:

    public void quit() {
        mQueue.quit(false);
    }

??我們知道,每一個Looper對象是被放在了一個ThreadLocal變量中,也就是說,在哪個線程調用Looper的quit方法,就是quit哪個線程的MessageQueue,從而退出這個Looper的loop循環。
??例如:

    private void sendMessage() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                Handler handler = new Handler(){
                    @Override
                    public void handleMessage(Message msg) {
                        Log.i("pby123", (msg.obj) + "");
                    }
                };
                Message message = Message.obtain();
                message.obj = "我是Message";

                handler.sendMessageDelayed(message, 2000);
                Looper.loop();
                Log.i("pby123", "123");


            }
        }).start();
    }

??上面的代碼中,我們在一個新的線程里面創建了一個Handler、Looper,然后使用Handler發送了一個消息,最后將調用Looper的loop方法,讓這個Looper活起來!然后我們來看看log:



??我們會發現,123這個log始終沒有打印出來,也就是說,根本沒有執行到這一句話,從而推出此時Looper的loop方法還在執行,當然如果沒有消息的話,應該是被阻塞住了。這個現象間接的證明了,我們對源碼的理解!
??這里需要注意的是,在同一個線程中,代碼不能這樣寫:

        new Thread(new Runnable() {
            @Override
            public void run() {
                //這里將Looper的prepare方法注釋了
                //表示當前的線程Looper沒有被初始化
                Looper.prepare();
                Handler mHandler = new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        Log.i("pby123", (msg.obj) + "");
                    }
                };
                
                Looper.loop();

                Message message = Message.obtain();
                message.obj = "我是Message";

                mHandler.sendMessageDelayed(message, 2000);
                Log.i("pby123", "123");


            }
        }).start();

??這樣寫的話,loop一直在執行,根本不能執行到loop的代碼!
??從而得知:

??1.除主線程之外,所有線程的Looper的loop方法不會自動調用,盡管我們調用Handler發送消息的相關方法。只有當我們在線程中明確調用loop方法,才會進行loop的循環。
??2.loop方法是一個死循環,它不斷的從MessageQueue里面去拿Message。只有當取出的Message為null時,loop的死循環才能被結束。
??2.在MessageQueue的next方法里面,如果取出的Message的執行時間大于當前的時間的話,當前線程就會被阻塞相應的差值時間。但是如果當前的MessageQueue為空的話,那么就會當前的線程就會被無限期的阻塞。
??3.如果當前的MessageQueue被調用了quit方法,Looper的loop方法從MessageQueue里面取出的是null,從而導致Looper的loop方法執行完成。
??4.MessageQueue的quit方法不能被直接調用,而是通過Handler的quit來間接實現的!

??到這里,我們可以知道,為什么loop方法不會導致ANR。這是因為loop不是一直執行,而是當前MessageQueue里面有Message時,才會不斷的去獲取Message;如果MessageQueue是空的話,線程會被阻塞,只有當消息的時候才會被喚醒,loop方法才算是又活起來了!

(2).enqueueMessage方法

??說實話,next方法真的不是很好的理解,特別是結合了Handler、Looper之后!但是,我們最終還是對next有了一個大概的理解,我可不敢說深入的理解,哈哈!因為自己本來就是一個菜雞!
??對next方法有了一個大概的理解,那么理解enqueueMessage方法應該不是很難的!先來看看enqueueMessage方法的代碼:

    boolean enqueueMessage(Message msg, long when) {
        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;
    }

??上面的代碼中,我刪除了部分我認為不重要的代碼。這個方法非常好理解,首先判斷這個MessageQueue是否被標記為回收。如果被回收了的話,返回false,表示插入新的Message失敗;其次如果沒有被回收,那么插入將Message插入到正確的位置,這個正確的位置表示的是,按照Message的執行時間從小到大排序,時間最小的,放在隊頭!
??我覺得這里最重要的一步是這個:

            if (needWake) {
                nativeWake(mPtr);
            }

??如果當前需要喚醒的話,就去喚醒!

5.總結

??說實話,現在這篇文章寫得差不多了,該寫一個總結,將理解到的知識概括一下!

??1.在創建Handler之前,必須將當前線程的Looper線程創建完畢,也就是Looper的preapre方法必須放在Handler創建之前。之所以在線程中不用創建Looper,那是因為Google爸爸在ActivityThread的main方法里面已經給我們創建好了!
??2.Looper不會自動調用,必須在本線程中的最后一行代碼來調用!因為一旦loop方法執行完畢,表示當前的線程即將執行完畢。這個可以參考ActivityThread的main方法代碼!
??3.在調用Handler的發送Message的相關方法時,最終會將該Message放入與該線程綁定的MessageQueue里面。在放入Message,需要注意的是,Message本身帶了when屬性,這個表示Message時間,MessageQueue內部使用的這個When來排的序。
??4.在Looper的loop方法中,會不斷的調用MessageQueue的next來獲取一個Message。在獲取Message時,要分為兩種情況:1.Message不為null,那么就該Message分發到Message屬于的那個Handler里面去消耗;2.如果為null的話,表示當前的MessageQueue已經被調用quit方法了,loop方法就會結束,整個線程就會結束!
??5.在MessageQueue的next方法里面,如果獲取的Message執行時間大于當前的時間,表示還沒有達到該Message的執行時機,于是讓當前的線程阻塞相應的時間;如果獲取的Message符合執行時機的話,那么立即返回;如果當前的MessageQueue為空了,立即阻塞當前的線程!

??最后,在結合的知識來解釋一下,為什么我們在一個子線程中發送一個Message,主線程會被接受得到。
??首先,我們我們使用的是在主線程中創建Handler來發送Message,雖然說是在子線程發送Message,但是Message有一個target用來記錄發送它的Handler。同時用于Handler在創建的時候,會持有當前線程的Looper和MessageQueue,所以這個Message最終還是發送了主線程的MessageQueue中。而在ActivityThread的main方法里面,主線程的Looper在不斷的loop,所以不斷的從自己線程的MessageQueue取Message來消耗,Message被獲取之后,然后交給了target的handlerMessage方法去消耗,這個target就是在主線程我們自己創建的Handler。最終,我們就能知道為什么在子線程里面發送的Message能夠到達主線程中的Handler

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,310評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,951評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,796評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,566評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,055評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,303評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,799評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,683評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,899評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,135評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,520評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,757評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,528評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,844評論 2 372

推薦閱讀更多精彩內容