安卓中的消息循環(huán)模型

引子

Android應(yīng)用程序是通過消息來驅(qū)動的铺遂,即在應(yīng)用程序的主線程(UI線程)中有一個消息循環(huán),負責(zé)處理消息隊列中的消息疚鲤。Android應(yīng)用程序是支持多線程的啸如,即可以創(chuàng)建子線程來執(zhí)行一些計算型的任務(wù)。

  • 這些子線程能不能像應(yīng)用程序的主線程一樣具有消息循環(huán)呢成翩?
  • 這些子線程又能不能往應(yīng)用程序的主線程中發(fā)送消息呢觅捆?

一、原理

在開發(fā)Android應(yīng)用程序中麻敌,有時候我們需要在應(yīng)用程序中創(chuàng)建一些常駐的子線程來不定期地執(zhí)行一些不需要與應(yīng)用程序界面交互的計算型的任務(wù)栅炒。如果這些子線程具有消息循環(huán),那么它們就能夠常駐在應(yīng)用程序中不定期的執(zhí)行一些計算型任務(wù)了:當(dāng)我們需要用這些子線程來執(zhí)行任務(wù)時,就往這個子線程的消息隊列中發(fā)送一個消息赢赊,然后就可以在子線程的消息循環(huán)中執(zhí)行我們的計算型任務(wù)了乙漓。

但是,Android應(yīng)用程序的子線程是不可以操作主線程的UI的域携,那么簇秒,這個子線程應(yīng)該如何在應(yīng)用程序界面上顯示信息呢?如果我們能夠在子線程中往主線程的消息隊列中發(fā)送消息秀鞭,那么問題就迎刃而解了趋观,因為發(fā)往主線程消息隊列的消息最終是由主線程來處理的,在處理這個消息的時候锋边,我們就可以在應(yīng)用程序界面上顯示信息了皱坛。

二、消息循環(huán)模型

2.1 應(yīng)用程序主線程消息循環(huán)模型

當(dāng)運行在Android應(yīng)用程序框架層中的ActivityManagerService決定要為當(dāng)前啟動的應(yīng)用程序創(chuàng)建一個主線程的時候豆巨,它會在ActivityManagerService中的startProcessLocked成員函數(shù)調(diào)用Process類的靜態(tài)成員函數(shù)start為當(dāng)前應(yīng)用程序創(chuàng)建一個主線程:

public final class ActivityManagerService extends ActivityManagerNative    
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {    
    
    ......    
    
    private final void startProcessLocked(ProcessRecord app,    
                String hostingType, String hostingNameStr) {    
    
        ......    
    
        try {    
            int uid = app.info.uid;    
            int[] gids = null;    
            try {    
                gids = mContext.getPackageManager().getPackageGids(    
                    app.info.packageName);    
            } catch (PackageManager.NameNotFoundException e) {    
                ......    
            }    
                
            ......    
    
            int debugFlags = 0;    
                
            ......    
                
            int pid = Process.start("android.app.ActivityThread",    
                mSimpleProcessManagement ? app.processName : null, uid, uid,    
                gids, debugFlags, null);    
                
            ......    
    
        } catch (RuntimeException e) {    
                
            ......    
    
        }    
    }    
    
    ......    
    
}    

Process.start函數(shù)的第一個參數(shù)“android.app.ActivityThread”剩辟,它表示要在當(dāng)前新建的線程中加載android.app.ActivityThread類,并且調(diào)用這個類的靜態(tài)成員函數(shù)main作為應(yīng)用程序的入口點往扔。ActivityThread類定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:

public final class ActivityThread {  
    ......  
  
    public static final void main(String[] args) {  
        ......
  
        Looper.prepareMainLooper();  
         
        ......  
  
        ActivityThread thread = new ActivityThread();  
        thread.attach(false);  
  
        ...... 
        Looper.loop();  
  
        ...... 
  
        thread.detach();  
        ......  
    }  
  
    ......  
}  

在這個main函數(shù)里面贩猎,除了創(chuàng)建一個ActivityThread實例外,就是在進行消息循環(huán)了萍膛。

在進行消息循環(huán)之前吭服,首先會通過Looper類的靜態(tài)成員函數(shù)prepareMainLooper為當(dāng)前線程準(zhǔn)備一個消息循環(huán)對象。Looper類定義在frameworks/base/core/java/android/os/Looper.java文件中:

public class Looper {
    ......
 
    private static final ThreadLocal sThreadLocal = new ThreadLocal();
 
    final MessageQueue mQueue;
 
    ......
 
    /** Initialize the current thread as a looper.
    * This gives you a chance to create handlers that then reference
    * this looper, before actually starting the loop. Be sure to call
    * {@link #loop()} after calling this method, and end it by calling
    * {@link #quit()}.
    */
    public static final void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }
 
    /** Initialize the current thread as a looper, marking it as an application's main 
    *  looper. The main looper for your application is created by the Android environment,
    *  so you should never need to call this function yourself.
    * {@link #prepare()}
    */
 
    public static final void prepareMainLooper() {
        prepare();
        setMainLooper(myLooper());
        if (Process.supportsProcesses()) {
            myLooper().mQueue.mQuitAllowed = false;
        }
    }
 
    private synchronized static void setMainLooper(Looper looper) {
        mMainLooper = looper;
    }
 
    /**
    * Return the Looper object associated with the current thread.  Returns
    * null if the calling thread is not associated with a Looper.
    */
    public static final Looper myLooper() {
        return (Looper)sThreadLocal.get();
    }
 
    private Looper() {
        mQueue = new MessageQueue();
        mRun = true;
        mThread = Thread.currentThread();
    }
 
    ......
}

Looper類的靜態(tài)成員函數(shù)prepareMainLooper是專門應(yīng)用程序的主線程調(diào)用的蝗罗,應(yīng)用程序的其它子線程都不應(yīng)該調(diào)用這個函數(shù)來在本線程中創(chuàng)建消息循環(huán)對象艇棕,而應(yīng)該調(diào)用prepare函數(shù)來在本線程中創(chuàng)建消息循環(huán)對象。

函數(shù)prepareMainLooper做的事情其實就是在線程中創(chuàng)建一個Looper對象串塑,這個Looper對象是存放在sThreadLocal成員變量里面的沼琉,成員變量sThreadLocal的類型為ThreadLocal,表示這是一個線程局部變量桩匪,即保證每一個調(diào)用了prepareMainLooper函數(shù)的線程里面都有一個獨立的Looper對象打瘪。在線程是創(chuàng)建Looper對象的工作是由prepare函數(shù)來完成的,而在創(chuàng)建Looper對象的時候吸祟,會同時創(chuàng)建一個消息隊列MessageQueue瑟慈,保存在Looper的成員變量mQueue中,后續(xù)消息就是存放在這個隊列中去屋匕。MessageQueue的具體定義和實現(xiàn)是通過JNI方法完成的济锄。

為應(yīng)用程序的主線程專門準(zhǔn)備一個創(chuàng)建消息循環(huán)對象的函數(shù)捻浦,是為了讓其它地方能夠方便地通過Looper類的getMainLooper函數(shù)來獲得應(yīng)用程序主線程中的消息循環(huán)對象。獲得應(yīng)用程序主線程中的消息循環(huán)對象是為了能夠向應(yīng)用程序主線程發(fā)送消息了。

消息循環(huán)對象創(chuàng)建好之后,回到ActivityThread類的main函數(shù)中,接下來,就是要進入消息循環(huán)了:

 Looper.loop(); 

2.2 應(yīng)用程序子線程消息循環(huán)模型

在Java框架中,如果我們想在當(dāng)前應(yīng)用程序中創(chuàng)建一個子線程绞惦,一般就是通過自己實現(xiàn)一個類,這個類繼承于Thread類洋措,然后重載Thread類的run函數(shù)济蝉,把我們想要在這個子線程執(zhí)行的任務(wù)都放在這個run函數(shù)里面實現(xiàn)。最后實例這個自定義的類菠发,并且調(diào)用它的start函數(shù)王滤,這樣一個子線程就創(chuàng)建好了,并且會調(diào)用這個自定義類的run函數(shù)滓鸠。但是當(dāng)這個run函數(shù)執(zhí)行完成后雁乡,子線程也就結(jié)束了,它沒有消息循環(huán)的概念糜俗。

不過有時候我們需要在應(yīng)用程序中創(chuàng)建一些常駐的子線程來不定期地執(zhí)行一些計算型任務(wù)踱稍,這時候就可以考慮使用Android系統(tǒng)提供的HandlerThread類了,它具有創(chuàng)建具有消息循環(huán)功能的子線程的作用悠抹。

HandlerThread類實現(xiàn)在frameworks/base/core/java/android/os/HandlerThread.java文件中珠月。

public class HandlerThread extends Thread {
    ......
    private Looper mLooper;
 
    public HandlerThread(String name) {
        super(name);
        ......
    }
 
    ......
 
    public void run() {
        ......
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            ......
        }
        ......
        Looper.loop();
        ......
    }
 
    public Looper getLooper() {
        ......
        return mLooper;
    }
 
    ......
}

HandlerThread類繼承了Thread類,因此楔敌,通過它可以在應(yīng)用程序中創(chuàng)建一個子線程桥温。在它的run函數(shù)中,會進入一個消息循環(huán)中梁丘,因此,這個子線程可以常駐在應(yīng)用程序中旺韭,直到它接收收到一個退出消息為止氛谜。

在run函數(shù)中,首先是調(diào)用Looper類的靜態(tài)成員函數(shù)prepare來準(zhǔn)備一個消息循環(huán)對象:

Looper.prepare();

然后通過Looper類的myLooper成員函數(shù)將這個子線程中的消息循環(huán)對象保存在HandlerThread類中的成員變量mLooper中:

mLooper = Looper.myLooper();

這樣区端,其它地方就可以方便地通過它的getLooper函數(shù)來獲得這個消息循環(huán)對象了值漫,有了這個消息循環(huán)對象后,就可以往這個子線程的消息隊列中發(fā)送消息织盼,通知這個子線程執(zhí)行特定的任務(wù)了杨何。

最在這個run函數(shù)通過Looper類的loop函數(shù)進入消息循環(huán)中:

Looper.loop();

這樣,一個具有消息循環(huán)的應(yīng)用程序子線程就準(zhǔn)備就緒了沥邻。

三危虱、應(yīng)用程序消息處理機制(Looper、Handler)

Android應(yīng)用程序通過消息來驅(qū)動唐全,系統(tǒng)為每一個應(yīng)用程序維護一個消息隊例埃跷,應(yīng)用程序的主線程不斷地從這個消息隊例中獲取消息(Looper)蕊玷,然后對這些消息進行處理(Handler),這樣就實現(xiàn)了通過消息來驅(qū)動應(yīng)用程序的執(zhí)行弥雹。

3.1 消息循環(huán)

在消息處理機制中垃帅,消息都是存放在一個消息隊列中去,而應(yīng)用程序的主線程就是圍繞這個消息隊列進入一個無限循環(huán)的剪勿,直到應(yīng)用程序退出贸诚。如果隊列中有消息,應(yīng)用程序的主線程就會把它取出來厕吉,并分發(fā)給相應(yīng)的Handler進行處理酱固;如果隊列中沒有消息,應(yīng)用程序的主線程就會進入空閑等待狀態(tài)赴涵,等待下一個消息的到來媒怯。在Android應(yīng)用程序中,這個消息循環(huán)過程是由Looper類來實現(xiàn)的髓窜,它定義在frameworks/base/core/java/android/os/Looper.java文件中扇苞,上文已經(jīng)介紹過。

在上面這些工作都準(zhǔn)備好之后寄纵,就調(diào)用Looper類的loop函數(shù)進入到消息循環(huán)中去了:

public class Looper {
    ......
 
    public static final void loop() {
        Looper me = myLooper();
        MessageQueue queue = me.mQueue;
 
        ......
 
        while (true) {
            Message msg = queue.next(); // might block
            ......
 
            if (msg != null) {
                if (msg.target == null) {
                    // No target is a magic identifier for the quit message.
                    return;
                }
 
                ......
 
                msg.target.dispatchMessage(msg);
                
                ......
 
                msg.recycle();
            }
        }
    }
 
    ......
}

這里就是進入到消息循環(huán)中去了鳖敷,它不斷地從消息隊列mQueue中去獲取下一個要處理的消息msg,如果消息的target成員變量為null程拭,就表示要退出消息循環(huán)了定踱,否則的話就要調(diào)用這個target對象的dispatchMessage成員函數(shù)來處理這個消息,這個target對象的類型為Handler恃鞋,下面我們分析消息的發(fā)送時會看到這個消息對象msg是如設(shè)置的崖媚。

這個函數(shù)最關(guān)鍵的地方便是從消息隊列中獲取下一個要處理的消息了,即MessageQueue.next函數(shù)恤浪,它實現(xiàn)在frameworks/base/core/java/android/os/MessageQueue.java文件中:

public class MessageQueue {
    ......
 
    final Message next() {
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
 
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            nativePollOnce(mPtr, nextPollTimeoutMillis);
 
            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                final Message msg = mMessages;
                if (msg != null) {
                    final long when = msg.when;
                    if (now >= when) {
                        mBlocked = false;
                        mMessages = msg.next;
                        msg.next = null;
                        if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg);
                        return msg;
                    } else {
                        nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
                    }
                } else {
                    nextPollTimeoutMillis = -1;
                }
 
                // If first time, then get the number of idlers to run.
                if (pendingIdleHandlerCount < 0) {
                    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("MessageQueue", "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;
        }
    }
 
    ......
}

調(diào)用這個函數(shù)的時候畅哑,有可能會讓線程進入等待狀態(tài)。什么情況下水由,線程會進入等待狀態(tài)呢荠呐?兩種情況:

  • 一是當(dāng)消息隊列中沒有消息時,它會使線程進入等待狀態(tài)砂客;
  • 二是消息隊列中有消息泥张,但是消息指定了執(zhí)行的時間,而現(xiàn)在還沒有到這個時間鞠值,線程也會進入等待狀態(tài)媚创。

消息隊列中的消息是按時間先后來排序的,后面我們在分析消息的發(fā)送時會看到彤恶。

這里的等待筝野,是空閑等待晌姚,而不是忙等待,在進入空閑等待狀態(tài)前歇竟,如果應(yīng)用程序注冊了IdleHandler接口來處理一些事情挥唠,那么就會先執(zhí)行這里IdleHandler,然后再進入等待狀態(tài)焕议。IdlerHandler是定義在MessageQueue的一個內(nèi)部類:

public class MessageQueue {
    ......
 
    /**
    * Callback interface for discovering when a thread is going to block
    * waiting for more messages.
    */
    public static interface IdleHandler {
        /**
        * Called when the message queue has run out of messages and will now
        * wait for more.  Return true to keep your idle handler active, false
        * to have it removed.  This may be called if there are still messages
        * pending in the queue, but they are all scheduled to be dispatched
        * after the current time.
        */
        boolean queueIdle();
    }
 
    ......
}

它只有一個成員函數(shù)queueIdle宝磨,執(zhí)行這個函數(shù)時,如果返回值為false盅安,那么就會從應(yīng)用程序中移除這個IdleHandler唤锉,否則的話就會在應(yīng)用程序中繼續(xù)維護著這個IdleHandler,下次空閑時仍會再執(zhí)會這個IdleHandler别瞭。MessageQueue提供了addIdleHandler和removeIdleHandler兩注冊和刪除IdleHandler窿祥。

3.2 消息的發(fā)送

應(yīng)用程序的主線程準(zhǔn)備好消息隊列并且進入到消息循環(huán)后,其它地方就可以往這個消息隊列中發(fā)送消息了蝙寨。

代碼中需要將消息封裝成Message對象msg晒衩,然后通過Handler類的基類實現(xiàn)的sendMessage函數(shù)把這個消息對象msg加入到應(yīng)用程序的消息隊列中去。自定義的基類繼承于Handler類墙歪,安卓源碼中的樣例如下:

public final class ActivityThread {  
  
    ......  
  
    private final class H extends Handler {  
  
        ......  
  
        public void handleMessage(Message msg) {  
            ......  
            switch (msg.what) {    
            ......  
            }  
  
        ......  
  
    }  
  
    ......  
} 

這個H類就是通過其成員函數(shù)handleMessage函數(shù)來處理消息的听系。H類繼承于Handler類,當(dāng)創(chuàng)建這個H對象時虹菲,會調(diào)用Handler類的構(gòu)造函數(shù)靠胜,這個函數(shù)定義在frameworks/base/core/java/android/os/Handler.java文件中:

public class Handler {
    ......
 
    public Handler() {
        ......
 
        mLooper = Looper.myLooper();
        ......
 
        mQueue = mLooper.mQueue;
        ......
    }
 
 
    final MessageQueue mQueue;
    final Looper mLooper;
    ......
}

在Hanlder類的構(gòu)造函數(shù)中,主要就是初始成員變量mLooper和mQueue了毕源。這里的myLooper是Looper類的靜態(tài)成員函數(shù)浪漠,通過它來獲得一個Looper對象,這個Looper對象就是前面我們在分析消息循環(huán)時霎褐,在ActivityThread類的main函數(shù)中通過Looper.prepareMainLooper函數(shù)創(chuàng)建的郑藏。Looper.myLooper函數(shù)實現(xiàn)在frameworks/base/core/java/android/os/Looper.java文件中:

public class Looper {
    ......
 
    public static final Looper myLooper() {
        return (Looper)sThreadLocal.get();
    }
 
    ......
}

有了這個Looper對象后,就可以通過Looper.mQueue來訪問應(yīng)用程序的消息隊列了瘩欺。

有了這個Handler對象H后,就可以通過它來往應(yīng)用程序的消息隊列中加入新的消息了拌牲。就開始調(diào)用H.sendMessage函數(shù)來發(fā)送消息了俱饿,這個函數(shù)定義在frameworks/base/core/java/android/os/Handler.java文件中:

public class Handler {
    ......
 
    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);
    }
 
    public boolean sendMessageAtTime(Message msg, long uptimeMillis)
    {
        boolean sent = false;
        MessageQueue queue = mQueue;
        if (queue != null) {
            msg.target = this;
            sent = queue.enqueueMessage(msg, uptimeMillis);
        }
        else {
            ......
        }
        return sent;
    }
 
    ......
}

在發(fā)送消息時,是可以指定消息的處理時間的塌忽,但是通過sendMessage函數(shù)發(fā)送的消息的處理時間默認就為當(dāng)前時間拍埠,即表示要馬上處理,因此土居,從sendMessage函數(shù)中調(diào)用sendMessageDelayed函數(shù)枣购,傳入的時間參數(shù)為0嬉探,表示這個消息不要延時處理棉圈,而在sendMessageDelayed函數(shù)中涩堤,則會先獲得當(dāng)前時間,然后加上消息要延時處理的時間分瘾,即得到這個處理這個消息的絕對時間胎围,然后調(diào)用sendMessageAtTime函數(shù)來把消息加入到應(yīng)用程序的消息隊列中去。

在sendMessageAtTime函數(shù)德召,首先得到應(yīng)用程序的消息隊列mQueue白魂,這是在Handler對象構(gòu)造時初始化好的,前面已經(jīng)分析過了上岗,接著設(shè)置這個消息的目標(biāo)對象target福荸,即這個消息最終是由誰來處理的:

msg.target = this;

這里將它賦值為this,即表示這個消息最終由這個Handler對象來處理肴掷,設(shè)置為別的對象敬锐,則會控制其他進程實現(xiàn)。

函數(shù)最后調(diào)用queue.enqueueMessage來把這個消息加入到應(yīng)用程序的消息隊列中去捆等,這個函數(shù)實現(xiàn)在frameworks/base/core/java/android/os/MessageQueue.java文件中:

public class MessageQueue {
    ......
 
    final boolean enqueueMessage(Message msg, long when) {
        ......
 
        final boolean needWake;
        synchronized (this) {
            ......
 
            msg.when = when;
            //Log.d("MessageQueue", "Enqueing: " + msg);
            Message p = mMessages;
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked; // new head, might need to wake up
            } else {
                Message prev = null;
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
                msg.next = prev.next;
                prev.next = msg;
                needWake = false; // still waiting on head, no need to wake up
            }
 
        }
        if (needWake) {
            nativeWake(mPtr);
        }
        return true;
    }
 
    ......
}

把消息加入到消息隊列時滞造,分兩種情況,一種當(dāng)前消息隊列為空時栋烤,這時候應(yīng)用程序的主線程一般就是處于空閑等待狀態(tài)了谒养,這時候就要喚醒它,另一種情況是應(yīng)用程序的消息隊列不為空明郭,這時候就不需要喚醒應(yīng)用程序的主線程了买窟,因為這時候它一定是在忙著處于消息隊列中的消息,因此不會處于空閑等待的狀態(tài)薯定。

第一種情況比較簡單始绍,只要把消息放在消息隊列頭就可以了:

msg.next = p;
mMessages = msg;
needWake = mBlocked; // new head, might need to wake up

第二種情況相對就比較復(fù)雜一些了,前面我們說過话侄,當(dāng)往消息隊列中發(fā)送消息時亏推,是可以指定消息的處理時間的,而消息隊列中的消息年堆,就是按照這個時間從小到大來排序的吞杭,因此,當(dāng)把新的消息加入到消息隊列時变丧,就要根據(jù)它的處理時間來找到合適的位置芽狗,然后再放進消息隊列中去:

Message prev = null;
while (p != null && p.when <= when) {
    prev = p;
    p = p.next;
}
msg.next = prev.next;
prev.next = msg;
needWake = false; // still waiting on head, no need to wake up

把消息加入到消息隊列去后,如果應(yīng)用程序的主線程正處于空閑等待狀態(tài)痒蓬,就需要調(diào)用natvieWake函數(shù)來喚醒它了童擎,這是一個JNI方法滴劲。

3.3 消息的處理

在Looper類的loop成員函數(shù)中進行消息循環(huán)過程, 它從消息隊列中獲得消息對象msg后顾复,就會調(diào)用它的target成員變量的dispatchMessage函數(shù)來處理這個消息班挖。

在前面分析消息的發(fā)送時說過,這個消息對象msg的成員變量target是在發(fā)送消息的時候設(shè)置好的捕透,一般就通過哪個Handler來發(fā)送消息聪姿,就通過哪個Handler來處理消息。

dispatchMessage函數(shù)定義在frameworks/base/core/java/android/os/ Handler.java文件中:

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

這里的消息對象msg的callback成員變量和Handler類的mCallBack成員變量一般都為null乙嘀,于是末购,就會調(diào)用Handler類的handleMessage函數(shù)來處理這個消息,由于子類在繼承Handler類時虎谢,一般會重寫handleMessage函數(shù)盟榴,因此,這里調(diào)用的實際上是子類的handleMessage函數(shù)婴噩。例:

public final class ActivityThread {  
  
    ......  
  
    private final class H extends Handler {  
  
        ......  
  
        public void handleMessage(Message msg) {  
            ......  
            switch (msg.what) {  
            case LAUNCH_ACTIVITY: {  
                ActivityClientRecord r = (ActivityClientRecord)msg.obj;  
  
                r.packageInfo = getPackageInfoNoCheck(  
                    r.activityInfo.applicationInfo);  
                handleLaunchActivity(r, null);  
            } break;  
            ......  
            }  
  
        ......  
  
    }  
  
    ......  
}  

四擎场、總結(jié)

整個Handler的處理模型可以用下圖來說明:

Handler處理模型

而安卓中的多線程消息模型如下表示:

安卓消息機制

線程模型的序列圖如下:

初始化階段
執(zhí)行階段

參考文章:

老羅博客

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市几莽,隨后出現(xiàn)的幾起案子迅办,更是在濱河造成了極大的恐慌,老刑警劉巖章蚣,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件站欺,死亡現(xiàn)場離奇詭異,居然都是意外死亡纤垂,警方通過查閱死者的電腦和手機矾策,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來峭沦,“玉大人贾虽,你說我怎么就攤上這事『鹩悖” “怎么了蓬豁?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長菇肃。 經(jīng)常有香客問我地粪,道長,這世上最難降的妖魔是什么巷送? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮矛辕,結(jié)果婚禮上笑跛,老公的妹妹穿的比我還像新娘付魔。我一直安慰自己,他們只是感情好飞蹂,可當(dāng)我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布几苍。 她就那樣靜靜地躺著,像睡著了一般陈哑。 火紅的嫁衣襯著肌膚如雪妻坝。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天惊窖,我揣著相機與錄音刽宪,去河邊找鬼。 笑死界酒,一個胖子當(dāng)著我的面吹牛圣拄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播毁欣,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼庇谆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了凭疮?” 一聲冷哼從身側(cè)響起饭耳,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎执解,沒想到半個月后寞肖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡材鹦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年逝淹,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片桶唐。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡栅葡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出尤泽,到底是詐尸還是另有隱情欣簇,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布坯约,位于F島的核電站熊咽,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏闹丐。R本人自食惡果不足惜横殴,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧衫仑,春花似錦梨与、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至瞄崇,卻和暖如春呻粹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背苏研。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工等浊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人楣富。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓凿掂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親纹蝴。 傳聞我的和親對象是個殘疾皇子庄萎,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,675評論 2 359

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