Android進階學習——Message複用機制原理

最近在學習Android消息機制方面的原理,到了Message這塊的時候發現少有文章進行分析,本着“生死看淡不服就幹”的原則,我深入了源碼層面,對Message巧妙的複用機制進行了解析,並寫下這篇文章,供大家分享學習,如果感覺對你有幫助,歡迎點贊,私信,關注我,您的鼓勵是對我最大的幫助。

在日常的Android開發中,我們經常會使用Handler來進行主子線程之間的消息傳遞,其中我們用的最多的就是Message對象。通過查閱官方文檔或者代碼註釋我們可以發現,對於Message,官方建議我們使用Message.obtain來進行Message對象的獲取。

那麼問題來了,爲什麼Google會建議我們使用Message.obtain來進行對象的獲取而不是讓我們自己new一個Message出來呢?或者說,通過Message.obtain來獲取Message對象有沒有什麼好處呢?

其實答案都在源碼裏了,談到這裏,我們就不得不提一種設計模式——享元模式,這是一種和單例設計模式很相似的設計模式,具體的細節在這裏就不講了,感興趣的話歡迎移步菜鳥教程——享元模式

下面還是讓我們從源碼開始吧,Message複用原理的入口在Message.obtain中:

/**
        從Message池中返回一個Message對象,並且允許分配新的實例
     */
    public static Message obtain() {
        //先是加了一把對象鎖,這個鎖是使用Object來實現的
        synchronized (sPoolSync) {
            if (sPool != null) {
                //如果Message池中非空,就從池子裏取出一個Message
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        //如果Message池爲空,就新建一個Message對象出來
        return new Message();
    }

在obtain的代碼中,我們可以看到,先是判斷了一把Message Pool是不是爲空,如果非空的話就從池子裏取出之前用過的Message進行復用,並且異步地對池子裏 Message的數量進行了更新。但是如果Message池爲空的話說明還沒有Message被放進去,那麼就new出來一個Message對象好了。

到這裏,僅僅是對之前使用過的Message進行了取出,或者新建一個Message的動作,還沒有看到Message到底是怎麼被回收的,別急,讓我們移步Message的recycle方法。

public void recycle() {
        if (isInUse()) {
            if (gCheckRecycle) {
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        //最終調用到了這個方法
        recycleUnchecked();
    }

    /**
     * Recycles a Message that may be in-use.
     * Used internally by the MessageQueue and Looper when disposing of queued Messages.
        這句話的意思是:一般來說這個方法是被內部的MessageQueue和Looper在分發排序的Message時候來使用的
     */
    void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        //先把這個Message標記爲使用狀態,防止被二次回收
        //然後把這個Message的相關狀態進行了手動復位
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;
        //Message的複用機制使用了鏈表數據結構來實現
        //但神奇的是這個鏈表沒有藉助任何一種數據結構如LinkedList等實現,而是通過指針的形式,將不        
        //同的Message對象進行了串聯
        //在這裏,先是判斷了一把,如果當前Message池子的大小還沒到限制值
        //那麼就把當前Message池子的頭指向了當前Message實例的sPool字段,也就是Message池的頭        
        //部,當然,next字段和Message池子都屬於Message類型
        //並且更新了池子的大小
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

從代碼中我們可以看到,Message的複用機制沒有使用任何一種數據結構,如LinkedArrayList,而是通過Message對象內部的spool和next字段,通過指針的方式來進行對象管理,不得不說,是一種非常巧妙的設計方式,一來降低了設計複雜度,而且由於沒有創建額外的數據容器來管理對象,減輕了內存的壓力,實現了輕量化的目的。

具體的實現過程我找到了一幅圖來進行說明:

在Message池初始狀態下,如果一個Message來到了池子裏,他肯定先是被清空了數據,然後將他的sPool字段指向了Message池的頭部,而next字段指向了空,當第二個Message被回收進來之後,m2的next字段指向了m1,原來m1的next字段仍然保持不變,而Message池子的頭部也就是sPool指向了m2。

這樣週而復始地循環,也就完成了Message的鏈表,進而實現了Message的複用目的。

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