Handler機(jī)制淺析

本文我們主要是要介紹Handler機(jī)制姓赤,但是涉及到Handler又不得不介紹Message,MessageQueue,Looper熬苍,Handler機(jī)制主要是依賴后面幾個的,所以我們在文中會一次介紹到他們幾個府框。

通過本文你可能會了解到一下幾點(diǎn)

  • 1.Handler機(jī)制及Handler與Message,MessageQueue,Looper的關(guān)系
  • 2.Handler在子線程中的應(yīng)用及原理
  • 3.Message的復(fù)用機(jī)制
  • 4.為什么主線程可以有死循環(huán)(loop())

Handler源碼位置:

android\frameworks\base\core\java\android\os\Handler.java

首先看他的構(gòu)造:

    public Handler() {
        this(null, false);
    }

    public Handler(Callback callback) {
        this(callback, false);
    }

    public Handler(Looper looper) {
        this(looper, null, false);
    }

    public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }

    public Handler(boolean async) {
        this(null, 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;
    }

    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

一般我們都用的是無參的構(gòu)造血筑。先說一下那個Callback痒钝,他是一個接口:

    public interface Callback {
        public boolean handleMessage(Message msg);
    }

這是處理消息的一種手段迅皇,一般我們也很少用从铲,因?yàn)槲覀兌贾貙懥薶andleMessage方法纫骑,效果一樣的蝎亚,我們主要從無參構(gòu)造這條線看下去。

在無參構(gòu)造中先馆,直接調(diào)用了參數(shù)類型為Callback 和boolean 的構(gòu)造颖对。這里面他給Looper賦值為Looper.myLooper()。來看一下這個方法:

Looper源碼位置:

android\frameworks\base\core\java\android\os\Looper.java
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

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

這個方法實(shí)際上就是獲取當(dāng)前線程的looper磨隘,ThreadLocal是Java的lang包中的類缤底,他的set方法是在當(dāng)前線程中保存一個對象,get是從當(dāng)前線程中取出那個對象番捂。這里存取的就是Looper个唧。但這個Looper在哪存的呢?還是從源碼中找答案:

    public static void prepare() {
        prepare(true);
    }

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

有些人看到這應(yīng)該就明白了设预,這里也給出了另一個問題的答案徙歼,為什么在子線程中使用Handler或者Toast時要先調(diào)用Looper.prepare()。原來這個方法有一個重要的作用就是給當(dāng)前線程設(shè)置Looper鳖枕,如果不設(shè)置魄梯,就是null,當(dāng)然會有問題宾符。但為什么主線程中不需要呢酿秸?

這個問題要從app的創(chuàng)建說起,但具體的app啟動流程這里就不詳細(xì)敘述了魏烫,只說明一點(diǎn)辣苏,在啟動app的進(jìn)程時,Zygote會通過socket接受AMS的請求哄褒,通過反射的方法調(diào)用ActivityThread的main方法稀蟋,ActivityThread也就是一個應(yīng)用的線程,也就是主線程呐赡,我們看一下這個方法:

    public static void main(String[] args) {
        ....

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        ....
        Looper.loop();

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

這里調(diào)用了prepareMainLooper方法退客,它和prepare有什么區(qū)別呢?直接看源碼:

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

可見prepareMainLooper也是調(diào)用了prepare链嘀,只有參數(shù)的差別萌狂,一個為false一個為true。這個參數(shù)最后用到了Looper的構(gòu)造上:

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

在構(gòu)造上又傳給了MessageQueue管闷。這個參數(shù)quitAllowed的作用在MessageQueue中很清楚:

android\frameworks\base\core\java\android\os\MessageQueue.java
    void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        ....
    }

只在這里用到了上文中的參數(shù)quitAllowed粥脚,也就是為true時,會報(bào)異常包个,異常也很清楚刷允,就是主線程不允許調(diào)用quit退出冤留,這個值只在調(diào)用prepareMainLooper方法時為true,我們用的時候都是調(diào)用prepare方法树灶,也就是為false纤怒。

這里做一個小結(jié),Looper.prepare()最主要的作用是初始化Looper天通,而Handler是基于Looper泊窘,一個線程創(chuàng)建后是沒有Looper,主線程也不例外像寒,要想使用Handler就必須初始化烘豹,一般線程調(diào)用prepare方法,主線程調(diào)用prepareMainLooper诺祸。

下面我們還回到構(gòu)造方法中去携悯。

這里判斷了一下mLooper 是否為空,若為空拋異常筷笨,這也就是子線程中不能直接使用的直接來源憔鬼。然后初始化了MessageQueue類型成員變量mQueue ,他從Looper中獲得胃夏,Looper中是在構(gòu)造函數(shù)中初始化的轴或。又初始化了成員變量mCallback ,當(dāng)然我們這條線上他為空仰禀。最后初始化了標(biāo)志位mAsynchronous 照雁,我們這里為false,這個標(biāo)志位的左后后面會提到悼瘾。

到這里Handler的構(gòu)造就完成了囊榜,除了初始化一些東西也沒做什么审胸。我們一般用的時候在構(gòu)造完之后亥宿,就是重寫handleMessage方法,用于處理接受到的消息砂沛。下面分析從發(fā)送消息到接受消息的流程烫扼。

先從最普通的sendMessage開始:

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

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

發(fā)現(xiàn)sendMessage內(nèi)部調(diào)用的是sendMessageDelayed,也就是延遲為0碍庵,很好理解映企。主要看sendMessageAtTime

    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) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

這個里面先獲取了消息隊(duì)列,這個變量在構(gòu)造中初始化静浴,就是Looper中的隊(duì)列堰氓,在looper中的構(gòu)造中初始化。接下來調(diào)用了enqueueMessage方法苹享,這個方法里双絮,首先設(shè)置Message 的target 為當(dāng)前Handler,方便調(diào)用sendToTarget時使用。最后調(diào)用了MessageQueue 的enqueueMessage方法入隊(duì)囤攀。我們看一下這個方法软免,還是很有意思的:

    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) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                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; 
                prev.next = msg;
            }

            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

這里就需要有一些數(shù)據(jù)結(jié)構(gòu)的功底了。這個方法的主要思想是利用next 成員創(chuàng)建一個鏈表焚挠。方法中首先判斷了target 是否為空及此msg是否使用膏萧,然后判斷了mQuitting變量,這個變量在quit方法中為true蝌衔,在主線程中這個變量永遠(yuǎn)為false榛泛。接下來標(biāo)記該msg已用,然后配置該msg的執(zhí)行時間噩斟。

下面就是鏈表的操作了挟鸠,如果鏈表為空,或者該msg需要立即執(zhí)行或者亩冬,該msg執(zhí)行時間小于第一位的msg艘希,就把他插入到鏈表頭部(鏈表插入方法中的頭插法,代碼看不懂的可以畫圖手動操作一些硅急,會有奇效)覆享。

如果不滿足上面三個條件,就循環(huán)遍歷所有鏈表結(jié)點(diǎn)营袜,根據(jù)時間插入到合適位置撒顿,這里涉及到鏈表的插入操作,代碼看不懂的畫圖演示比較直觀有效荚板。其中有一點(diǎn)很重要凤壁,這個鏈表是有序的。

消息進(jìn)入隊(duì)列后跪另,發(fā)消息的第一步就是要先從隊(duì)列中取消息拧抖,我們來看這一步,這一步在Looper中的loop方法免绿,還記得如何在子線程中使用Handler么唧席,執(zhí)行完prepare方法后,就可以設(shè)置Handler了嘲驾,但此時Handler還沒有用淌哟,缺少一個驅(qū)動,這個驅(qū)動就是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;

        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); 
            if (msg == null) {
                return;
            }

            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (slowDispatchThresholdMs > 0) {
                final long time = end - start;
                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }

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

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

這里首先也驗(yàn)證了一下,保證Looper 不為空誊垢。然后獲取了MessageQueue 掉弛。接下來開啟了一個死循環(huán)(看到這喻杈,有人也許會問,在ActivityThread中也調(diào)用了該方法狰晚,為什么主線程中有個死循環(huán)筒饰,而且還沒問題,這個問題最后在講)壁晒,這個循環(huán)的目的就是不斷地從隊(duì)列中取消息瓷们,取消息用的是queue.next(),這是一個阻塞方法(這個過程類似于Socket中服務(wù)端監(jiān)聽請求的過程)秒咐,我們來看next方法:

    Message next() {
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1;
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        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 {
                    nextPollTimeoutMillis = -1;
                }

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

                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    mBlocked = true;
                    continue;
                }

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

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


            pendingIdleHandlerCount = 0;
            nextPollTimeoutMillis = 0;
        }
    }

先看第一個判斷谬晕,這里出現(xiàn)一個變量mPtr,這個變量是什么哪里來的携取?他是在構(gòu)造中初始化的攒钳,調(diào)用了native方法nativeInit();既然是源碼分析,所以雖然是jni我們也要去看看雷滋。源碼位置:

android\frameworks\base\core\jni\android_os_MessageQueue.cpp
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
    if (!nativeMessageQueue) {
        jniThrowRuntimeException(env, "Unable to allocate native queue");
        return 0;
    }

    nativeMessageQueue->incStrong(env);
    return reinterpret_cast<jlong>(nativeMessageQueue);
}

這里創(chuàng)建了native層的MessageQueue對象不撑,這個類定義就在android_os_MessageQueue.cpp中。nativeInit的返回值只是為了標(biāo)志NativeMessageQueue是否創(chuàng)建成功晤斩,不成功的會返回0焕檬,成功的返回指針地址(轉(zhuǎn)為long類型),另外在MessageQueue 的dispose()方法中會把他置為0澳泵。我們主要還是回到next方法中实愚,這個標(biāo)志位主要標(biāo)志該隊(duì)列是否可用,不可用返回null即可兔辅。下面才是正式的取消息部分腊敲。

這里你會驚奇的發(fā)現(xiàn)有一個死循環(huán),這也就是為什么這個方法時阻塞的原因维苔∨龈ǎ看來到了較底層的部分,也沒有什么花哨技巧了蕉鸳,直接用上死循環(huán)來處理乎赴。我們直接看synchronized 代碼塊的第一個判斷,里面有一個target == null的判斷潮尝,我們在enqueueMessage知道,如果target 為null的話會直接拋異常饿序,那么會什么會有target 為空的msg呢勉失?實(shí)際上MessageQueue 有這樣一個方法:

   public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }

    private int postSyncBarrier(long when) {
        synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;

            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) { 
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

這個方法也是插入一個msg,但是是target為空的特殊msg原探,稱之為SyncBarrier乱凿。這種msg的作用就是攔截顽素,所有執(zhí)行時間在這個msg之后的同步消息都不會執(zhí)行,直到遇到下一個SyncBarrier徒蟆,也就是異步和同步之分胁出。如果你記憶力夠好或者沒有被源碼繞暈的話,希望你記得在Handler構(gòu)造中初始化了一個參數(shù)mAsynchronous 段审,若從空參來的話全蝶,他為false。在Handler的enqueueMessage中發(fā)揮作用寺枉,若為true的話抑淫,所有msg會setAsynchronous(true)。一直影響到next方法里姥闪,若isAsynchronous()為true始苇,會不受異步影響,若isAsynchronous()為false筐喳,會受異步影響催式,跳過所有同步消息。好吧避归,跨度有點(diǎn)大蓄氧,多回頭看看源碼捋一捋就好。

我們從正常情況開始分析槐脏,就是isAsynchronous()為false喉童,但是沒有插入SyncBarrier。這時取到頭部msg顿天,判斷是否為null堂氯,不為空的話,檢測執(zhí)行時間牌废,如果未到時間咽白,則設(shè)置nextPollTimeoutMillis 為等待時間,若到時間鸟缕,則從隊(duì)列移除該msg晶框,重設(shè)頭結(jié)點(diǎn),并返回該消息懂从。這時正常取到msg的流程授段,若msg==null,設(shè)置nextPollTimeoutMillis =-1番甩,然后判斷是不是因?yàn)檎{(diào)用quit導(dǎo)致的(調(diào)用該方法會清空隊(duì)列)侵贵,若是則返回null。最后看一下是否有IdleHandlers缘薛,IdleHandler是是一種利用系統(tǒng)空閑時機(jī)去處理一些不重要事件用的窍育,如gc卡睦,這和我們要講的消息關(guān)系不大。且看邏輯漱抓,如果沒有要執(zhí)行的IdleHandlers表锻,則阻塞設(shè)為true,繼續(xù)循環(huán)(此時意味著取到了一個取到了一個消息乞娄,但還沒到執(zhí)行事件瞬逊,nextPollTimeoutMillis為大于零某數(shù),或沒有取到消息补胚,nextPollTimeoutMillis為-1)码耐,下一次循環(huán),會到nativePollOnce方法溶其,我們這里介紹一下這個方法:

static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
        jlong ptr, jint timeoutMillis) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}

void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
    mPollEnv = env;
    mPollObj = pollObj;
    mLooper->pollOnce(timeoutMillis);
    mPollObj = NULL;
    mPollEnv = NULL;

    if (mExceptionObj) {
        env->Throw(mExceptionObj);
        env->DeleteLocalRef(mExceptionObj);
        mExceptionObj = NULL;
    }
}

他先還原了之前保存的NativeMessageQueue對象骚腥,然后執(zhí)行了mLooper->pollOnce(timeoutMillis)。mLooper是native層的Looper對象瓶逃,在NativeMessageQueue構(gòu)造中初始化束铭,他和java的Looper并沒多大關(guān)系:

NativeMessageQueue::NativeMessageQueue() :
        mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
        mLooper = new Looper(false);
        Looper::setForThread(mLooper);
    }
}

Looper.cpp位置:

android\system\core\libutils\Looper.cpp

pollOnce內(nèi)部又調(diào)用了pollInner方法,這個方法非常長厢绝,這里就不貼出來了契沫,關(guān)鍵一步在:

int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

到這里已經(jīng)超出Android編程范圍了,涉及到了Linux編程昔汉,這里涉及到了epoll機(jī)制懈万,這里可以這樣理解,在java層靶病,next方法會阻塞会通,阻塞的實(shí)質(zhì)就是這里,java層調(diào)用的時nativePollOnce方法娄周,next初始時nextPollTimeoutMillis為0涕侈,立即返回,不阻塞煤辨,隨后取一個msg裳涛,計(jì)算等待時間,下次循環(huán)調(diào)用nativePollOnce時開始阻塞众辨。(簡單介紹一下端三,需要詳細(xì)了解的朋友還是要翻看代碼)。

這里面還有一點(diǎn)泻轰,在沒取到消息時技肩,nextPollTimeoutMillis=-1,可能會永久阻塞浮声,但是在MessageQueue 的enqueueMessage方法中虚婿,當(dāng)成功添加后,有一個判斷:

if (needWake) {
    nativeWake(mPtr);
}

這個needWake在添加隊(duì)列中第一個消息時泳挥,被賦值然痊,就是mBlocked的值,mBlocked 在next()中需要循環(huán)時被置為true屉符。nativeWake就是去喚醒剧浸,不再阻塞。起始只有隊(duì)列為空時再回取到null消息矗钟,所以在添加第一個消息到隊(duì)列時唆香,自然要解除阻塞《滞В可以看一下nativeWake:

static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->wake();
}

void NativeMessageQueue::wake() {
    mLooper->wake();
}

void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
    ALOGD("%p ~ wake", this);
#endif

    uint64_t inc = 1;
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
    if (nWrite != sizeof(uint64_t)) {
        if (errno != EAGAIN) {
            LOG_ALWAYS_FATAL("Could not write wake signal to fd %d: %s",
                    mWakeEventFd, strerror(errno));
        }
    }
}

在Looper::rebuildEpollLocked方法中

    eventItem.data.fd = mWakeEventFd;
    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);

wake中執(zhí)行了write(mWakeEventFd, &inc, sizeof(uint64_t))躬它,mWakeEventFd就是epoll監(jiān)聽的事件,通過wake()就解除了阻塞东涡。

分析了這么長冯吓,又摻入native部分,總算把取消息的流程說完了疮跑,這一部分要多看幾遍源碼组贺,仔細(xì)琢磨。下面我們回到loop方法中祖娘。

當(dāng)取到一個消息失尖,并不為空時,調(diào)用了msg.target.dispatchMessage(msg)去分發(fā)事件:

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

到這里就回調(diào)到了handleMessage渐苏,進(jìn)行了一次選擇掀潮,如果實(shí)現(xiàn)了callback 就走callback ,否則走h(yuǎn)andleMessage方法整以。到這里整個發(fā)送接收體系就完全介紹完了胧辽。

在loop方法最后,調(diào)用了msg.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) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

這個方法是在一個Message用完之后調(diào)用的公黑,清空了Message的所有內(nèi)容邑商,并且加入了一個Pool,看著像一個池子凡蚜,實(shí)際上是Message利用next成員建立起的一個鏈表人断,為什么要用鏈表,且看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
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

這是一個靜態(tài)方法朝蜘,可以代替new Message()獲得一個Message實(shí)例恶迈。方法中,就是取到了鏈表的頭部那個Message,這個鏈表在每次loop循環(huán)后增長暇仲,形成緩存池步做,實(shí)現(xiàn)了Message的復(fù)用,節(jié)省了內(nèi)存奈附。這個鏈表最大長度為50(在Android 8.0中)全度,所以日常開發(fā)時,我們應(yīng)該盡可能的調(diào)用obtain方法去獲得一個Message,實(shí)際上源碼也是這樣做的斥滤。

    public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }

    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }

除了讓Looper控制建立緩存将鸵,我們也可以自己回收,雖然我們不能調(diào)用recycleUnchecked方法(有訪問權(quán)限控制)佑颇,但是我們可以調(diào)用recycle方法顶掉,回收一個Message,他只是做一個安全檢查而已挑胸。

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

最后痒筒,我們來解釋文中的一個遺留問題,為什么ActivityThread這個應(yīng)用主線程可以有一個死循環(huán)嗜暴。

首先看凸克,如果沒有死循環(huán),Looper.loop()執(zhí)行完之后闷沥,直觀來看會拋一個異常

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

實(shí)際上萎战,就算不拋異常,那么到這這個main方法就執(zhí)行完了舆逃,你站在java代碼角度看蚂维,main就執(zhí)行完,程序不就結(jié)束了么路狮?

有過socket開發(fā)經(jīng)驗(yàn)的朋友虫啥,肯定不會讓服務(wù)端接受一個請求后就讓main函數(shù)結(jié)束,一般會一直循環(huán)監(jiān)聽請求奄妨。

還有另外一個更生動的場景涂籽,我們在學(xué)習(xí)c語言時,第一個程序都是helloworld砸抛,有些編譯器很智能评雌,打印完helloworld,程序暫停直焙,但有些直接就退出景东,現(xiàn)象就是我們執(zhí)行程序時,屏幕閃一下黑框就什么都沒了奔誓,大多數(shù)人都會一臉懵逼的去百度斤吐,然后加個暫停或者等待輸入的函數(shù),讓控制臺保留下來和措,得意的看著自己第一個程序庄呈。但實(shí)際上程序執(zhí)行完打印語句后就應(yīng)該退出了,我們只是讓程序阻塞住而已臼婆。

android也一樣抒痒,所以要有一個死循環(huán)幌绍,保證主線程不自動結(jié)束颁褂。但為什么要死循環(huán)呢?注意一點(diǎn)傀广,loop()方法的循環(huán)并不是無意義的循環(huán)颁独,而是不斷取事件執(zhí)行事件,Android是建立在事件驅(qū)動機(jī)制上的伪冰,程序在創(chuàng)建運(yùn)行過程中有很多事件誓酒,都是Handler處理的,所以要有一個loop()去驅(qū)動事件執(zhí)行贮聂。我們大致可以看下ActivityThread中的Handler方法:



定義了非常多事件靠柑,例如第一個LAUNCH_ACTIVITY事件中,就創(chuàng)建了Activity實(shí)例吓懈。

至于為什么不會ANR歼冰,其實(shí)ANR并不是一種錯誤,只是系統(tǒng)認(rèn)為我們在某些地方耗時太長耻警,造成了流程上的阻塞隔嫡,是一種檢測機(jī)制。從上文我們可以知道甘穿,Handler是順序執(zhí)行事件的腮恩,一個事件執(zhí)行的時間過長,就導(dǎo)致后續(xù)事件阻塞温兼,所以必須有ANR機(jī)制去檢測秸滴。換句話說,如果沒有ANR檢測募判,其實(shí)也是沒問題的荡含,只不過會導(dǎo)致界面一直卡著,或者我們執(zhí)行某個操作長時間無反應(yīng)而已兰伤,正如出現(xiàn)無響應(yīng)時系統(tǒng)會給我們一個繼續(xù)等待的選項(xiàng)内颗。而這里的死循環(huán),相當(dāng)于后臺敦腔,事件相當(dāng)于前臺均澳,后臺阻塞的去監(jiān)聽前臺請求是沒問題的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市找前,隨后出現(xiàn)的幾起案子糟袁,更是在濱河造成了極大的恐慌,老刑警劉巖躺盛,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件项戴,死亡現(xiàn)場離奇詭異,居然都是意外死亡槽惫,警方通過查閱死者的電腦和手機(jī)周叮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來界斜,“玉大人仿耽,你說我怎么就攤上這事「鬓保” “怎么了项贺?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長峭判。 經(jīng)常有香客問我开缎,道長,這世上最難降的妖魔是什么林螃? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任奕删,我火速辦了婚禮,結(jié)果婚禮上治宣,老公的妹妹穿的比我還像新娘急侥。我一直安慰自己,他們只是感情好侮邀,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布坏怪。 她就那樣靜靜地躺著,像睡著了一般绊茧。 火紅的嫁衣襯著肌膚如雪铝宵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天华畏,我揣著相機(jī)與錄音鹏秋,去河邊找鬼。 笑死亡笑,一個胖子當(dāng)著我的面吹牛侣夷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播仑乌,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼百拓,長吁一口氣:“原來是場噩夢啊……” “哼琴锭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起衙传,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤决帖,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蓖捶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體地回,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年俊鱼,在試婚紗的時候發(fā)現(xiàn)自己被綠了刻像。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡亭引,死狀恐怖绎速,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情焙蚓,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布洒宝,位于F島的核電站购公,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏雁歌。R本人自食惡果不足惜宏浩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望靠瞎。 院中可真熱鬧比庄,春花似錦、人聲如沸乏盐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽父能。三九已至神凑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間何吝,已是汗流浹背溉委。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留爱榕,地道東北人瓣喊。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像黔酥,于是被迫代替她去往敵國和親藻三。 傳聞我的和親對象是個殘疾皇子八匠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344

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