Android 進(jìn)階之 Android消息機(jī)制Handler

消息機(jī)制 由Handler/Looper/MessageQueue/Message等構(gòu)成的

Android有大量的消息驅(qū)動(dòng)方式來進(jìn)行交互,比如Android的四劍客Activity, Service, Broadcast, ContentProvider的啟動(dòng)過程的交互,都離不開消息機(jī)制,Android某種意義上也可以說成是一個(gè)以消息驅(qū)動(dòng)的系統(tǒng)汗茄。消息機(jī)制涉及MessageQueue/Message/Looper/Handler這4個(gè)類袍镀。

消息機(jī)制主要包含:

  • Message:消息分為硬件產(chǎn)生的消息(如按鈕翼闽、觸摸)和軟件生成的消息倦零;
  • MessageQueue:消息隊(duì)列的主要功能向消息池投遞消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next)藐石;
  • Handler:消息輔助類即供,主要功能向消息池發(fā)送各種消息事件(Handler.sendMessage)和處理相應(yīng)消息事件(Handler.handleMessage);
  • Looper:不斷循環(huán)執(zhí)行(Looper.loop)于微,按分發(fā)機(jī)制將消息分發(fā)給目標(biāo)處理者逗嫡。
架構(gòu)圖
  • Looper有一個(gè)MessageQueue消息隊(duì)列;
  • MessageQueue有一組待處理的Message株依;
  • Message中有一個(gè)用于處理消息的Handler驱证;
  • Handler中有Looper和MessageQueue。

如何使用 Handler

  • 創(chuàng)建 Handler
     private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
        //接收處理 收到的消息
            super.handleMessage(msg);
            switch (msg.what) {
                case 0:
                    Log.e("====",msg.obj);
                    break;
                case 1:
                    break;
                default:
            }
        }
    };
  • 創(chuàng)建并發(fā)送 Message
                // 獲取一個(gè) Message
                Message message = Message.obtain();
                message.what = 0;
                message.obj = new Object();
                // 使用 Handler 發(fā)送 Message
                // 消息發(fā)送完成后 Handler 的 handleMessage(Message msg) 會(huì)處理消息
                handler.sendMessage(message);

                // 延遲 1s 發(fā)送 Message
                handler.sendMessageDelayed(message, 1000);
                // 發(fā)送一個(gè)空的 Message
                handler.sendEmptyMessage(message.what);
                // 延遲發(fā)送一個(gè)空的 Message
                handler.sendEmptyMessageDelayed(0, 1000);

                // 還可以這樣
                // 創(chuàng)建 Message 并綁定 Handler
                Message message = handler.obtainMessage();
                message.what = 0;
                message.obj = new Object();

                // 發(fā)送 Message
                message.sendToTarget();

  • 使用 Handler 子線程請(qǐng)求數(shù)據(jù)恋腕,主線程刷新 UI
new Thread(new Runnable() {
            @Override
            public void run() {

//                // 獲取一個(gè) Message
//                Message message = Message.obtain();
//                message.what = 0;
//                message.obj = new Object();
//                // 使用 Handler 發(fā)送 Message
//                // 消息發(fā)送完成后 Handler 的 handleMessage(Message msg) 會(huì)處理消息
//                handler.sendMessage(message);
//
//                // 延遲 1s 發(fā)送 Message
//                handler.sendMessageDelayed(message, 1000);
//                // 發(fā)送一個(gè)空的 Message
//                handler.sendEmptyMessage(message.what);
//                // 延遲發(fā)送一個(gè)空的 Message
//                handler.sendEmptyMessageDelayed(0, 1000);
//
//                // 還可以這樣
//                // 創(chuàng)建 Message 并綁定 Handler
//                Message message = handler.obtainMessage();
//                message.what = 0;
//                message.obj = new Object();
//
//                // 發(fā)送 Message
//                message.sendToTarget();

                // 方法一:將數(shù)據(jù)作為 Message 的 obj 發(fā)送出去抹锄,在 handleMessage 中刷新 UI
                Message msg = Message.obtain();
                msg.what = 1;
                msg.obj = "可以是個(gè)對(duì)象";
                handler.sendMessage(msg);

                // 方法二:直接在 post 中刷新 UI
                handler.post(new Runnable() {
                    @Override public void run() {
                        // 使用 datas 刷新 UI
                        // 這個(gè)方法也會(huì)在 Handler 創(chuàng)建的線程執(zhí)行
                        textView.setText("在handler 創(chuàng)建的線程中執(zhí)行");
                    }
                });


            }
        }).start();

同樣的handler 不止可以在主線程創(chuàng)建,可以在其他線程中創(chuàng)建, 需要調(diào)用 Looper.prepare(),和Looper.loop();

new Thread(new Runnable() {
            @Override
            public void run() {

                Looper.prepare();
                new Handler(){
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                        //處理相應(yīng)的邏輯
                    }
                };
                Looper.loop();
            }
        }).start();

看完了 Handler 的基本用法,接下來就要從源碼角度來探索下 消息傳遞機(jī)制接下來 逐個(gè)類去分析源碼;

Looper

prepare()

對(duì)于無參的情況,默認(rèn)調(diào)用prepare(true)荠藤,表示的是這個(gè)Looper運(yùn)行退出伙单,而對(duì)于false的情況則表示當(dāng)前Looper不運(yùn)行退出,主線程調(diào)用的就是prepare(false)。

private static void prepare(boolean quitAllowed) {
    //每個(gè)線程只允許執(zhí)行一次該方法哈肖,第二次執(zhí)行時(shí)線程的TLS已有數(shù)據(jù)吻育,則會(huì)拋出異常。
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //創(chuàng)建Looper對(duì)象淤井,并保存到當(dāng)前線程的TLS區(qū)域
    sThreadLocal.set(new Looper(quitAllowed));
}

這里的sThreadLocal是ThreadLocal類型布疼,下面,先說說ThreadLocal币狠。

ThreadLocal: 線程本地存儲(chǔ)區(qū)(Thread Local Storage游两,簡(jiǎn)稱為TLS),每個(gè)線程都有自己的私有的本地存儲(chǔ)區(qū)域漩绵,不同線程之間彼此不能訪問對(duì)方的TLS區(qū)域器罐。

ThreadLocal的get()和set()方法操作的類型都是泛型,接著回到前面提到的sThreadLocal變量渐行,其定義如下:

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

可見sThreadLocal的get()和set()操作的類型都是Looper類型轰坊。

Looper.prepare()

Looper.prepare()在每個(gè)線程只允許執(zhí)行一次,該方法會(huì)創(chuàng)建Looper對(duì)象祟印,Looper的構(gòu)造方法中會(huì)創(chuàng)建一個(gè)MessageQueue對(duì)象肴沫,再將Looper對(duì)象保存到當(dāng)前線程TLS。

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);  //創(chuàng)建MessageQueue對(duì)象. 
    mThread = Thread.currentThread();  //記錄當(dāng)前線程.
}

另外蕴忆,與prepare()相近功能的颤芬,還有一個(gè)prepareMainLooper()方法,該方法主要在ActivityThread類中使用。

public static void prepareMainLooper() {
    prepare(false); //設(shè)置不允許退出的Looper
    synchronized (Looper.class) {
        //將當(dāng)前的Looper保存為主Looper站蝠,每個(gè)線程只允許執(zhí)行一次汰具。
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

loop()

public static void loop() {
    final Looper me = myLooper();  //獲取TLS存儲(chǔ)的Looper對(duì)象
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;  //獲取Looper對(duì)象中的消息隊(duì)列

    Binder.clearCallingIdentity();
    //確保在權(quán)限檢查時(shí)基于本地進(jìn)程,而不是基于最初調(diào)用進(jìn)程菱魔。
    final long ident = Binder.clearCallingIdentity();

    for (;;) { //進(jìn)入loop的主循環(huán)方法
        Message msg = queue.next(); //可能會(huì)阻塞 
        if (msg == null) { //沒有消息留荔,則退出循環(huán)
            return;
        }

        Printer logging = me.mLogging;  //默認(rèn)為null,可通過setMessageLogging()方法來指定輸出澜倦,用于debug功能
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }
        msg.target.dispatchMessage(msg); //用于分發(fā)Message 
        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        final long newIdent = Binder.clearCallingIdentity(); //確保分發(fā)過程中identity不會(huì)損壞
        if (ident != newIdent) {
             //打印identity改變的log聚蝶,在分發(fā)消息過程中是不希望身份被改變的。
        }
        msg.recycleUnchecked();  //將Message放入消息池 
    }
}

loop()進(jìn)入循環(huán)模式藻治,不斷重復(fù)下面的操作碘勉,直到?jīng)]有消息時(shí)退出循環(huán)

  • 讀取MessageQueue的下一條Message;
  • 把Message分發(fā)給相應(yīng)的target桩卵;
  • 再把分發(fā)后的Message回收到消息池验靡,以便重復(fù)利用。

這是這個(gè)消息處理的核心部分雏节。另外胜嗓,上面代碼中可以看到有l(wèi)ogging方法,這是用于debug的矾屯,默認(rèn)情況下logging == null,通過設(shè)置setMessageLogging()用來開啟debug工作初厚。

quit()

public void quit() {
    mQueue.quit(false); //消息移除
}

public void quitSafely() {
    mQueue.quit(true); //安全地消息移除
}

Looper.quit()方法的實(shí)現(xiàn)最終調(diào)用的是MessageQueue.quit()方法

void quit(boolean safe) {
        // 當(dāng)mQuitAllowed為false件蚕,表示不運(yùn)行退出,強(qiáng)行調(diào)用quit()會(huì)拋出異常
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }
        synchronized (this) {
            if (mQuitting) { //防止多次執(zhí)行退出操作
                return;
            }
            mQuitting = true;
            if (safe) {
                removeAllFutureMessagesLocked(); //移除尚未觸發(fā)的所有消息
            } else {
                removeAllMessagesLocked(); //移除所有的消息
            }
            //mQuitting=false产禾,那么認(rèn)定為 mPtr != 0
            nativeWake(mPtr);
        }
    }

消息退出的方式:

  • 當(dāng)safe =true時(shí)排作,只移除尚未觸發(fā)的所有消息,對(duì)于正在觸發(fā)的消息并不移除亚情;
  • 當(dāng)safe =flase時(shí)妄痪,移除所有的消息

myLooper

用于獲取TLS存儲(chǔ)的Looper對(duì)象

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

post

發(fā)送消息,并設(shè)置消息的callback楞件,用于處理消息衫生。

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

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

Handler

創(chuàng)建Handler

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

public Handler(Callback callback, boolean async) {
    //匿名類、內(nèi)部類或本地類都必須申明為static土浸,否則會(huì)警告可能出現(xiàn)內(nèi)存泄露
    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());
        }
    }
    //必須先執(zhí)行Looper.prepare()罪针,才能獲取Looper對(duì)象,否則為null.
    mLooper = Looper.myLooper();  //從當(dāng)前線程的TLS中獲取Looper對(duì)象
    if (mLooper == null) {
        throw new RuntimeException("");
    }
    mQueue = mLooper.mQueue; //消息隊(duì)列黄伊,來自Looper對(duì)象
    mCallback = callback;  //回調(diào)方法
    mAsynchronous = async; //設(shè)置消息是否為異步處理方式
}

對(duì)于Handler的無參構(gòu)造方法泪酱,默認(rèn)采用當(dāng)前線程TLS中的Looper對(duì)象,并且callback回調(diào)方法為null,且消息為同步處理方式墓阀。只要執(zhí)行的Looper.prepare()方法毡惜,那么便可以獲取有效的Looper對(duì)象。

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

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

Handler類在構(gòu)造方法中斯撮,可指定Looper经伙,Callback回調(diào)方法以及消息的處理方式(同步或異步),對(duì)于無參的handler吮成,默認(rèn)是當(dāng)前線程的Looper橱乱。

消息分發(fā)機(jī)制

在Looper.loop()中,當(dāng)發(fā)現(xiàn)有消息時(shí)粱甫,調(diào)用消息的目標(biāo)handler泳叠,執(zhí)行dispatchMessage()方法來分發(fā)消息。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        //當(dāng)Message存在回調(diào)方法茶宵,回調(diào)msg.callback.run()方法危纫;
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            //當(dāng)Handler存在Callback成員變量時(shí),回調(diào)方法handleMessage()乌庶;
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //Handler自身的回調(diào)方法handleMessage()
        handleMessage(msg);
    }
}

分發(fā)消息流程:

  • 1.當(dāng)Message的回調(diào)方法不為空時(shí)种蝶,則回調(diào)方法msg.callback.run(),其中callBack數(shù)據(jù)類型為Runnable,否則進(jìn)入步驟2瞒大;
  • 2.當(dāng)Handler的mCallback成員變量不為空時(shí)螃征,則回調(diào)方法mCallback.handleMessage(msg),否則進(jìn)入步驟3;
  • 3.調(diào)用Handler自身的回調(diào)方法handleMessage()透敌,該方法默認(rèn)為空盯滚,Handler子類通過覆寫該方法來完成具體的邏輯。

對(duì)于很多情況下酗电,消息分發(fā)后的處理方法是第3種情況魄藕,即Handler.handleMessage(),一般地往往通過覆寫該方法從而實(shí)現(xiàn)自己的業(yè)務(wù)邏輯撵术。

消息發(fā)送

發(fā)送消息調(diào)用鏈

可以發(fā)現(xiàn)所有的發(fā)消息方式背率,最終都是調(diào)用MessageQueue.enqueueMessage();

Handler.sendEmptyMessage()等系列方法最終調(diào)用MessageQueue.enqueueMessage(msg, uptimeMillis),將消息添加到消息隊(duì)列中嫩与,其中uptimeMillis為系統(tǒng)當(dāng)前的運(yùn)行時(shí)間寝姿,不包括休眠時(shí)間。

obtainMessage

獲取消息

public final Message obtainMessage() {
    return Message.obtain(this); 
}

Handler.obtainMessage()方法划滋,最終調(diào)用Message.obtainMessage(this)会油,其中this為當(dāng)前的Handler對(duì)象。

removeMessages

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

Handler是消息機(jī)制中非常重要的輔助類古毛,更多的實(shí)現(xiàn)都是MessageQueue, Message中的方法翻翩,Handler的目的是為了更加方便的使用消息機(jī)制都许。

MessageQueue

MessageQueue是消息機(jī)制的Java層和C++層的連接紐帶,大部分核心方法都交給native層來處理嫂冻,其中MessageQueue類中涉及的native方法如下:

創(chuàng)建MessageQueue

在handler 的prepare() 中,Looper 的構(gòu)造方法里會(huì)new出一個(gè) MessageQueue

    MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    //通過native方法初始化消息隊(duì)列胶征,其中mPtr是供native代碼使用
    mPtr = nativeInit();
}

next()

提取下一條message

Message next() {
    final long ptr = mPtr;
    if (ptr == 0) { //當(dāng)消息循環(huán)已經(jīng)退出,則直接返回
        return null;
    }
    int pendingIdleHandlerCount = -1; // 循環(huán)迭代的首次為-1
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        //阻塞操作桨仿,當(dāng)?shù)却齨extPollTimeoutMillis時(shí)長(zhǎng)睛低,或者消息隊(duì)列被喚醒,都會(huì)返回
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                //當(dāng)消息Handler為空時(shí)服傍,查詢MessageQueue中的下一條異步消息msg钱雷,則退出循環(huán)。
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    //當(dāng)異步消息觸發(fā)時(shí)間大于當(dāng)前時(shí)間吹零,則設(shè)置下一次輪詢的超時(shí)時(shí)長(zhǎng)
                    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;
                    //設(shè)置消息的使用狀態(tài),即flags |= FLAG_IN_USE
                    msg.markInUse();
                    return msg;   //成功地獲取MessageQueue中的下一條即將要執(zhí)行的消息
                }
            } else {
                //沒有消息
                nextPollTimeoutMillis = -1;
            }
            //消息正在退出灿椅,返回null
            if (mQuitting) {
                dispose();
                return null;
            }
            //當(dāng)消息隊(duì)列為空套蒂,或者是消息隊(duì)列的第一個(gè)消息時(shí)
            if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                //沒有idle handlers 需要運(yùn)行,則循環(huán)并等待茫蛹。
                mBlocked = true;
                continue;
            }
            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }
        //只有第一次循環(huán)時(shí)操刀,會(huì)運(yùn)行idle handlers,執(zhí)行完成后婴洼,重置pendingIdleHandlerCount為0.
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; //去掉handler的引用
            boolean keep = false;
            try {
                keep = idler.queueIdle();  //idle時(shí)執(zhí)行的方法
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }
            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }
        //重置idle handler個(gè)數(shù)為0骨坑,以保證不會(huì)再次重復(fù)運(yùn)行
        pendingIdleHandlerCount = 0;
        //當(dāng)調(diào)用一個(gè)空閑handler時(shí),一個(gè)新message能夠被分發(fā)柬采,因此無需等待可以直接查詢pending message.
        nextPollTimeoutMillis = 0;
    }
}

nativePollOnce是阻塞操作欢唾,其中nextPollTimeoutMillis代表下一個(gè)消息到來前,還需要等待的時(shí)長(zhǎng)警没;當(dāng)nextPollTimeoutMillis = -1時(shí)匈辱,表示消息隊(duì)列中無消息振湾,會(huì)一直等待下去杀迹。

當(dāng)處于空閑時(shí),往往會(huì)執(zhí)行IdleHandler中的方法押搪。當(dāng)nativePollOnce()返回后树酪,next()從mMessages中提取一個(gè)消息。

enqueueMessage

添加一條消息到消息隊(duì)列

boolean enqueueMessage(Message msg, long when) {
    // 每一個(gè)普通Message必須有一個(gè)target
    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) {  //正在退出時(shí)大州,回收msg续语,加入到消息池
            msg.recycle();
            return false;
        }
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            //p為null(代表MessageQueue沒有消息) 或者msg的觸發(fā)時(shí)間是隊(duì)列中最早的, 則進(jìn)入該該分支
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked; //當(dāng)阻塞時(shí)需要喚醒
        } else {
            //將消息按時(shí)間順序插入到MessageQueue厦画。一般地疮茄,不需要喚醒事件隊(duì)列滥朱,除非
            //消息隊(duì)頭存在barrier,并且同時(shí)Message是隊(duì)列中最早的異步消息力试。
            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;
        }
        //消息沒有退出徙邻,我們認(rèn)為此時(shí)mPtr != 0
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

MessageQueue是按照Message觸發(fā)時(shí)間的先后順序排列的,隊(duì)頭的消息是將要最早觸發(fā)的消息。當(dāng)有消息需要加入消息隊(duì)列時(shí),會(huì)從隊(duì)列頭開始遍歷禾酱,直到找到消息應(yīng)該插入的合適位置滞详,以保證所有消息的時(shí)間順序。

removeMessages

void removeMessages(Handler h, int what, Object object) {
    if (h == null) {
        return;
    }
    synchronized (this) {
        Message p = mMessages;
        //從消息隊(duì)列的頭部開始装畅,移除所有符合條件的消息
        while (p != null && p.target == h && p.what == what
               && (object == null || p.obj == object)) {
            Message n = p.next;
            mMessages = n;
            p.recycleUnchecked();
            p = n;
        }
        //移除剩余的符合要求的消息
        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.recycleUnchecked();
                    p.next = nn;
                    continue;
                }
            }
            p = n;
        }
    }
}

這個(gè)移除消息的方法,采用了兩個(gè)while循環(huán),第一個(gè)循環(huán)是從隊(duì)頭開始并徘,移除符合條件的消息,第二個(gè)循環(huán)是從頭部移除完連續(xù)的滿足條件的消息之后嚷缭,再?gòu)年?duì)列后面繼續(xù)查詢是否有滿足條件的消息需要被移除饮亏。

Message

創(chuàng)建消息

每個(gè)消息用Message表示,Message主要包含以下內(nèi)容:

數(shù)據(jù)類型          成員變量              解釋
int               what              消息類別
long              when            消息觸發(fā)時(shí)間
int                arg1             參數(shù)1
int                arg2             參數(shù)2
Object             obj              消息內(nèi)容
Handler           target            消息響應(yīng)方
Runnable          callback          回調(diào)方法

消息池

在代碼中阅爽,可能經(jīng)陈沸遥看到recycle()方法,咋一看付翁,可能是在做虛擬機(jī)的gc()相關(guān)的工作简肴,其實(shí)不然,這是用于把消息加入到消息池的作用百侧。這樣的好處是砰识,當(dāng)消息池不為空時(shí),可以直接從消息池中獲取Message對(duì)象佣渴,而不是直接創(chuàng)建辫狼,提高效率。

靜態(tài)變量sPool的數(shù)據(jù)類型為Message辛润,通過next成員變量膨处,維護(hù)一個(gè)消息池;靜態(tài)變量MAX_POOL_SIZE代表消息池的可用大猩笆真椿;消息池的默認(rèn)大小為50。

消息池常用的操作方法是obtain()和recycle()乎澄。

obtain

從消息池中獲取消息

public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null; //從sPool中取出一個(gè)Message對(duì)象突硝,并消息鏈表斷開
            m.flags = 0; // 清除in-use flag
            sPoolSize--; //消息池的可用大小進(jìn)行減1操作
            return m;
        }
    }
    return new Message(); // 當(dāng)消息池為空時(shí),直接創(chuàng)建Message對(duì)象
}

obtain()置济,從消息池取Message解恰,都是把消息池表頭的Message取走锋八,再把表頭指向next;

recycle

把不再使用的消息加入消息池

public void recycle() {
    if (isInUse()) { //判斷消息是否正在使用
        if (gCheckRecycle) { //Android 5.0以后的版本默認(rèn)為true,之前的版本默認(rèn)為false.
            throw new IllegalStateException("This message cannot be recycled because it is still in use.");
        }
        return;
    }
    recycleUnchecked();
}

//對(duì)于不再使用的消息,加入到消息池
void recycleUnchecked() {
    //將消息標(biāo)示位置為IN_USE护盈,并清空消息所有的參數(shù)查库。
    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) { //當(dāng)消息池沒有滿時(shí),將Message對(duì)象加入消息池
            next = sPool;
            sPool = this;
            sPoolSize++; //消息池的可用大小進(jìn)行加1操作
        }
    }
}

recycle()黄琼,將Message加入到消息池的過程樊销,都是把Message加到鏈表的表頭;

總結(jié)

消息機(jī)制

相信看了源碼之后,現(xiàn)在對(duì)消息機(jī)制應(yīng)該是比較清晰了,

  • Handler通過sendMessage()發(fā)送Message到MessageQueue隊(duì)列脏款;
  • Looper通過loop()围苫,不斷提取出達(dá)到觸發(fā)條件的Message,并將Message交給target來處理撤师;
  • 經(jīng)過dispatchMessage()后剂府,交回給Handler的handleMessage()來進(jìn)行相應(yīng)地處理。
  • 將Message加入MessageQueue時(shí)剃盾,處往管道寫入字符腺占,可以會(huì)喚醒loop線程;如果MessageQueue中沒有Message痒谴,并處于Idle狀態(tài)衰伯,則會(huì)執(zhí)行IdelHandler接口中的方法,往往用于做一些清理性地工作积蔚。

消息分發(fā)的優(yōu)先級(jí):

  • Message的回調(diào)方法:message.callback.run()意鲸,優(yōu)先級(jí)最高;
  • Handler的回調(diào)方法:Handler.mCallback.handleMessage(msg)尽爆,優(yōu)先級(jí)僅次于1怎顾;
  • Handler的默認(rèn)方法:Handler.handleMessage(msg),優(yōu)先級(jí)最低漱贱。

搬運(yùn)來源,感謝大神:

Android源碼:https://developer.android.google.cn/reference/android/os/Handler.html

http://gityuan.com/2015/12/26/handler-message-framework/

http://www.reibang.com/p/1a5a3db45cfa

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末槐雾,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子幅狮,更是在濱河造成了極大的恐慌募强,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件彪笼,死亡現(xiàn)場(chǎng)離奇詭異钻注,居然都是意外死亡蚂且,警方通過查閱死者的電腦和手機(jī)配猫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來杏死,“玉大人泵肄,你說我怎么就攤上這事捆交。” “怎么了腐巢?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵品追,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我冯丙,道長(zhǎng)肉瓦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任胃惜,我火速辦了婚禮泞莉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘船殉。我一直安慰自己鲫趁,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布利虫。 她就那樣靜靜地躺著挨厚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪糠惫。 梳的紋絲不亂的頭發(fā)上疫剃,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音硼讽,去河邊找鬼慌申。 笑死,一個(gè)胖子當(dāng)著我的面吹牛理郑,可吹牛的內(nèi)容都是我干的蹄溉。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼您炉,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼柒爵!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起赚爵,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤棉胀,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后冀膝,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體唁奢,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年窝剖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了麻掸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡赐纱,死狀恐怖脊奋,靈堂內(nèi)的尸體忽然破棺而出熬北,到底是詐尸還是另有隱情,我是刑警寧澤诚隙,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布讶隐,位于F島的核電站,受9級(jí)特大地震影響久又,放射性物質(zhì)發(fā)生泄漏巫延。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一地消、第九天 我趴在偏房一處隱蔽的房頂上張望烈评。 院中可真熱鬧,春花似錦犯建、人聲如沸讲冠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽竿开。三九已至,卻和暖如春玻熙,著一層夾襖步出監(jiān)牢的瞬間否彩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工嗦随, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留列荔,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓枚尼,卻偏偏與公主長(zhǎng)得像贴浙,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子署恍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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