Handler的前世今生3——Message

1. Message的重要屬性

Message文檔

Each Handler has its own name-space for message codes, so you do not need to worry about yours conflicting with other handlers. 每個Handler都有自己命名空間的消息碼,這樣不必擔心和其它Handler衝突。

每個Handler只處理自家的Message, 但Message是如何區分Handler呢?

這就需要我們考慮去將MessageHandler 進行綁定。

我們查看Message的源碼,會發現有一個成員變量:

Handler target;

當我們查看Message中各個obtain()方法,都會發現與target進行綁定(特殊的obtain()可以進一步查看Handler,其最終也是要對target進行綁定的,這裏不再展開)。

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

除了target ,還有:

Runnable callback

public long when;

關於Handler的更多詳情 Handler的前世今生4——Handler


2. Message 的obtain()

官方建議我們在發送消息儘量使用obtain(),我們來看一下obtain()的實現:

從全局的消息池中給我們返回一個新的Message實例,很多情況下可以避免我們新建對象。

 /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();

我們通過進一步的分析Message的成員變量:

Message next;
private static Message sPool;
public static final Object sPoolSync = new Object(); // 全局鎖對象
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;

這就告訴我們,Message是鏈表設計Message池的最大數量爲50.


3. Message的回收

FLAG_IN_USE 標記消息在使用中,當消息進入隊列或者複用時都被被置爲該狀態。

 /** If set message is in use.
     * This flag is set when the message is enqueued and remains set while it
     * is delivered and afterwards when it is recycled.  The flag is only cleared
     * when a new message is created or obtained since that is the only time that
     * applications are allowed to modify the contents of the message.
     *
     * It is an error to attempt to enqueue or recycle a message that is already in use.
     */
    static final int FLAG_IN_USE = 1 << 0;

recyclerUnchecked()方法是用來複用消息的。這裏主要就是對消息的屬性恢復默認,其判斷消息池是否溢出。我們在Looper的loop()方法看過它的身影。

 /**
     * Recycles a Message that may be in-use.
     * Used internally by the MessageQueue and Looper when disposing of queued Messages.
     */
    @UnsupportedAppUsage
    void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = UID_NONE;
        workSourceUid = UID_NONE;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

我們還看到一個recycle() 方法:

 public void recycle() {
        if (isInUse()) {
            if (gCheckRecycle) {
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        recycleUnchecked();
    }

有沒有似曾相識的感覺,你在處理Bitmap時…(PS:Bitmap類中也有一個recycle()方法)。在涉及到複用的情況下,基本都會出現該方法的身影。

爲什麼Looper的loop()方法調用的是recycleUnchecked()呢?

個人認爲是:loop()中可以保證消息一定沒有在使用,完全沒有必要判斷isInUse(),所以直接調用了recycleUnchecked()。

OK ,關於Message的內容就介紹到這裏…

歡迎繼續收看 Handler的前世今生4 —— Handler

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章