Android Handler機制之訊息池的擴充套件 SimplePool與SynchronizedPool

AndyJennifer發表於2018-09-22

訊息池.gif

該文章屬於《Android Handler機制之》系列文章,如果想了解更多,請點選 《Android Handler機制之總目錄》

前言

在上篇文章《Android Handler機制之Message及Message回收機制 》我們講解了Message中所攜帶的資訊及訊息池中的實現方式。其中我們已經瞭解了Message中訊息池是以連結串列的形式來完成。在完成了上篇文章後,我就一直想在Java或Android中是否已經為我們提供了一種物件池來幫助我們來實現快取物件的實現呢。果真在Android中的android.support.v4.util下的Pools類就為我們提供了SimplePool、SynchronizedPool來建立物件池。下面我就對該包下的類進行講解。

Android中提供的物件池

在android.support.v4.util包下的Pools類中,分別宣告瞭Pool介面,SimplePool實現類與SynchronizedPool實現類,其中具體的UML關係如下圖所示:

繼承關係.png
在上圖中,Pool的具體實現類為SimplePool,而SynchronizedPool為SimplePool的子類。

簡單物件池(SimplePool)

在討論了具體的UML關係後,現在我們來看看SimplePool的程式碼實現,具體程式碼如下圖所示:

  public static class SimplePool<T> implements Pool<T> {
        private final Object[] mPool;//儲存物件的陣列
        private int mPoolSize;//當前物件池中的物件個數
		
        public SimplePool(int maxPoolSize) {
            if (maxPoolSize <= 0) {
                throw new IllegalArgumentException("The max pool size must be > 0");
            }
            mPool = new Object[maxPoolSize];//初始化物件池的最大容量
        }
		//從物件池中獲取資料
        @Override
        @SuppressWarnings("unchecked")
        public T acquire() {
            if (mPoolSize > 0) {
                final int lastPooledIndex = mPoolSize - 1;
                T instance = (T) mPool[lastPooledIndex];
                mPool[lastPooledIndex] = null;
                mPoolSize--;//當前物件池中物件個數減1
                return instance;
            }
            return null;
        }
		
		//回收當前物件到物件池中,
        @Override
        public boolean release(@NonNull T instance) {
            if (isInPool(instance)) {//如果物件池中已經有當前物件了,會丟擲異常
                throw new IllegalStateException("Already in the pool!");
            }
            if (mPoolSize < mPool.length) {
                mPool[mPoolSize] = instance;
                mPoolSize++;
                return true;
            }
            return false;
        }
		
		//判斷當前物件是否在物件池中
        private boolean isInPool(@NonNull T instance) {
            for (int i = 0; i < mPoolSize; i++) {
                if (mPool[i] == instance) {
                    return true;
                }
            }
            return false;
        }
    }
複製程式碼

對於SimplePool的程式碼其實很好理解,其物件池是以陣列的方式來實現的。其中物件池的最大容量是通過使用者手動設定。從物件池中獲取資料是通過acquire方法。回收當前物件到物件池中是通過release方法。關於這兩個方法的詳細流程會在下文具體介紹。

關於acquire方法

在acquire方法中,會從物件池中取出物件。具體列子如下圖所示:

acquire.png
在上圖中,當前物件池中儲存了10個object物件,當前sPoolSize = 10。當呼叫acquire()方法時,會獲取最後一個物件(也就是 mPool[9],將該物件取出後,會將該位置置為null(mPool9] =null),當前sPoolSize = 9。當再次呼叫acquire()方法時,會獲取mPool[8]位置下的物件。同理將該位置置為null,當前sPoolSize = 8。

總結:acquire()方法總會取當前物件池中儲存的最後一個資料。如果有則返回。同時將該位置置為null。反之返回為null。

關於release方法

在release方法中,會將物件快取到物件池中。如果當前物件已經存在,會丟擲異常。反之則儲存。具體列子如下圖所示:

realase.png
在上圖中。當前物件池儲存了8個object物件。當前sPoolSize = 8。當呼叫realse方法將object9物件回收到物件池中去時,會儲存在 mPool[8]位置下。且當前sPoolSize = 9,那麼當再次呼叫realse方法將object10物件放入時,會將object10物件儲存在mPool[9]位置下。

總結:release( T instance)方法,總會將需要回收的物件存入當前物件池中儲存的最後一個資料的下一個位置。如果當前回收的物件已經存在會丟擲異常。反之則成功。

同步物件池(SynchronizedPool)

在前面的文章中我們介紹了SimplePool的存取資料的主要實現。細心的小夥伴肯定都已經發現了。在多執行緒的情況下,如果使用SimplePool肯定是會出現問題的。但是Google已經為我們考慮到了,為我們提供了執行緒安全的物件池SynchronizedPool,下面我們就來看看SynchronizedPool的具體實現。具體程式碼如下

  public static class SynchronizedPool<T> extends SimplePool<T> {
        private final Object mLock = new Object();

        /**
         * Creates a new instance.
         *
         * @param maxPoolSize The max pool size.
         *
         * @throws IllegalArgumentException If the max pool size is less than zero.
         */
        public SynchronizedPool(int maxPoolSize) {
            super(maxPoolSize);
        }

        @Override
        public T acquire() {
            synchronized (mLock) {
                return super.acquire();
            }
        }

        @Override
        public boolean release(@NonNull T element) {
            synchronized (mLock) {
                return super.release(element);
            }
        }
    }
複製程式碼

SynchronizedPool的程式碼理解起來也同樣非常簡單,直接繼承SimplePool。並重寫了SimplePool的兩個方法。併為其加上了鎖,保證了多執行緒情況下使用的安全性。

物件池的使用

上面我們討論了兩種不同的物件池的實現,下面我們來看看對於這兩種物件池的使用。這裡就使用官方的SynchronizedPool的使用例子(這裡對SimplePool的使用也是通用的,根據是否需要多執行緒操作來選擇不同的物件池)。

  public class MyPooledClass {
	 
	  //宣告物件池的大小
      private static final SynchronizedPool<MyPooledClass> sPool =
              new SynchronizedPool<MyPooledClass>(10);
              
	  //從物件池中獲取資料,如果為null,則建立
      public static MyPooledClass obtain() {
         MyPooledClass instance = sPool.acquire();
        return (instance != null) ? instance : new MyPooledClass();
       }
	  
	  //回收物件到物件池中。當然你也可以清除物件的狀態
      public void recycle() {
           // 清除物件的狀態,如果你自己需要的話,
          sPool.release(this);
      }
 }
複製程式碼

總結

  • 對於頻繁建立的物件,可以考慮使用物件池。
  • 實現物件池的方式有幾種,可以採用陣列的形式,也可以採用連結串列的形式。
  • 在實現物件池快取物件時,需要考慮到執行緒安全的問題。該加鎖就加鎖。

相關文章