android.support.v4.util.Pools详解

在看VelocityTracker的源码时,我发现了一个SynchronizedPool对象,查看源代码发现了android.support.v4.util下Pools这个类,这里就分析一下流程、用途和源代码,方便对照使用。

###流程和用途

由于安卓系统硬件资源的限制,特别是对于需要通过native的方式调用资源,Android在调用时使用了Pools的子类——SimplePool和SynchronizedPool(这两个一个同步一个不同步)来优化,和线程池类似。

首先需要定义池的大小并创建,此时池中都是Null对象,并通过acquire()取出该位置、赋值并在池中减小一个位置,调用完毕后通过release()返还赋值过的对象并在池中增加一个位置,方便复用。当位置被取光(即mPool.length == 0),再次acquire()时会抛出空对象;当位置被占满(即mPoolSize < mPool.length)时,该对象不会被添加,并返回false。

由此可知一个对象内容较大占用资源较多,并且复用性较强且多处需要使用,可以使用Pools来限制使用规模。

###举例

public class MyPooledClass {

	...

    private static final SynchronizedPool sPool = new SynchronizedPool(10);

    public static MyPooledClass obtain() {
        MyPooledClass instance = sPool.acquire();
        return (instance != null) ? instance : new MyPooledClass();
    }

    public void recycle() {
         sPool.release(this);
    }
}

###源代码分析

//其实没什么好分析的 (雾

public final class Pools {

	...

    public static class SimplePool implements Pool {
        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() {
			//只有池大于0时候才能取
            if (mPoolSize > 0) {
				//将数组最后一位取出并强转对象,如果是第一次取就会是null对象
                final int lastPooledIndex = mPoolSize - 1;
                T instance = (T) mPool[lastPooledIndex];
				//清空当前位,节省内存
                mPool[lastPooledIndex] = null;
				//记录位置
                mPoolSize--;
                return instance;
            }
            return null;
        }

        @Override
        public boolean release(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(T instance) {
            for (int i = 0; i < mPoolSize; i++) {
                if (mPool[i] == instance) {
                    return true;
                }
            }
            return false;
        }
    }
	
	/**
	*	就是加了个同步锁的SimplePool
	*/
    public static class SynchronizedPool extends SimplePool {
		
		//创建锁对象,类似于synchronized(this),但是为什么不用synchronized(this)呢,下面会讲
        private final Object mLock = new Object();

        public SynchronizedPool(int maxPoolSize) {
            super(maxPoolSize);
        }

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

        @Override
        public boolean release(T element) {
            synchronized (mLock) {
                return super.release(element);
            }
        }
    }
}

Ps: 其中有一个小细节,在SynchronizedPool类中,创建了一个mLock对象,而不是使用synchronized(this),有多个原因:

  • 如果使用synchronized(this)这种代码块,整个对象所有加了锁的代码都使用同一把锁,如果调用者在外部的观察者中或是在死循环中调用带锁的方法,并由于业务需求给代码块加了锁,此时会导致死锁,外部带锁的方法则永远也不能被调用,如下:

      private boolean mIsRunning = false;
      private SynchronizedPool myPool;	
    
      public static void main(String [] args){
      	myPool = SynchronizedPool.acquire();
    
      	//无限循环检测mIsRunning是否被改变,一旦改变就退出循环
      	new Thread(new Runnable(){
      		synchronized(myPool){
      			while(!mIsRunning){
      				System.out.println("whether run or not");
      				//处理改变时候的逻辑
      			}
      		}
      	}).start;
      	
      	//假设有一个execute()方法,使用synchronized(this)来同步
      	myPool.execute(new Runnable()->{
      		//执行代码并改变mIsRunning
      		mIsRunning = true;
      	})
      }
    

假设Synchronized中有一段代码execute(Runnable runnable)用synchronized(this)来同步,由于isRun持有了对象锁,所以myPool.execute()一直在等锁释放,所以永远也改变不了mIsRunning,导致胡锁,那怎么避免这样的情况呢,在需要锁的类中定义一个Object作为锁对象即可,这样外部调用不到,则不会出现以上的状况。

  • 由于所有对象公用一把锁,所以其他的代码块需要等待锁释放,会降低效率

你可能感兴趣的:(AndroidApi)