Android更新Ui進(jìn)階精解(二)

《代碼里的世界》

用文字札記描繪自己 android學(xué)習(xí)之路

轉(zhuǎn)載請保留出處 by Qiao
http://blog.csdn.net/qiaoidea/article/details/45115047

【導(dǎo)航】
Android更新Ui的幾種方法和見解 android更新ui基本常用方法
Android更新Ui進(jìn)階精解(一) android ui線程檢查機(jī)制
Android更新Ui進(jìn)階精解(二) android 線程更新UI機(jī)制


1.回顧

第一篇講了對Ui線程更新的方法和見解,然后接著講了線程檢查機(jī)制瓶珊,這里來詳細(xì)分析下更新Ui的核心——Android中消息系統(tǒng)模型啸箫。當(dāng)然,這里要講的其實也已經(jīng)不再簡簡單單地是更新Ui的范疇了伞芹。不過還是很值得學(xué)習(xí)和分析一下忘苛。另外驹尼,其實網(wǎng)上關(guān)于這方面的講解也有很多了园匹,本篇也是綜合整理并用自己的理解加以描述和概括悬秉。同時也感謝有更高造詣的大大能予以批評指正痴奏。

提煉

Android中的消息機(jī)制主要有如下幾個要點,這里也著重圍繞這些內(nèi)容來講解:
  
1. Handler 調(diào)度消息和runnable對象在不同線程中執(zhí)行相應(yīng)動作胸遇。
2. Looper消息泵荧呐,用來為一個線程開啟一個消息循環(huán)
3. MessageQueue 遵循FIFO先進(jìn)先出規(guī)則的消息隊列,以鏈表形式存取Message,供looper提取

(為了深入了解纸镊,已從源碼從提取這幾個類
  Handler/Looper/MessageQueue/Message .java 方便新手下載查看)


2.分析

為了方便分析倍阐,借用一下找到的模型圖綜合看一下:
  

消息系統(tǒng)模型
消息系統(tǒng)模型

  首先在一個線程中初始化一個looper并prepare(準(zhǔn)備),然后創(chuàng)建該looper對象的處理對象Handler逗威,接著當(dāng)需要交互變更時峰搪,可以在其他線程(或自身線程)中使用handler發(fā)消息至該消息隊列MessageQueue,最后looper會自動有序抽取消息(沒有消息則掛起)凯旭,交給handler執(zhí)行消息處理邏輯概耻。
  Orz,我的概念描述還是一塌糊涂,還是轉(zhuǎn)代碼說明吧:
比如我們有個線程專門負(fù)責(zé)一類處理邏輯,并且只允許該線程來處理這類邏輯,那么我們怎么做到呢罐呼?

  1. 在一個線程里邊定義一個Looper
    Looper.prepare(); //稍微有點兒多鞠柄,詳細(xì)見下文

2.定義一個處理消息的Handler

    handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //處理邏輯
            }
    };

3.啟動looper,并開始工作嫉柴,輪詢消息

    Looper.loop(); //詳細(xì)見下文
    //要停止的話厌杜,使用Looper.quit();

4.在其他線程將要處理的數(shù)據(jù)data或回調(diào)對象callback以消息Message模式通過Handler發(fā)至該消息隊列MessageQueue

    handler.sendMessage(msg)

5.Looper的loop()方法會從消息隊列中取到該消息并喚醒處理邏輯

    //即loop()方法中的代碼
    for (;;) { //顯然這個死循環(huán)一直在等待消息到來并處理
            Message msg = queue.next(); // 取一條消息
            if (msg == null) {
                return;
            }
            msg.target.dispatchMessage(msg); //調(diào)用消息綁定的handler執(zhí)行處理邏輯
            //other code....
    }

6.handler跳轉(zhuǎn)到執(zhí)行處理邏輯的過程

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) { //如果有回調(diào),則調(diào)用
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    
    //調(diào)用回調(diào)方法
     private static void handleCallback(Message message) {
        message.callback.run();
    }

以上便是整個消息系統(tǒng)的過程差凹,后邊我們會逐個分析精講期奔。


3.回到我們更新UI講解

在ActivityManagerService為Android應(yīng)用程序創(chuàng)建新的進(jìn)程并啟動activity時候侧馅,主線程ActivityThread首先被創(chuàng)建危尿。該進(jìn)程 Process.java@start("android.app.ActivityThread",...)會加載執(zhí)行ActivityThread的靜態(tài)成員函數(shù)main,打開該方法:

    public static void main(String[] args) {  
    //other code.. 我們只看有用的部分,其他暫略過
    
    Looper.prepareMainLooper();  //準(zhǔn)備looper,注馁痴,綁定的為當(dāng)前主線程
    
    ActivityThread thread = new ActivityThread();//開啟一個新ActivityThread線程
    thread.attach(false);//最后執(zhí)行到activity
    //other code..
  
    Looper.loop();  //啟動looper

這個靜態(tài)函數(shù)做了兩件事情谊娇,一是在主線程中創(chuàng)建了一個ActivityThread實例,二是通過Looper類使主線程進(jìn)入消息循環(huán)罗晕。
  然后济欢,代碼經(jīng)過一系列邏輯( ActivityThread.attach->IActivityManager. attachApplication -> attachApplicationApplicationThread.scheduleLaunchActivity ->... ->ActivityThread.performLaunchActivity ),最終會調(diào)用activity的attach方法小渊。
  我們打開activity類法褥。可以看到酬屉,它定義了uiThread和Handler參數(shù)

    ActivityThread mMainThread;//對應(yīng)上邊的ActivityThread線程 
    
    private Thread mUiThread;//主Ui線程
    final Handler mHandler = new Handler();//這個handler就是activity用來處理Ui的了半等。我們自己定義的handler其實等于重新定義了這個mHandler揍愁;

我們來看activity的attach方法:

final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, IVoiceInteractor voiceInteractor) {
     
        mUiThread = Thread.currentThread();//當(dāng)前主線程Ui線程
        mMainThread = aThread;   //對應(yīng)上邊的ActivityThread線程   
}

所以,當(dāng)我們要更新UI的時候杀饵,都會用到sendMessage,比如使用runOnUiThread莽囤,來看下這個方法

public final void runOnUiThread(Runnable action) {
        /**
        *如果當(dāng)前線程不為Ui主線程則使用定義好的mHandler
        */
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
}

打開post方法:

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

還是熟悉的配方,還是熟悉的味道切距。朽缎。封裝成消息,然后發(fā)送出去谜悟。好话肖,我們再回頭看看最初第一篇文章里的四種方法:
  1.new 一個handler來 sendMessage();
  2.利用new handler來post
  不過是把上邊已經(jīng)定義好Activity的mHandler重新定義了一遍,然后封裝成消息發(fā)送出去赌躺;
  3.runOnUiThread
  同樣不過是用了Activity的mHandler發(fā)送消息狼牺;
  4.view.post
  稍微看一下代碼:

    public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }
        // Assume that post will succeed later
        ViewRootImpl.getRunQueue().post(action);
        return true;
    }

對于AttachInfo應(yīng)該不算陌生吧,附加view到Activity的時候會把a(bǔ)ctivity的一些屬性附加給AttachInfo礼患,這里同樣調(diào)用取得mHandler然后再post是钥。。繞了一圈又回來了缅叠。
  
  綜上看來悄泥,整個UI更新機(jī)制其實就是Android消息系統(tǒng)模型的一個簡單實現(xiàn)。至此肤粱,我們的更新UI部分也算講完了弹囚,那么作為補(bǔ)充部分,還是從源碼上完整細(xì)致的過一下整個消息系統(tǒng)模型吧领曼。


4.精解

這里通過剖析源碼來理解各部分的具體實現(xiàn)鸥鹉,再結(jié)合前面講的內(nèi)容加以融會貫通,便于深入理解最終達(dá)到在不同場景的熟練使用庶骄。
  我們將按照順序來逐個查看毁渗。
  首先說說消息對象,畢竟其他類操作的最基本元素也都是它单刁。

4.1 Message

public final class Message implements Parcelable {
    //繼承Parcelable 用于數(shù)據(jù)傳遞

    /**幾種數(shù)據(jù)類型**/
    public int arg1; 
    public int arg2; 
    public Object obj;
    Bundle data;
    
    public int what;//供handler處理的消息識別標(biāo)識身份
    long when;//什么時候處理該消息

    Handler target;//處理該消息的目標(biāo)handler
    Runnable callback;  //回調(diào)方法

    int flags;//標(biāo)簽標(biāo)識
    static final int FLAG_IN_USE = 1 << 0;//是否可用(回收利用)
    static final int FLAG_ASYNCHRONOUS = 1 << 1;
    static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
    
    public Messenger replyTo;//可選對象灸异,可以用來記錄發(fā)送方或接收者

    Message next;//這條消息的下一條消息

    /**
    *開一個消息池,便于循環(huán)利用消息羔飞,避免生成新對象并分配內(nèi)存
    */
    private static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;
    private static final int MAX_POOL_SIZE = 50;
}

既然提到消息池又在前面講了利用obtain()節(jié)省內(nèi)存資源肺樟,那么我們就看下這個obtain()

    /**
     *從消息池中返回一個新的消息實例,避免我們通常情況下分配新對象逻淌。
     */
     public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

然后就是基于此方法的一系列運用:先調(diào)用obtain()方法么伯,然后把獲取的Message實例的 各種參數(shù)賦值傳參。

    //取一個消息對象卡儒,把已存在的消息內(nèi)容賦值過去
    public static Message obtain(Message orig) {
        Message m = obtain();
        m.what = orig.what;
        m.arg1 = orig.arg1;
        m.arg2 = orig.arg2;
        m.obj = orig.obj;
        m.replyTo = orig.replyTo;
        if (orig.data != null) {
            m.data = new Bundle(orig.data);
        }
        m.target = orig.target;
        m.callback = orig.callback;

        return m;
    }
    
    public static Message obtain(Handler h, int what, 
            int arg1, int arg2, Object obj) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.arg1 = arg1;
        m.arg2 = arg2;
        m.obj = obj;

        return m;
    }
    //調(diào)用obtain并賦值田柔,不再一一列出
    public static Message obtain(Handler h) {//..}
    public static Message obtain(Handler h, Runnable callback) {//..}
    public static Message obtain(Handler h, int what) {//...}
    public static Message obtain(Handler h, int what, Object obj) {//...}
    public static Message obtain(Handler h, int what, int arg1, int arg2) {//...}

然后就是回收釋放recycle,它返回一個消息池實例誓篱。釋放之后將不能再使用此方法。

    public void recycle() {
        clearForRecycle();

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }
    
    //清空所有數(shù)據(jù)
    void clearForRecycle() {
        flags = 0;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        when = 0;
        target = null;
        callback = null;
        data = null;
    }

    //拷貝消息內(nèi)容
    public void copyFrom(Message o) {
        this.flags = o.flags & ~FLAGS_TO_CLEAR_ON_COPY_FROM;
        this.what = o.what;
        this.arg1 = o.arg1;
        this.arg2 = o.arg2;
        this.obj = o.obj;
        this.replyTo = o.replyTo;

        if (o.data != null) {
            this.data = (Bundle) o.data.clone();
        } else {
            this.data = null;
        }
    }

后邊就是get和set方法以及Parcelable 的讀寫凯楔。

4.2 Looper

先看他所定義的屬性:

public class Looper {
    private static final String TAG = "Looper";

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    
    private static Looper sMainLooper;//唯一對應(yīng)一個主線程的looper靜態(tài)實例
    final MessageQueue mQueue;//消息隊列
    final Thread mThread; //當(dāng)前綁定線程
    volatile boolean mRun; //是否允許退出

    private Printer mLogging;//日志打印

    //....各種方法體....
}

通常操作系統(tǒng)都為線程提供了內(nèi)部存儲空間窜骄,一個線程對應(yīng)一個內(nèi)存空間,因此這里很方便的為一個線程定義唯一對應(yīng)的looper實例:ThreadLocal< Looper > 這個有點類似C中申請內(nèi)存大小 malloc(sizeof Looper)摆屯,或者我們可以簡單理解為只作用于當(dāng)前線程的new* Looper.
  而sMainLooper是當(dāng)前應(yīng)用程序的主線程looper邻遏,區(qū)別是適用于主線程。
  我們再看他的構(gòu)造方法:

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

此構(gòu)造方法是私有的虐骑,即不允許在外部實例化准验,這樣通過單例模式保證外部從該線程取得looper唯一。另外它主要初始化了mQueue 廷没、mRun 和 mThread 幾個屬性糊饱,并綁定了當(dāng)前線程。找一下它調(diào)用實例化的部分:

    //重載颠黎,主要看下邊的prepare(boolean quitAllowed)方法
    public static void prepare() {
        prepare(true);
    }

    /**
    *初始化當(dāng)前線程作為Looper并存為本地變量另锋,
    *并由此來創(chuàng)建handler和處理程序
    *
    *@quitAllowed 是否允許退出(循環(huán)取消息)
    *通過調(diào)用loop()和quit()來開啟和結(jié)束循環(huán)
    */
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) { //保證一個線程唯一對應(yīng)一個Looper
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));//在線程中初始化一個實例
    }

sThreadLocal的get和set,負(fù)責(zé)在內(nèi)存中存取與線程唯一對應(yīng)的looper狭归。
  同時我們會注意到有兩個方法prepareMainLooper和getMainLooper:

    /**
    *初始化當(dāng)前線程作為Looper并作為android應(yīng)用的取消息邏輯夭坪,
    *是由當(dāng)前運行環(huán)境來創(chuàng)建,不需要手動調(diào)用
    */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {  //加鎖过椎,保證實例化唯一一個looper
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

    /** 
    * 返回當(dāng)前應(yīng)用程序主線程的looper實例
    */
    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

這部分是共應(yīng)用程序初始化的時候調(diào)用的室梅,我們一般用不到,不過也可以看出只是初始化了主線程的looper疚宇。
  好的亡鼠,基本的初始化工作也已經(jīng)完成了,來看該類中的核心部分敷待,循環(huán)取消息的loop()

    /** 
    * 啟動隊列的循環(huán)取消息操作间涵,直到調(diào)用quit()退出
    */
    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;

        // 確保當(dāng)前線程是本地進(jìn)程的唯一標(biāo)示
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        //開始循環(huán)取消息操作
        for (;;) {
            Message msg = queue.next(); //取下一條消息
            if (msg == null) {
                // 如果消息隊列沒有消息則掛起
                return;
            }

            // 打印日志部分
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
            //調(diào)用消息處理邏輯(回調(diào)和執(zhí)行handler處理)
            msg.target.dispatchMessage(msg);

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

            // 確保在處理消息邏輯時當(dāng)前線程并沒有被打斷
            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.recycle();
        }
    }

基本都有備注,不用過多解釋了讼撒。msg.target.dispatchMessage前面已經(jīng)講過浑厚,后便可能會在拉出來遛遛股耽。
  其他再就是基本的get和打印和異常捕獲相關(guān)的了根盒,有興趣的可以自己去看一下。

4.3 MessageQueue

類的描述簡要翻譯一下:

MessageQueue是一個低級類物蝙,負(fù)責(zé)維護(hù)一個需要被Looper派發(fā)處理的消息列表炎滞。其消息對象是通過hanlder綁定到looper上的,而不是直接添加到消息隊列中去的诬乞。

其實即MessageQueue只是一個消息隊列册赛,提供給handler加入和Looper取出消息操作钠导,這兩個接口分別是 enqueueMessage(Message msg, long when)next()
   先看屬性:

public class MessageQueue {
    // 消息隊列是否可以退出
    private final boolean mQuitAllowed;

    Message mMessages;//message實例(類似鏈表)
    //存放 IHandler的list和數(shù)組
    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
    private IdleHandler[] mPendingIdleHandlers;
    private boolean mQuiting; //Thread是否退出

    // 判斷next()是否因一個非零的超時pollOnce()而處于阻塞等待
    private boolean mBlocked;

    //標(biāo)志位森瘪,表示阻礙是否是由于消息的空target(handler)引起
    private int mNextBarrierToken;

    //native code部分略過
    @SuppressWarnings("unused")
    private int mPtr; // used by native code
    
    private native void nativeInit();
    private native void nativeDestroy();
    private native void nativePollOnce(int ptr, int timeoutMillis);
    private native void nativeWake(int ptr);

我們來了解一下MessageQueue內(nèi)部定義的IdleHanlder接口:
   這是一個提供給線程牡属,用來阻塞等待更多消息的回調(diào)接口。

 public static interface IdleHandler {
        boolean queueIdle();
    }

queueIdle()方法會在該消息隊列處理完所有消息并且不會有新消息時候調(diào)用扼睬,返回true則該閑置idlehandler保持活躍逮栅,否則(false)移除該idleHandler。當(dāng)然窗宇,如果還有消息在隊列中等待措伐,并且這些消息是在接下來的時間才被處理,那么queueIdle()也會被調(diào)用军俊。
   對于添加和移除idlehandler我們簡要略過.

 public final void addIdleHandler(IdleHandler handler) {
        if (handler == null) {
            throw new NullPointerException("Can't add a null IdleHandler");
        }
        synchronized (this) {
            mIdleHandlers.add(handler);
        }
    }
    
  public final void removeIdleHandler(IdleHandler handler) {
        synchronized (this) {
            mIdleHandlers.remove(handler);
        }
    }

取消息next()

 final Message next() {
        int pendingIdleHandlerCount = -1; //空閑的idleHandler個數(shù)
        int nextPollTimeoutMillis = 0;//下次取消息的時間

        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();//刷新等待命令
            }
            nativePollOnce(mPtr, nextPollTimeoutMillis);//更新下次取消息時間

            //取消息鎖
            synchronized (this) {
                if (mQuiting) {//線程正退出
                    return null;
                }

                // 嘗試取下一條消息并返回
                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) {
                        // 如果當(dāng)前消息并沒到指定時間侥加,則等待nextPollTimeoutMillis 后執(zhí)行取操作
                        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 (false) Log.v("MessageQueue", "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // 當(dāng)沒有消息時,下次取操作的時間間隔設(shè)置為-1
                    nextPollTimeoutMillis = -1;
                }

                // 如果是首次閑置粪躬,則獲取需要運行的空閑hanlder數(shù)量担败。閑置的hanlder只有在消息隊列為空或者當(dāng)前時間沒有消息被處理的時候等待
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // 如果沒有閑置handler等待,則消息隊列進(jìn)入阻塞等待
                    mBlocked = true;
                    continue;
                }

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

            // 運行閑置handler镰官,我們只有在首次迭代時運行下邊這段代碼
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; //釋放閑置hanlder

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf("MessageQueue", "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            //重置閑置handler數(shù)量
            pendingIdleHandlerCount = 0;

            //當(dāng)回收閑置handler時候可能有新消息被放進(jìn)來,所以更新下次取消息時間重新執(zhí)行
            nextPollTimeoutMillis = 0;
     }
}

***插入消息enqueueMessage(Message msg, long when) ***

final boolean enqueueMessage(Message msg, long when) {
        //如果消息正在被使用或者消息的處理handler為空朋魔,都會拋異常
        if (msg.isInUse()) {
            throw new AndroidRuntimeException(msg + " This message is already in use.");
        }
        if (msg.target == null) {
            throw new AndroidRuntimeException("Message must have a target.");
        }

        boolean needWake;
        synchronized (this) {
            if (mQuiting) {//如果線程已退出孙援,則拋出異常
                RuntimeException e = new RuntimeException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", e.getMessage(), e);
                return false;
            }

            msg.when = when;
            Message p = mMessages;
            if (p == null || when == 0 || when < p.when) {
                // 如果消息處理時間等于0或者小雨隊列頭的處理時間镶奉,則將該消息插至消息隊列頭
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                //將消息插入對位哨苛,通常我們不需要喚醒事件鸽凶,除非消息隊列處在阻塞狀態(tài)并且這條消息是隊列中最早的異步消息
                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;
            }
        }
        if (needWake) {
            nativeWake(mPtr);
        }
        return true;
    }

***刪除消息removeMessages() ***

final void removeMessages(Handler h, int what, Object object) {
        if (h == null) {
            return;
        }

        synchronized (this) {
            Message p = mMessages;

             // Remove all messages at front.
            while (p != null && p.target == h && p.what == what
                   && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycle();
                p = n;
            }

            // Remove all messages after front.
            while (p != null) {
                Message n = p.next;
                if (n != null) {
                    if (n.target == h && n.what == what
                        && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycle();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }

    final void removeMessages(Handler h, Runnable r, Object object) {
        if (h == null || r == null) {
            return;
        }

        synchronized (this) {
            Message p = mMessages;

              // Remove all messages at front.
            while (p != null && p.target == h && p.callback == r
                   && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycle();
                p = n;
            }

           // Remove all messages after front.
            while (p != null) {
                Message n = p.next;
                if (n != null) {
                    if (n.target == h && n.callback == r
                        && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycle();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }

    final void removeCallbacksAndMessages(Handler h, Object object) {
        if (h == null) {
            return;
        }

        synchronized (this) {
            Message p = mMessages;

            // Remove all messages at front.
            while (p != null && p.target == h
                    && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycle();
                p = n;
            }

           // Remove all messages after front.
            while (p != null) {
                Message n = p.next;
                if (n != null) {
                    if (n.target == h && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycle();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }

4.4 Handler

Handler是和線程的MessageQueue相關(guān)聯(lián)的Runable對象,用于發(fā)送和處理消息建峭。Handlerg和線程及線程的MessageQueue是一一對應(yīng)的玻侥,即每個Handler實例關(guān)聯(lián)一個單一線程和該線程的messagequeue。當(dāng)您創(chuàng)建一個Handler時亿蒸,它就綁定到創(chuàng)建它的線程以及對應(yīng)的消息隊列凑兰。使用該handler將發(fā)送消息到對應(yīng)消息隊列掌桩,并由Handler處理取出的消息。
  簡單來說姑食,handler主要做了兩件事:

  • 將要處理的數(shù)據(jù)消息或runnable以消息形式放入消息隊列波岛,在指定時間處理;
  • 保證運行在多個線程中得消息對象能夠在指定線程中被有序處理音半。
1.先看屬性變量
public class Handler {
    /*
     * 標(biāo)志位盆色,用來檢測那些繼承于它但不是靜態(tài)的匿名類、本地類或成員類祟剔,這些類可能會導(dǎo)致內(nèi)存泄露隔躲。
     */
    private static final boolean FIND_POTENTIAL_LEAKS = false;
    private static final String TAG = "Handler";

    final MessageQueue mQueue;
    final Looper mLooper;
    final Callback mCallback;
    final boolean mAsynchronous;
    IMessenger mMessenger;

}
2.再看構(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);
    }

最后都執(zhí)行的是這兩段構(gòu)造方法:

  1. Handler(Callback callback, boolean async)
    • 當(dāng)標(biāo)志位為true,檢測到可能造成內(nèi)存泄露的類時拋出異常
    • 得到當(dāng)前線程的looper初始化其屬性變量
  2. Handler(Looper looper, Callback callback, boolean async)
    • 由傳入的參數(shù)初始化屬性變量
    
    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;
    }
3.接著看它的生成Message

產(chǎn)生消息的方法obtainMessage()是間接調(diào)用Message的obtain()方法物延,嘗試從消息池中取已有message實例宣旱,便于高效和重復(fù)利用。

    //基本類似叛薯,不再一一列舉
    public final Message obtainMessage()
    public final Message obtainMessage(int what)
    public final Message obtainMessage(int what, Object obj);
    //利用Message的obtain方法構(gòu)造消息
    public final Message obtainMessage(int what, int arg1, int arg2, Object obj){
        return Message.obtain(this, what, arg1, arg2, obj);
    }

    //將runnable封裝成消息
    private static Message getPostMessage(Runnable r) 
    private static Message getPostMessage(Runnable r, Object token) {
        Message m = Message.obtain();
        m.obj = token;
        m.callback = r;
        return m;
    }
4.然后看它的send消息的過程

handler有提供post(runnable)和sendMessage(message)兩種方法浑吟。post其實就是通過上邊的getPostMessage方法將runnable對象封裝成消息發(fā)送至消息隊列。這些發(fā)送消息的方法有:

    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);
    }
    //上述方法都調(diào)用sendMessageDelayed()方法
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

這些方法都轉(zhuǎn)入了sendEmptyMessageAtTime這個方法中去

    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 sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
    }

sendMessageAtTime在指定時間發(fā)送消息耗溜,對消息進(jìn)行有序排隊组力。調(diào)用enqueueMessage()方法,該方法又調(diào)用消息隊列的queue.enqueueMessage()方法抖拴。在指定時間更新消息時序燎字。

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

發(fā)送消息還有一個方法,將消息放在隊列首并立即取消息阿宅。使用方法sendMessageAtFrontOfQueue

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

最后looper循環(huán)取消息并調(diào)用handler的處理方法dispatchMessage 和handleMessage

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    
    //當(dāng)message的callback不為空的時候候衍,調(diào)用handleCallback
    private final void handleCallback(Message message) {  
        message.callback.run();  
    }  

    //這里是個空方法需要子類定義處理邏輯
    public void handleMessage(Message msg) {
    }   

當(dāng)我們實例化一個handler時候,handler會通過mCallback接口來回調(diào)我們的handleMessage方法洒放,看一下這個接口:

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

同時handler也提供了移除回調(diào)removeCallbacks 和 移除消息removeMessages方法

    //移除回調(diào)
    public final void removeCallbacks(Runnable r)
    {
        mQueue.removeMessages(this, r, null);
    }
    
    public final void removeCallbacks(Runnable r, Object token)
    {
        mQueue.removeMessages(this, r, token);
    }

    //移除消息
    public final void removeMessages(int what) {
        mQueue.removeMessages(this, what, null);
    }

    public final void removeMessages(int what, Object object) {
        mQueue.removeMessages(this, what, object);
    }

    //移除消息和回調(diào)
    public final void removeCallbacksAndMessages(Object token) {
        mQueue.removeCallbacksAndMessages(this, token);
    }

5.綜述

以上便是Android中的消息機(jī)制的幾個核心部分內(nèi)容和源碼概括完了蛉鹿。詳細(xì)讀到這里多少也對整個體系有了點深入了解吧。當(dāng)然往湿,個人敘述相對有些混亂妖异,建議感興趣的朋友可以在sdk\sources目錄下找到相應(yīng)源碼查看具體詳細(xì)。
  作為補(bǔ)充還是把對應(yīng)的Handler Looper MessageQueue 和 Message 的.java文件放上來领追,方便不會關(guān)聯(lián)源碼的朋友查看他膳。
  Handler/Looper/MessageQueue/Message .java 文件


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蔓腐,隨后出現(xiàn)的幾起案子矩乐,更是在濱河造成了極大的恐慌龄句,老刑警劉巖回论,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件散罕,死亡現(xiàn)場離奇詭異,居然都是意外死亡傀蓉,警方通過查閱死者的電腦和手機(jī)欧漱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來葬燎,“玉大人误甚,你說我怎么就攤上這事∑拙唬” “怎么了窑邦?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長壕探。 經(jīng)常有香客問我冈钦,道長,這世上最難降的妖魔是什么李请? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任瞧筛,我火速辦了婚禮,結(jié)果婚禮上导盅,老公的妹妹穿的比我還像新娘较幌。我一直安慰自己,他們只是感情好白翻,可當(dāng)我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布乍炉。 她就那樣靜靜地躺著,像睡著了一般滤馍。 火紅的嫁衣襯著肌膚如雪恩急。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天纪蜒,我揣著相機(jī)與錄音衷恭,去河邊找鬼。 笑死纯续,一個胖子當(dāng)著我的面吹牛随珠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播猬错,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼窗看,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了倦炒?” 一聲冷哼從身側(cè)響起显沈,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后拉讯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體涤浇,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年魔慷,在試婚紗的時候發(fā)現(xiàn)自己被綠了只锭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡院尔,死狀恐怖蜻展,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情邀摆,我是刑警寧澤纵顾,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站栋盹,受9級特大地震影響片挂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜贞盯,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一音念、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧躏敢,春花似錦闷愤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至啼器,卻和暖如春旬渠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背端壳。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工告丢, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人损谦。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓岖免,卻偏偏與公主長得像,于是被迫代替她去往敵國和親照捡。 傳聞我的和親對象是個殘疾皇子颅湘,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,472評論 2 348

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