1. 程式人生 > 其它 >Android訊息機制1-Handler(Java層)

Android訊息機制1-Handler(Java層)

一、概述

在整個Android的原始碼世界裡,有兩大利劍,其一是Binder IPC機制,,另一個便是訊息機制(由Handler/Looper/MessageQueue等構成的)。

Android有大量的訊息驅動方式來進行互動,比如Android的四劍客Activity,Service,Broadcast,ContentProvider的啟動過程的互動,都離不開訊息機制,Android某種意義上也可以說成是一個以訊息驅動的系統。訊息機制涉及MessageQueue/Message/Looper/Handler這4個類。

1.1 模型

訊息機制主要包含:
  • Message:訊息分為硬體產生的訊息(如按鈕、觸控)和軟體生成的訊息;
  • MessageQueue:訊息佇列的主要功能向訊息池投遞訊息(MessageQueue.enqueueMessage)和取走訊息池的訊息(MessageQueue.next);
  • Handler:訊息輔助類,主要功能向訊息池傳送各種訊息事件(Handler.sendMessage)和處理相應訊息事件(Handler.handleMessage);
  • Looper:不斷迴圈執行(Looper.loop),按分發機制將訊息分發給目標處理者。

1.2 架構圖

  • Looper有一個MessageQueue訊息佇列;
  • MessageQueue有一組待處理的Message;
  • Message
    中有一個用於處理訊息的Handler;
  • Handler中有Looper和MessageQueue。

1.3 典型例項

先展示一個典型的關於Handler/Looper的執行緒
class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        Looper.prepare();   //【見 2.1】

        mHandler = new Handler() {  //【見 3.1】
            public void handleMessage(Message msg) {
                //TODO 定義訊息處理邏輯. 【見 3.2】
            }
        };

        Looper.loop();  //【見 2.2】
    }
}

 

二、Looper

2.1 prepare()

對於無參的情況,預設呼叫prepare(true),表示的是這個Looper允許退出,而對於false的情況則表示當前Looper不允許退出。
private static void prepare(boolean quitAllowed) {
    //每個執行緒只允許執行一次該方法,第二次執行時執行緒的TLS已有資料,則會丟擲異常。
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //建立Looper物件,並儲存到當前執行緒的TLS區域
    sThreadLocal.set(new Looper(quitAllowed));
}

  

這裡的sThreadLocal是ThreadLocal型別,下面,先說說ThreadLocal。 ThreadLocal: 執行緒本地儲存區(Thread Local Storage,簡稱為TLS),每個執行緒都有自己的私有的本地儲存區域,不同執行緒之間彼此不能訪問對方的TLS區域。TLS常用的操作方法:
  • ThreadLocal.set(T value):將value儲存到當前執行緒的TLS區域,原始碼如下:
public void set(T value) {
    Thread currentThread = Thread.currentThread(); //獲取當前執行緒
    Values values = values(currentThread); //查詢當前執行緒的本地儲存區
    if (values == null) {
        //當執行緒本地儲存區,尚未儲存該執行緒相關資訊時,則建立Values物件
        values = initializeValues(currentThread);
    }
    //儲存資料value到當前執行緒this
    values.put(this, value);
}  
  • ThreadLocal.get():獲取當前執行緒TLS區域的資料,原始碼如下:
public T get() {
    Thread currentThread = Thread.currentThread(); //獲取當前執行緒
    Values values = values(currentThread); //查詢當前執行緒的本地儲存區
    if (values != null) {
        Object[] table = values.table;
        int index = hash & values.mask;
        if (this.reference == table[index]) {
            return (T) table[index + 1]; //返回當前執行緒儲存區中的資料
        }
    } else {
        //建立Values物件
        values = initializeValues(currentThread);
    }
    return (T) values.getAfterMiss(this); //從目標執行緒儲存區沒有查詢是則返回null
} 
ThreadLocal的get()和set()方法操作的型別都是泛型,接著回到前面提到的sThreadLocal變數,其定義如下:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>() 
可見sThreadLocal的get()和set()操作的型別都是Looper型別。 Looper.prepare() Looper.prepare()在每個執行緒只允許執行一次,該方法會建立Looper物件,Looper的構造方法中會建立一個MessageQueue物件,再將Looper物件儲存到當前執行緒TLS。 對於Looper型別的構造方法如下:
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);  //建立MessageQueue物件. 【見4.1】
    mThread = Thread.currentThread();  //記錄當前執行緒.
}
另外,與prepare()相近功能的,還有一個prepareMainLooper()方法,該方法主要在ActivityThread類中使用。
public static void prepareMainLooper() {
    prepare(false); //設定不允許退出的Looper
    synchronized (Looper.class) {
        //將當前的Looper儲存為主Looper,每個執行緒只允許執行一次。
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
} 

2.2 loop()

public static void loop() {
    final Looper me = myLooper();  //獲取TLS儲存的Looper物件 【見2.4】
    final MessageQueue queue = me.mQueue;  //獲取Looper物件中的訊息佇列

    Binder.clearCallingIdentity();
    //確保在許可權檢查時基於本地程序,而不是呼叫程序。
    final long ident = Binder.clearCallingIdentity();

    for (;;) { //進入loop的主迴圈方法
        Message msg = queue.next(); //可能會阻塞 【見4.2】
        if (msg == null) { //沒有訊息,則退出迴圈
            return;
        }

        //預設為null,可通過setMessageLogging()方法來指定輸出,用於debug功能
        Printer logging = me.mLogging;  
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }
        msg.target.dispatchMessage(msg); //用於分發Message 【見3.2】
        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        //恢復呼叫者資訊
        final long newIdent = Binder.clearCallingIdentity();
        msg.recycleUnchecked();  //將Message放入訊息池 【見5.2】
    }
}
loop()進入迴圈模式,不斷重複下面的操作,直到沒有訊息時退出迴圈
  • 讀取MessageQueue的下一條Message;
  • 把Message分發給相應的target;
  • 再把分發後的Message回收到訊息池,以便重複利用。
這是這個訊息處理的核心部分。另外,上面程式碼中可以看到有logging方法,這是用於debug的,預設情況下logging == null,通過設定setMessageLogging()用來開啟debug工作。

2.3 quit()

public void quit() {
    mQueue.quit(false); //訊息移除
}
public void quitSafely() {
    mQueue.quit(true); //安全地訊息移除
}
Looper.quit()方法的實現最終呼叫的是MessageQueue.quit()方法 MessageQueue.quit()
void quit(boolean safe) {
        // 當mQuitAllowed為false,表示不執行退出,強行呼叫quit()會丟擲異常
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }
        synchronized (this) {
            if (mQuitting) { //防止多次執行退出操作
                return;
            }
            mQuitting = true;
            if (safe) {
                removeAllFutureMessagesLocked(); //移除尚未觸發的所有訊息
            } else {
                removeAllMessagesLocked(); //移除所有的訊息
            }
            //mQuitting=false,那麼認定為 mPtr != 0
            nativeWake(mPtr);
        }
    } 
訊息退出的方式:
  • 當safe =true時,只移除尚未觸發的所有訊息,對於正在觸發的訊息並不移除;
  • 當safe =flase時,移除所有的訊息

2.4 常用方法

2.4.1 myLooper

用於獲取TLS儲存的Looper物件
public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

2.4.2 post

傳送訊息,並設定訊息的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

3.1 建立Handler

3.1.1 無參構造

public Handler() {
    this(null, false);
}
public Handler(Callback callback, boolean async) {
    //匿名類、內部類或本地類都必須申明為static,否則會警告可能出現記憶體洩露
    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());
        }
    }
    //必須先執行Looper.prepare(),才能獲取Looper物件,否則為null.
    mLooper = Looper.myLooper();  //從當前執行緒的TLS中獲取Looper物件【見2.1】
    if (mLooper == null) {
        throw new RuntimeException("");
    }
    mQueue = mLooper.mQueue; //訊息佇列,來自Looper物件
    mCallback = callback;  //回撥方法
    mAsynchronous = async; //設定訊息是否為非同步處理方式
}
對於Handler的無參構造方法,預設採用當前執行緒TLS中的Looper物件,並且callback回撥方法為null,且訊息為同步處理方式。只要執行的Looper.prepare()方法,那麼便可以獲取有效的Looper物件。

3.1.2 有參構造

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類在構造方法中,可指定Looper,Callback回撥方法以及訊息的處理方式(同步或非同步),對於無參的handler,預設是當前執行緒的Looper。

3.2 訊息分發機制

在Looper.loop()中,當發現有訊息時,呼叫訊息的目標handler,執行dispatchMessage()方法來分發訊息。
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        //當Message存在回撥方法,回撥msg.callback.run()方法;
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            //當Handler存在Callback成員變數時,回撥方法handleMessage();
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //Handler自身的回撥方法handleMessage()
        handleMessage(msg);
    }
}
分發訊息流程:
  1. Message的回撥方法不為空時,則回撥方法msg.callback.run(),其中callBack資料型別為Runnable,否則進入步驟2;
  2. HandlermCallback成員變數不為空時,則回撥方法mCallback.handleMessage(msg),否則進入步驟3;
  3. 呼叫Handler自身的回撥方法handleMessage(),該方法預設為空,Handler子類通過覆寫該方法來完成具體的邏輯。
對於很多情況下,訊息分發後的處理方法是第3種情況,即Handler.handleMessage(),一般地往往通過覆寫該方法從而實現自己的業務邏輯。

3.3 訊息傳送

傳送訊息呼叫鏈: 從上圖,可以發現所有的發訊息方式,最終都是呼叫MessageQueue.enqueueMessage();

3.3.1 sendEmptyMessage

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

3.3.2 sendEmptyMessageDelayed

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

3.3.3 sendMessageDelayed

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

3.3.4 sendMessageAtTime

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

3.3.5 sendMessageAtFrontOfQueue

public final boolean sendMessageAtFrontOfQueue(Message msg) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        return false;
    }
    return enqueueMessage(queue, msg, 0);
}
該方法通過設定訊息的觸發時間為0,從而使Message加入到訊息佇列的隊頭。

3.3.6 post

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

3.3.7 postAtFrontOfQueue

public final boolean postAtFrontOfQueue(Runnable r) {
    return sendMessageAtFrontOfQueue(getPostMessage(r));
}

3.3.8 enqueueMessage

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

小節

Handler.sendEmptyMessage()等系列方法最終呼叫MessageQueue.enqueueMessage(msg, uptimeMillis),將訊息新增到訊息佇列中,其中uptimeMillis為系統當前的執行時間,不包括休眠時間。

3.4 Handler其他方法

3.4.1 obtainMessage

獲取訊息
public final Message obtainMessage() {
    return Message.obtain(this); 【見5.2】
}
Handler.obtainMessage()方法,最終呼叫Message.obtainMessage(this),其中this為當前的Handler物件。

3.4.2 removeMessages

public final void removeMessages(int what) {
    mQueue.removeMessages(this, what, null); 【見 4.5】
}
Handler是訊息機制中非常重要的輔助類,更多的實現都是MessageQueue, Message中的方法,Handler的目的是為了更加方便的使用訊息機制。

四、MessageQueue

MessageQueue是訊息機制的Java層和C++層的連線紐帶,大部分核心方法都交給native層來處理,其中MessageQueue類中涉及的native方法如下:
private native static long nativeInit();
private native static void nativeDestroy(long ptr);
private native void nativePollOnce(long ptr, int timeoutMillis);
private native static void nativeWake(long ptr);
private native static boolean nativeIsPolling(long ptr);
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);

  

關於這些native方法的介紹,見Android訊息機制2-Handler(native篇)。

4.1 建立MessageQueue

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    //通過native方法初始化訊息佇列,其中mPtr是供native程式碼使用
    mPtr = nativeInit();
}

  

4.2 next()

提取下一條message
Message next() {
    final long ptr = mPtr;
    if (ptr == 0) { //當訊息迴圈已經退出,則直接返回
        return null;
    }
    int pendingIdleHandlerCount = -1; // 迴圈迭代的首次為-1
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        //阻塞操作,當等待nextPollTimeoutMillis時長,或者訊息佇列被喚醒,都會返回
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            //當訊息的Handler為空時,則查詢非同步訊息
            if (msg != null && msg.target == null) {
                //當查詢到非同步訊息,則立刻退出迴圈
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    //當非同步訊息觸發時間大於當前時間,則設定下一次輪詢的超時時長
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 獲取一條訊息,並返回
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    //設定訊息的使用狀態,即flags |= FLAG_IN_USE
                    msg.markInUse();
                    return msg;   //成功地獲取MessageQueue中的下一條即將要執行的訊息
                }
            } else {
                //沒有訊息
                nextPollTimeoutMillis = -1;
            }
            //訊息正在退出,返回null
            if (mQuitting) {
                dispose();
                return null;
            }
            //當訊息佇列為空,或者是訊息佇列的第一個訊息時
            if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                //沒有idle handlers 需要執行,則迴圈並等待。
                mBlocked = true;
                continue;
            }
            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }
        //只有第一次迴圈時,會執行idle handlers,執行完成後,重置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時執行的方法
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }
            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }
        //重置idle handler個數為0,以保證不會再次重複執行
        pendingIdleHandlerCount = 0;
        //當呼叫一個空閒handler時,一個新message能夠被分發,因此無需等待可以直接查詢pending message.
        nextPollTimeoutMillis = 0;
    }
}

  

nativePollOnce是阻塞操作,其中nextPollTimeoutMillis代表下一個訊息到來前,還需要等待的時長;當nextPollTimeoutMillis = -1時,表示訊息佇列中無訊息,會一直等待下去。 當處於空閒時,往往會執行IdleHandler中的方法。當nativePollOnce()返回後,next()從mMessages中提取一個訊息。 nativePollOnce()在native做了大量的工作,想進一步瞭解可檢視 Android訊息機制2-Handler(native篇)

4.3 enqueueMessage

新增一條訊息到訊息佇列
boolean enqueueMessage(Message msg, long when) {
    // 每一個普通Message必須有一個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) {  //正在退出時,回收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的觸發時間是佇列中最早的, 則進入該該分支
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked; //當阻塞時需要喚醒
        } else {
            //將訊息按時間順序插入到MessageQueue。一般地,不需要喚醒事件佇列,除非
            //訊息隊頭存在barrier,並且同時Message是佇列中最早的非同步訊息。
            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;
        }
        //訊息沒有退出,我們認為此時mPtr != 0
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

  

MessageQueue是按照Message觸發時間的先後順序排列的,隊頭的訊息是將要最早觸發的訊息。當有訊息需要加入訊息佇列時,會從佇列頭開始遍歷,直到找到訊息應該插入的合適位置,以保證所有訊息的時間順序。

4.4 removeMessages

void removeMessages(Handler h, int what, Object object) {
    if (h == null) {
        return;
    }
    synchronized (this) {
        Message p = mMessages;
        //從訊息佇列的頭部開始,移除所有符合條件的訊息
        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;
        }
    }
}

  

這個移除訊息的方法,採用了兩個while迴圈,第一個迴圈是從隊頭開始,移除符合條件的訊息,第二個迴圈是從頭部移除完連續的滿足條件的訊息之後,再從佇列後面繼續查詢是否有滿足條件的訊息需要被移除。

4.5 postSyncBarrier

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

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

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

  

前面小節[4.3]已說明每一個普通Message必須有一個target,對於特殊的message是沒有target,即同步barrier token。 這個訊息的價值就是用於攔截同步訊息,所以並不會喚醒Looper.
public void removeSyncBarrier(int token) {
     synchronized (this) {
         Message prev = null;
         Message p = mMessages;
         //從訊息佇列找到 target為空,並且token相等的Message
         while (p != null && (p.target != null || p.arg1 != token)) {
             prev = p;
             p = p.next;
         }
         final boolean needWake;
         if (prev != null) {
             prev.next = p.next;
             needWake = false;
         } else {
             mMessages = p.next;
             needWake = mMessages == null || mMessages.target != null;
         }
         p.recycleUnchecked();

         if (needWake && !mQuitting) {
             nativeWake(mPtr);
         }
     }
 }

  

postSyncBarrier只對同步訊息產生影響,對於非同步訊息沒有任何差別。

五、 Message

5.1 訊息物件

每個訊息用Message表示,Message主要包含以下內容:
資料型別 成員變數 解釋
int what 訊息類別
long when 訊息觸發時間
int arg1 引數1
int arg2 引數2
Object obj 訊息內容
Handler target 訊息響應方
Runnable callback 回撥方法
建立訊息的過程,就是填充訊息的上述內容的一項或多項。

5.2 訊息池

在程式碼中,可能經常看到recycle()方法,咋一看,可能是在做虛擬機器的gc()相關的工作,其實不然,這是用於把訊息加入到訊息池的作用。這樣的好處是,當訊息池不為空時,可以直接從訊息池中獲取Message物件,而不是直接建立,提高效率。 靜態變數sPool的資料型別為Message,通過next成員變數,維護一個訊息池;靜態變數MAX_POOL_SIZE代表訊息池的可用大小;訊息池的預設大小為50。 訊息池常用的操作方法是obtain()和recycle()。

5.2.1 obtain

從訊息池中獲取訊息
public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null; //從sPool中取出一個Message物件,並訊息連結串列斷開
            m.flags = 0; // 清除in-use flag
            sPoolSize--; //訊息池的可用大小進行減1操作
            return m;
        }
    }
    return new Message(); // 當訊息池為空時,直接建立Message物件
}

  

obtain(),從訊息池取Message,都是把訊息池表頭的Message取走,再把表頭指向next;

5.2.2 recycle

把不再使用的訊息加入訊息池
public void recycle() {
    if (isInUse()) { //判斷訊息是否正在使用
        if (gCheckRecycle) { //Android 5.0以後的版本預設為true,之前的版本預設為false.
            throw new IllegalStateException("This message cannot be recycled because it is still in use.");
        }
        return;
    }
    recycleUnchecked();
}

//對於不再使用的訊息,加入到訊息池
void recycleUnchecked() {
    //將訊息標示位置為IN_USE,並清空訊息所有的引數。
    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) { //當訊息池沒有滿時,將Message物件加入訊息池
            next = sPool;
            sPool = this;
            sPoolSize++; //訊息池的可用大小進行加1操作
        }
    }
}

  

recycle(),將Message加入到訊息池的過程,都是把Message加到連結串列的表頭;

六、總結

最後用一張圖,來表示整個訊息機制 圖解:
  • Handler通過sendMessage()傳送Message到MessageQueue佇列;
  • Looper通過loop(),不斷提取出達到觸發條件的Message,並將Message交給target來處理;
  • 經過dispatchMessage()後,交回給Handler的handleMessage()來進行相應地處理。
  • 將Message加入MessageQueue時,處往管道寫入字元,可以會喚醒loop執行緒;如果MessageQueue中沒有Message,並處於Idle狀態,則會執行IdelHandler介面中的方法,往往用於做一些清理性地工作。
訊息分發的優先順序:
  1. Message的回撥方法:message.callback.run(),優先順序最高;
  2. Handler的回撥方法:Handler.mCallback.handleMessage(msg),優先順序僅次於1;
  3. Handler的預設方法:Handler.handleMessage(msg),優先順序最低。
訊息快取: 為了提供效率,提供了一個大小為50的Message快取佇列,減少物件不斷建立與銷燬的過程。