Android消息機(jī)制Handler源碼分析

系列文章

前言

Android和Windows一樣都采用了消息機(jī)制戚炫,從開(kāi)發(fā)角度說(shuō),Handler是Android消息機(jī)制的上層接口栗菜,開(kāi)發(fā)過(guò)程中和Handler交互即可泡一。Android規(guī)范限制我們不能在子線程中更新UI,只能在主線程中更新UI哈恰,Handler可以輕松的將一個(gè)任務(wù)切換到Handler所在的線程中去執(zhí)行。所以,開(kāi)發(fā)過(guò)程中Handler通常被用來(lái)更新UI,利用Handler將更新UI的操作切換到主線程中執(zhí)行赶么。

ViewRootImpl對(duì)我們的UI操作進(jìn)行了驗(yàn)證,具體是在checkThread()方法中完成的脊串,如下:

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}

ViewRootImpl是所有View的根,WindowView通過(guò)ViewRootImpl建立聯(lián)系辫呻,checkThread()方法校驗(yàn)當(dāng)前線程是否是ViewRootImpl被創(chuàng)建時(shí)所在的線程。具體請(qǐng)參考Android中子線程真的不能更新UI嗎洪规?

說(shuō)明:本文中的源碼都是基于Android-25版本印屁。

Handler

Android的消息機(jī)制主要是指Handler的運(yùn)行機(jī)制,Handler的運(yùn)行需要MessageQueueLooper支撐斩例,MessageQueue是消息隊(duì)列雄人,內(nèi)部存儲(chǔ)了一組消息,而Looper則是消息循環(huán)念赶,Looper會(huì)無(wú)限循環(huán)查看是否有新消息础钠,如果有的話處理消息,沒(méi)有的話就一直等待叉谜。

Looper中有一個(gè)特殊概念ThreadLocal旗吁,ThreadLocal并不是線程,作用是在每個(gè)線程中存儲(chǔ)數(shù)據(jù)停局,可以讓不同線程保存在同一個(gè)ThreadLocal中的對(duì)象數(shù)據(jù)互不干擾很钓。ThreadLocal的工作原理請(qǐng)參考:Android的消息機(jī)制之ThreadLocal的工作原理

工作過(guò)程

Handler在創(chuàng)建過(guò)程時(shí)會(huì)采用當(dāng)前線程的Looper來(lái)構(gòu)造消息循環(huán)系統(tǒng)香府,那么Handler內(nèi)部是如何獲取到Looper呢?通過(guò)ThreadLocal可以很輕松的獲取每個(gè)線程的Looper码倦,但是線程默認(rèn)是沒(méi)有Looper的企孩,要使用Handler就必須為線程創(chuàng)建Looper。而主線程(UI)線程即ActivityThread被創(chuàng)建時(shí)就初始化了Looper袁稽,所以我們?cè)谥骶€程中可以默認(rèn)使用Looper勿璃。

Handler的默認(rèn)構(gòu)造方法最終會(huì)通過(guò)以下構(gòu)造方法實(shí)現(xiàn):

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

從中可以看到,如果當(dāng)前線程沒(méi)有Looper,則會(huì)拋出異常推汽,而mLooper是由Looper.myLooper()方法返回的补疑,代碼如下:

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

Looper.myLooper()就是返回了保存在ThreadLocal中的Looper對(duì)象。
Handler創(chuàng)建完畢后歹撒,Handler和Looper以及MessageQueue便協(xié)同工作莲组,構(gòu)成一個(gè)消息系統(tǒng)。通過(guò)Handler的post系列方法將一個(gè)Runnable投遞到Looper栈妆,或者send系列方法發(fā)送一個(gè)消息到Looper中胁编,通過(guò)調(diào)用MessageQueue的enqueueMessage()方法將消息放入消息隊(duì)列中,此時(shí)Looper發(fā)現(xiàn)有新消息到來(lái)鳞尔,處理此消息嬉橙。最終消息中的Runnable或者Handler的handleMessage()方法就會(huì)被調(diào)用,這樣就又將任務(wù)切換到創(chuàng)建Handler的線程中去了寥假。過(guò)程如圖所示:

Handler工作過(guò)程

工作原理

Handler的工作主要包括發(fā)送和接收消息市框。

發(fā)送消息

上面提到,發(fā)送消息是通過(guò)一系列post或者send方法實(shí)現(xiàn)糕韧,post系列的方法最終都是通過(guò)send系列方法實(shí)現(xiàn)的枫振。Handler中有關(guān)發(fā)送消息的send系列方法源碼如下:

public final boolean sendMessage(Message msg){
    return sendMessageDelayed(msg, 0);
}
 
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);
}

public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
}

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

public final boolean sendMessageAtFrontOfQueue(Message msg) {
    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, 0);
}
    
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

可以看到除了sendMessageAtFrontOfQueue()方法,其余最終都是調(diào)用sendMessageAtTime()方法萤彩,在sendMessageAtTime()方法中調(diào)用enqueueMessage()方法粪滤,再調(diào)用MessageQueueenqueueMessage()方法,發(fā)送消息就是向消息隊(duì)列中插入一條消息雀扶,MessageQueue的next()方法將這條消息返回給Looper杖小。

處理消息

Looper接收到消息后處理,并最終交由Handler的dispatchMessage()處理愚墓,源碼如下:

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

首先檢查Messagecallback予权,不為null則調(diào)用handleCallback(),源碼如下:

private static void handleCallback(Message message) {
    message.callback.run();
}

Messagecallback是一個(gè)Runnable對(duì)象浪册,就是Handler中post方法的Runnable參數(shù)扫腺。post方法源碼如下:

public final boolean post(Runnable r){
    return  sendMessageDelayed(getPostMessage(r), 0);
}

post方法利用sendMessageDelayed()方法發(fā)送消息,其中又調(diào)用了getPostMessage()方法村象,其源碼如下:

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

該方法傳入一個(gè)Runnable參數(shù)笆环,得到一個(gè)Message對(duì)象攒至,將Runnable參數(shù)賦值給Message對(duì)象的callback字段。

然后檢查mCallback躁劣,不為null則調(diào)用mCallbackhandleMessage()嗓袱,Callback是一個(gè)接口,定義如下:

/**
 * Callback interface you can use when instantiating a Handler to avoid
 * having to implement your own subclass of Handler.
 *
 * @param msg A {@link android.os.Message Message} object
 * @return True if no further handling is desired
 */
public interface Callback {
    public boolean handleMessage(Message msg);
}

使用Callback創(chuàng)建Handler不需要派生一個(gè)Handler子類习绢,通常開(kāi)發(fā)過(guò)程中都是派生Handler子類并重寫(xiě)其handleMessage()方法來(lái)處理具體消息,Callback提供了另一種使用Handler方法蝙昙。
最后不符合以上兩點(diǎn)時(shí)闪萄,就調(diào)用Handler的handleMessage()來(lái)處理消息。

Looper

Looper在Android消息機(jī)制中扮演消息循環(huán)的角色奇颠,它會(huì)不斷從MessageQueue中查看是否有新消息败去,如果有新消息就立即處理,否則一直阻塞在那里烈拒。

創(chuàng)建Looper

首先看一下構(gòu)造方法圆裕,在構(gòu)造方法中它會(huì)創(chuàng)建一個(gè)MessageQueue消息隊(duì)列,然后將當(dāng)前線程的對(duì)象保存起來(lái)荆几。

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

Handler的工作需要Looper吓妆,沒(méi)有Looper的線程就會(huì)報(bào)錯(cuò),那么如何為線程創(chuàng)建Looper呢吨铸?通過(guò)Looper.prepare()即可為當(dāng)前線程創(chuàng)建一個(gè)Looper行拢,同時(shí)會(huì)將Looper保存在ThreadLocal中,Handler的構(gòu)造函數(shù)便是從ThreadLocal中獲取Looper對(duì)象诞吱,從代碼如下:

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

接著通過(guò)Looper.loop()來(lái)開(kāi)啟消息循環(huán)舟奠。為一個(gè)子線程創(chuàng)建Looper和Handler的代碼如下所示:

new Thread("Thread#2"){
    @Override
    public void run(){
        Looper.prepare();
        Handler handler = new Handler();
        Looper.loop();
    };
}.start();

Looper除了prepare()方法外,還提供了prepareMainLooper()方法房维,這個(gè)方法主要是給主線程即ActivityThread創(chuàng)建Looper使用的沼瘫,本質(zhì)也是通過(guò)prepare()方法來(lái)實(shí)現(xiàn)的,源碼如下:

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

在程序啟動(dòng)的時(shí)候咙俩,系統(tǒng)已經(jīng)幫我們自動(dòng)調(diào)用了Looper.prepare()方法耿戚。ActivityThread中的main()方法調(diào)用了Looper.prepareMainLooper()方法,而這個(gè)方法又會(huì)再去調(diào)用Looper.prepare()方法暴浦。因此我們應(yīng)用程序的主線程中會(huì)始終存在一個(gè)Looper對(duì)象溅话,從而不需要再手動(dòng)去調(diào)用Looper.prepare()方法了。

由于主線程Looper比較特殊歌焦,所以Looper提供了一個(gè)getMainLooper()方法飞几,通過(guò)它可以在任何地方獲取主線程的Looper。

Looper退出

Looper也提供了退出方法独撇,quit()quitSafely()屑墨,區(qū)別在于quit()是直接退出Looper躁锁,而quitSafely()只是設(shè)定一個(gè)退出標(biāo)記,然后把消息隊(duì)列中的已有消息全部處理完畢后再退出卵史。quit()方法最終調(diào)用的是MessageQueue中的removeAllMessagesLocked()方法战转,quitSafely()最終調(diào)用的是MessageQueue中的removeAllFutureMessagesLocked()方法,源碼如下:

private void removeAllMessagesLocked() {
        Message p = mMessages;
        while (p != null) {
            Message n = p.next;
            p.recycleUnchecked();
            p = n;
        }
        mMessages = null;
    }

private void removeAllFutureMessagesLocked() {
    final long now = SystemClock.uptimeMillis();
    Message p = mMessages;
    if (p != null) {
        if (p.when > now) {
            removeAllMessagesLocked();
        } 
        else {
            Message n;
            for (;;) {
                n = p.next;
                if (n == null) {
                    return;
                }
                if (n.when > now) {
                    break;
                }
                p = n;
            }
            p.next = null;
            do {
                p = n;
                n = p.next;
                p.recycleUnchecked();
            } while (n != null);
    }
}

Looper退出后以躯,通過(guò)Handler發(fā)送的消息會(huì)失敗槐秧,Handler的send方法會(huì)返回false,在子線程中如果為其手動(dòng)創(chuàng)建了Looper忧设,消息處理完畢后應(yīng)該調(diào)用quit()方法來(lái)終止消息循環(huán)刁标,否則這個(gè)子線程會(huì)一直處于等待狀態(tài),退出Looper以后址晕,這個(gè)子線程就會(huì)立刻終止膀懈,因此建議不需要的時(shí)候終止Looper。

消息循環(huán)

Looper通過(guò)loop()方法開(kāi)啟消息循環(huán)谨垃,源碼實(shí)現(xiàn)如下:

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;

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

loop()方法是一個(gè)死循環(huán)启搂,唯一跳出循環(huán)的方法是MessageQueuenext()方法返回了null。當(dāng)Looper的退出方法被調(diào)用時(shí)刘陶,通知消息隊(duì)列消息退出胳赌,當(dāng)消息隊(duì)列被標(biāo)記為退出狀態(tài)時(shí),它的next()方法就會(huì)返回null匙隔。也就是說(shuō)Looper必須退出匈织,否則loop()方法會(huì)無(wú)限循環(huán)下去。
loop()方法會(huì)調(diào)用MessageQueue中的next()方法來(lái)獲取新消息牡直,而next()是一個(gè)阻塞操作缀匕,沒(méi)有新消息時(shí)會(huì)一直阻塞在那里,同理導(dǎo)致loop()阻塞碰逸,如果MessageQueuenext()方法返回了新消息乡小,Looper便會(huì)處理這條消息,通過(guò)以下語(yǔ)句執(zhí)行:

msg.target.dispatchMessage(msg);

這里的msg.target是發(fā)送這條消息的Handler對(duì)象饵史,這樣通過(guò)Handler的dispatchMessage()方法來(lái)處理消息满钟,dispatchMessage()是在創(chuàng)建Handler時(shí)所使用的Looper中執(zhí)行的,這樣就將代碼邏輯切換到指定的線程中了胳喷。

Message & MessageQueue

Message在線程之間傳遞消息湃番,Messagewhat字段,是消息類型字段吭露,arg1arg2攜帶一些整型數(shù)據(jù)吠撮,obj字段攜帶一個(gè)object對(duì)象。
MessageQueue是消息隊(duì)列讲竿,存放所有通過(guò)Handler發(fā)送的消息泥兰。消息一直存放在消息隊(duì)列中弄屡,等待被處理。每個(gè)線程只會(huì)有一個(gè)MessageQueue對(duì)象鞋诗。
MessageQueue主要包含兩個(gè)操作膀捷,插入和讀取,讀取伴隨著刪除操作削彬。euqueueMessage()的作用是往消息隊(duì)列中插入一條消息全庸,next()的作用是從消息隊(duì)列中讀取一條消息并移除。盡管MessageQueue叫做消息隊(duì)列融痛,但是其內(nèi)部是通過(guò)單鏈表的數(shù)據(jù)結(jié)構(gòu)來(lái)維護(hù)消息列表糕篇。euqueueMessage()的源碼如下:

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

從源碼中可以分析出,當(dāng)前消息隊(duì)列的頭結(jié)點(diǎn)為空或待插入的消息需要被立即執(zhí)行時(shí)酌心,就讓當(dāng)前消息成為消息隊(duì)列的新的頭結(jié)點(diǎn),并且如果消息隊(duì)列處于阻塞狀態(tài)挑豌,則將消息隊(duì)列喚醒安券;否則則按消息等待被執(zhí)行的時(shí)間順序,將待插入消息插入消息隊(duì)列中氓英,最后如果需要喚醒消息隊(duì)列侯勉,則通過(guò)native方法nativeWake()來(lái)喚醒消息隊(duì)列。
下面再看看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;
           }

           ...
       }

       ...
   }
}

可以發(fā)現(xiàn)next()方法是一個(gè)無(wú)限循環(huán)方法铝阐,如果消息隊(duì)列中沒(méi)有消息址貌,其會(huì)一直阻塞在這里,有新消息到來(lái)時(shí)徘键,next()方法會(huì)返回這條消息并將其從單鏈表中移除练对。

Android中為什么主線程不會(huì)因?yàn)長(zhǎng)ooper.loop()里的死循環(huán)卡死?

問(wèn)題描述

Android程序的入口點(diǎn)可以認(rèn)為是ActivityThread類的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();
    
    ...
}

可以看到Looper開(kāi)啟了消息循環(huán)螟凭,loop()方法是一個(gè)死循環(huán),但是并沒(méi)有看見(jiàn)有相關(guān)代碼為這個(gè)死循環(huán)準(zhǔn)備了一個(gè)新線程去運(yùn)轉(zhuǎn)它呀,但是主線程卻并不會(huì)因?yàn)?code>Looper.loop()中的這個(gè)死循環(huán)卡死螺男,這是為什么呢?

原因

從上述代碼我們可以發(fā)現(xiàn)纵穿,首先調(diào)用prepareMainLooper()方法為主線程創(chuàng)建一個(gè)消息隊(duì)列下隧;其次,生成一個(gè)ActivityThread對(duì)象谓媒,在其初始化代碼中會(huì)創(chuàng)建一個(gè)H(Handler)對(duì)象淆院,即ActivityThread.H,它內(nèi)部定義了一組消息類型句惯,主要包含了四大組件的啟動(dòng)和停止過(guò)程迫筑,如下所示:

private class H extends Handler {
    public static final int LAUNCH_ACTIVITY         = 100;
    public static final int PAUSE_ACTIVITY          = 101;
    public static final int PAUSE_ACTIVITY_FINISHING= 102;
    public static final int STOP_ACTIVITY_SHOW      = 103;
    public static final int STOP_ACTIVITY_HIDE      = 104;
    public static final int SHOW_WINDOW             = 105;
    public static final int HIDE_WINDOW             = 106;
    public static final int RESUME_ACTIVITY         = 107;
    public static final int SEND_RESULT             = 108;
    public static final int DESTROY_ACTIVITY        = 109;
    public static final int BIND_APPLICATION        = 110;
    public static final int EXIT_APPLICATION        = 111;
    public static final int NEW_INTENT              = 112;
    public static final int RECEIVER                = 113;
    public static final int CREATE_SERVICE          = 114;
    public static final int SERVICE_ARGS            = 115;
    public static final int STOP_SERVICE            = 116;
    
    ...    
}

thread.attach(false)生成了一個(gè)AppplicationThread(Binder)對(duì)象宪赶,ActivityThread通過(guò)ApplicationThreadAMS進(jìn)行進(jìn)程間通信,AMS以進(jìn)程間通信方式完成ActivityThread的請(qǐng)求后會(huì)回調(diào)ApplicationThread中的Binder方法脯燃,Binder負(fù)責(zé)接遠(yuǎn)程ActivityManagerService(AMS)的IPC調(diào)用搂妻,用于接收系統(tǒng)服務(wù)AMS發(fā)來(lái)的消息,收到消息后辕棚,通過(guò)Handler將消息發(fā)送到消息隊(duì)列欲主,UI主線程會(huì)異步的從消息隊(duì)列中取出消息并執(zhí)行操作;最后逝嚎,UI主線程調(diào)用Looper.loop()進(jìn)入消息循環(huán)扁瓢。

Android的Handler消息機(jī)制涉及到Linux的pipe/epoll機(jī)制,MessageQueue沒(méi)有消息時(shí)补君,阻塞在那里引几,主線程會(huì)釋放CPU進(jìn)入休眠狀態(tài),通過(guò)Linux系統(tǒng)的epoll機(jī)制中的epoll_wait函數(shù)進(jìn)行等待挽铁,當(dāng)有新消息來(lái)臨時(shí)伟桅,往pipe(管道)寫(xiě)入端寫(xiě)入消息來(lái)喚醒主線程,其實(shí)就是一個(gè)生產(chǎn)消費(fèi)模型叽掘。
(還有一個(gè)疑問(wèn)楣铁,那就是怎么響應(yīng)點(diǎn)擊事件呢?或者說(shuō)對(duì)通常的GUI模型更扁,如windows都是怎么實(shí)現(xiàn)的呢盖腕?生產(chǎn)消費(fèi)模型?)

以上部分摘自柯元旦<Android內(nèi)核剖析>

Handler的其它用法

除了通過(guò)編寫(xiě)子線程并結(jié)合Handler發(fā)送消息改變UI外浓镜,Handler還有一些其他用法溃列。

View中的post()方法

代碼如下所示:

public boolean post(Runnable action) {
    final AttachInfo attachInfo = mAttachInfo;
    if (attachInfo != null) {
        return attachInfo.mHandler.post(action);
    }
    // Postpone the runnable until we know on which thread it needs to run.
    // Assume that the runnable will be successfully placed after attach.
    getRunQueue().post(action);
    return true;
}

發(fā)現(xiàn)View的post()方法就是調(diào)用了Handler中的post()方法,前文已經(jīng)說(shuō)過(guò)Handler的post()方法了膛薛,不再多解釋哭廉。

Activity中的runOnUiThread()方法

代碼如下所示:

public final void runOnUiThread(Runnable action) {
    if (Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}

先判斷當(dāng)前線程是否是UI線程,如果不是則調(diào)用Handler的post()方法相叁,否則就直接調(diào)用Runnable對(duì)象的run()方法遵绰。

參考信息

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末增淹,一起剝皮案震驚了整個(gè)濱河市椿访,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌虑润,老刑警劉巖成玫,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡哭当,警方通過(guò)查閱死者的電腦和手機(jī)猪腕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)钦勘,“玉大人陋葡,你說(shuō)我怎么就攤上這事〕共桑” “怎么了腐缤?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)肛响。 經(jīng)常有香客問(wèn)我岭粤,道長(zhǎng),這世上最難降的妖魔是什么特笋? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任剃浇,我火速辦了婚禮,結(jié)果婚禮上猎物,老公的妹妹穿的比我還像新娘虎囚。我一直安慰自己,他們只是感情好霸奕,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著吉拳,像睡著了一般质帅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上留攒,一...
    開(kāi)封第一講書(shū)人閱讀 49,185評(píng)論 1 284
  • 那天煤惩,我揣著相機(jī)與錄音,去河邊找鬼炼邀。 笑死魄揉,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的拭宁。 我是一名探鬼主播洛退,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼杰标!你這毒婦竟也來(lái)了兵怯?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤腔剂,失蹤者是張志新(化名)和其女友劉穎媒区,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡袜漩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年绪爸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宙攻。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡奠货,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出粘优,到底是詐尸還是另有隱情仇味,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布雹顺,位于F島的核電站丹墨,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏嬉愧。R本人自食惡果不足惜贩挣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望没酣。 院中可真熱鬧王财,春花似錦、人聲如沸裕便。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)偿衰。三九已至挂疆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間下翎,已是汗流浹背缤言。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留视事,地道東北人胆萧。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像俐东,于是被迫代替她去往敵國(guó)和親跌穗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

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

  • 孩子不管大小虏辫,都會(huì)有些自己的小心思小想法耍點(diǎn)小心眼的瞻离。 比如三年級(jí)的孩子,老師要求背一篇課文乒裆,逐段...
    寒夜中的點(diǎn)點(diǎn)星光閱讀 514評(píng)論 0 0
  • 放慢自己的腳步套利,一點(diǎn)一點(diǎn)慢慢來(lái)推励。 一張圖,畫(huà)了四遍線稿肉迫。 特地验辞,慢慢地畫(huà),去感受這個(gè)過(guò)程喊衫。 速度慢下來(lái)跌造,一筆一筆地...
    七醬的手繪日常閱讀 561評(píng)論 4 10
  • 雨水反復(fù)清洗 一棵靜默的梨樹(shù) 讓那一切關(guān)于寒冷的回憶 跌進(jìn)塵埃 褐色的枝頭聚集了一些可以吶喊的力量 那是明媚 那是...
    快雪神刀閱讀 631評(píng)論 0 2
  • 霞光萬(wàn)里,皓月當(dāng)空族购。 如斯美景壳贪,何人與共? 待幾時(shí)寝杖,你我攜手歸途违施。
    JOJO是啾啾閱讀 248評(píng)論 0 0