Android Handler機制之消息池的擴展 SimplePool與SynchronizedPool

消息池.gif

該文章屬於《Android Handler機制之》系列文章,若是想了解更多,請點擊 《Android Handler機制之總目錄》android

前言

在上篇文章《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方法。關於這兩個方法的詳細流程會在下文具體介紹。bash

關於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。less

關於release方法

在release方法中,會將對象緩存到對象池中。若是當前對象已經存在,會拋出異常。反之則存儲。具體列子以下圖所示: ide

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

總結:release( T instance)方法,總會將須要回收的對象存入當前對象池中存儲的最後一個數據的下一個位置。若是當前回收的對象已經存在會拋出異常。反之則成功。post

同步對象池(SynchronizedPool)

在前面的文章中咱們介紹了SimplePool的存取數據的主要實現。細心的小夥伴確定都已經發現了。在多線程的狀況下,若是使用SimplePool確定是會出現問題的。可是Google已經爲咱們考慮到了,爲咱們提供了線程安全的對象池SynchronizedPool,下面咱們就來看看SynchronizedPool的具體實現。具體代碼以下ui

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);
      }
 }
複製代碼

總結

  • 對於頻繁建立的對象,能夠考慮使用對象池。
  • 實現對象池的方式有幾種,能夠採用數組的形式,也能夠採用鏈表的形式。
  • 在實現對象池緩存對象時,須要考慮到線程安全的問題。該加鎖就加鎖。
相關文章
相關標籤/搜索