Glide(Android)之普通对象池

在Glide的设计中应用了大量对象池的概念,我们就来学习一下对象池

Android对象池的支持

Android中为了方便使用,官方提供了一个工具类Pools,让大家可以方便的实现对象池,这里不对源码详细解释了,大家可以自己看,很简单。简单说一下实现:就是采用一个Object数组(Android 的Message对象池是采用链表实现的,大小是50个),数组大小外部指定,acquire方法获取对象池中一个对象,没有就返回null,release回收这个对象,前提是数组没满,存取都是对数组末尾操作的。

代码实现很简单也很纯粹,只负责存取,不负责创建对象和对象自身状态更新,后面我们会给使用的方式,到时就明白了。

对象池中对象的自身状态

我们知道可以从对象池中取(acquire)对象使用,对象使用完毕,可以把这个对象放到对象池中(release)。

有时对象的使用过程是复杂的或者生命周期很长,可能在某些操作的时候,这个对象被对象池回收了,但是在某些操作的时候,又用到这个对象,这样就会出问题。所以,对象本身要知道自己有没有被回收是很重要的,这就是我们要说的对象自身的验证。

对象除了要知道自己是否被回收,有时知道自己被回收了要做一些操作,比如抛出一个异常。

因为对象池可以放各种类型的对象,为了通用化,我们抽象出一个接口Poolable,这个接口提供方法几个方法,一个方法是让设置对象是否被回收了,另一个方法如果对象被回收了就做一些操作。放到对象池的的类,实现Poolable接口就好了。

还有一个问题,如果Poolable的方法里面都是一些相同的操作,不同的类实现了Poolable都要写一些一样的代码,这样会产生大量重复代码。这个时候就体现面向对象三大特性之一的“封装性”了。我们把Poolable里面的方法封装到另一个接口StateVerifier里面,StateVerifier可以有各种实现类,Poolable只需要对外暴露一个getVerifier方法。这样,对象回收相关的操作都被封装到StateVerifier里面了,同时还方便以后进行扩展。很棒的设计。

上面说的这些就是Glide里面Poolable和StateVerifier的设计思想,大家再看源码的时候,就很容易理解了。

使用方式

方式一:

Pools给的例子中有一种使用方式,和Message中对象池暴露的接口是一样的。就是对象池做为类字段static的,类方法obtain去对象池中获取对象,没有创建一个;对象方法recycle对对象进行回收。我们看一下demo

  public class MyPooledClass {
 
      private static final SynchronizedPool<MyPooledClass> sPool =
              new SynchronizedPool<MyPooledClass>(10);
 
      public static MyPooledClass obtain() {
          MyPooledClass instance = sPool.acquire();
          //这里更新对象自身的状态
          return (instance != null) ? instance : new MyPooledClass();
      }
 
      public void recycle() {
           // Clear state if needed.
           //这里更新对象自身的状态
           sPool.release(this);
      }
 
      . . .
  }

上面这种方式简单直观,可是有个问题,如果我们定制化对象创建的过程呢?上面的实现方案就很难做到,不够灵活了。

Glide给出了另一种方案,我们可以看看,主要类是FactoryPools,相关的接口和类都封装到了里面

方式二:

package com.bumptech.glide.util.pool;

import android.support.annotation.NonNull;
import android.support.v4.util.Pools.Pool;
import android.support.v4.util.Pools.SimplePool;
import android.support.v4.util.Pools.SynchronizedPool;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;

/**
 * Provides implementations of {@link Pool} never return {@code null}, log when new instances are
 * created, and that can use the {@link com.bumptech.glide.util.pool.FactoryPools.Poolable}
 * interface to ensure objects aren't used while inside the pool.
 */
public final class FactoryPools {
  private static final String TAG = "FactoryPools";
  private static final int DEFAULT_POOL_SIZE = 20;
  private static final Resetter<Object> EMPTY_RESETTER = new Resetter<Object>() {
    @Override
    public void reset(@NonNull Object object) {
      // Do nothing.
    }
  };

  private FactoryPools() { }

  /**
   * Returns a non-thread safe {@link Pool} that never returns {@code null} from
   * {@link Pool#acquire()} and that contains objects of the type created by the given
   * {@link Factory} with the given maximum size.
   *
   * <p>If the pool is empty when {@link Pool#acquire()} is called, the given {@link Factory} will
   * be used to create a new instance.
   *
   * @param <T> The type of object the pool will contains.
   */
  @NonNull
  public static <T extends Poolable> Pool<T> simple(int size, @NonNull Factory<T> factory) {
    return build(new SimplePool<T>(size), factory);
  }

  /**
   * Returns a new thread safe {@link Pool} that never returns {@code null} from
   * {@link Pool#acquire()} and that contains objects of the type created by the given
   * {@link Factory} with the given maximum size.
   *
   * <p>If the pool is empty when {@link Pool#acquire()} is called, the given {@link Factory} will
   * be used to create a new instance.
   *
   * @param <T> The type of object the pool will contains.
   */
  @NonNull
  public static <T extends Poolable> Pool<T> threadSafe(int size, @NonNull Factory<T> factory) {
    return build(new SynchronizedPool<T>(size), factory);
  }

  /**
   * Returns a new {@link Pool} that never returns {@code null} and that contains {@link List Lists}
   * of a specific generic type with a standard maximum size of 20.
   *
   * <p>If the pool is empty when {@link Pool#acquire()} is called, a new {@link List} will be
   * created.
   *
   * @param <T> The type of object that the {@link List Lists} will contain.
   */
  @NonNull
  public static <T> Pool<List<T>> threadSafeList() {
    return threadSafeList(DEFAULT_POOL_SIZE);
  }

  /**
   * Returns a new thread safe {@link Pool} that never returns {@code null} and that contains
   * {@link List Lists} of a specific generic type with the given maximum size.
   *
   * <p>If the pool is empty when {@link Pool#acquire()} is called, a new {@link List} will be
   * created.
   *
   * @param <T> The type of object that the {@link List Lists} will contain.
   */
  // Public API.
  @SuppressWarnings("WeakerAccess")
  @NonNull
  public static <T> Pool<List<T>> threadSafeList(int size) {
    return build(new SynchronizedPool<List<T>>(size), new Factory<List<T>>() {
      @NonNull
      @Override
      public List<T> create() {
        return new ArrayList<>();
      }
    }, new Resetter<List<T>>() {
      @Override
      public void reset(@NonNull List<T> object) {
        object.clear();
      }
    });
  }

  @NonNull
  private static <T extends Poolable> Pool<T> build(@NonNull Pool<T> pool,
      @NonNull Factory<T> factory) {
    return build(pool, factory, FactoryPools.<T>emptyResetter());
  }

  @NonNull
  private static <T> Pool<T> build(@NonNull Pool<T> pool, @NonNull Factory<T> factory,
      @NonNull Resetter<T> resetter) {
    return new FactoryPool<>(pool, factory, resetter);
  }

  @NonNull
  @SuppressWarnings("unchecked")
  private static <T> Resetter<T> emptyResetter() {
    return (Resetter<T>) EMPTY_RESETTER;
  }

  /**
   * Creates new instances of the given type.
   *
   * @param <T> The type of Object that will be created.
   */
  public interface Factory<T> {
    T create();
  }

  /**
   * Resets state when objects are returned to the pool.
   *
   * @param <T> The type of Object that will be reset.
   */
  public interface Resetter<T> {
    void reset(@NonNull T object);
  }

  /**
   * Allows additional verification to catch errors caused by using objects while they are in
   * an object pool.
   */
  public interface Poolable {
    @NonNull
    StateVerifier getVerifier();
  }

  private static final class FactoryPool<T> implements Pool<T> {
    private final Factory<T> factory;
    private final Resetter<T> resetter;
    private final Pool<T> pool;

    FactoryPool(@NonNull Pool<T> pool, @NonNull Factory<T> factory, @NonNull Resetter<T> resetter) {
      this.pool = pool;
      this.factory = factory;
      this.resetter = resetter;
    }

    @Override
    public T acquire() {
      T result = pool.acquire();
      if (result == null) {
        result = factory.create();
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
          Log.v(TAG, "Created new " + result.getClass());
        }
      }
      if (result instanceof Poolable) {
        ((Poolable) result).getVerifier().setRecycled(false /*isRecycled*/);
      }
      return result;
    }

    @Override
    public boolean release(@NonNull T instance) {
      if (instance instanceof Poolable) {
        ((Poolable) instance).getVerifier().setRecycled(true /*isRecycled*/);
      }
      resetter.reset(instance);
      return pool.release(instance);
    }
  }
}

主要看内部静态类FactoryPool,实现了Pool接口。其实说白了,就是复用了Pool的acquire和release方法,对真正的对象池进行了包装,这样我们就可以灵活的做自己想做的事了,比如,在acquire的采用外部传进来的工厂类创建对象,同时可以更新对象自身状态。这种包装技术在实际开发中很常用。

我们看看FactoryPool是怎么使用的

  static class EngineJobFactory {
    @Synthetic final GlideExecutor diskCacheExecutor;
    @Synthetic final GlideExecutor sourceExecutor;
    @Synthetic final GlideExecutor sourceUnlimitedExecutor;
    @Synthetic final GlideExecutor animationExecutor;
    @Synthetic final EngineJobListener listener;
    @Synthetic final Pools.Pool<EngineJob<?>> pool =
        FactoryPools.threadSafe(
            JOB_POOL_SIZE,
            new FactoryPools.Factory<EngineJob<?>>() {
              @Override
              public EngineJob<?> create() {
                return new EngineJob<>(
                    diskCacheExecutor,
                    sourceExecutor,
                    sourceUnlimitedExecutor,
                    animationExecutor,
                    listener,
                    pool);
              }
            });

    EngineJobFactory(
        GlideExecutor diskCacheExecutor,
        GlideExecutor sourceExecutor,
        GlideExecutor sourceUnlimitedExecutor,
        GlideExecutor animationExecutor,
        EngineJobListener listener) {
      this.diskCacheExecutor = diskCacheExecutor;
      this.sourceExecutor = sourceExecutor;
      this.sourceUnlimitedExecutor = sourceUnlimitedExecutor;
      this.animationExecutor = animationExecutor;
      this.listener = listener;
    }

    @VisibleForTesting
    void shutdown() {
      Executors.shutdownAndAwaitTermination(diskCacheExecutor);
      Executors.shutdownAndAwaitTermination(sourceExecutor);
      Executors.shutdownAndAwaitTermination(sourceUnlimitedExecutor);
      Executors.shutdownAndAwaitTermination(animationExecutor);
    }

    @SuppressWarnings("unchecked")
    <R> EngineJob<R> build(
        Key key,
        boolean isMemoryCacheable,
        boolean useUnlimitedSourceGeneratorPool,
        boolean useAnimationPool,
        boolean onlyRetrieveFromCache) {
      EngineJob<R> result = Preconditions.checkNotNull((EngineJob<R>) pool.acquire());
      return result.init(
          key,
          isMemoryCacheable,
          useUnlimitedSourceGeneratorPool,
          useAnimationPool,
          onlyRetrieveFromCache);
    }
  }

FactoryPool是被一个工厂对象应用的,这个工厂对象在Glide中是一个单例,只有这样对象池才有意义。EngineJob的acquire我们看到了,释放(回收)在哪里呢?FactoryPool传给了EngineJob,当EngineJob执行完自己的工作,就会释放

  private synchronized void release() {
    if (key == null) {
      throw new IllegalArgumentException();
    }
    cbs.clear();
    key = null;
    engineResource = null;
    resource = null;
    hasLoadFailed = false;
    isCancelled = false;
    hasResource = false;
    decodeJob.release(/*isRemovedFromQueue=*/ false);
    decodeJob = null;
    exception = null;
    dataSource = null;
    pool.release(this);
  }

SingleRequest也使用了对象池,也很简单,SingleRequest里面有个静态变量是FactoryPool。

Glide中Bitmap对象池地key,也采用了对象池,对象池基础数据结构是队列,看如下代码

package com.bumptech.glide.load.engine.bitmap_recycle;

import com.bumptech.glide.util.Util;
import java.util.Queue;

abstract class BaseKeyPool<T extends Poolable> {
  private static final int MAX_SIZE = 20;
  private final Queue<T> keyPool = Util.createQueue(MAX_SIZE);

  T get() {
    T result = keyPool.poll();
    if (result == null) {
      result = create();
    }
    return result;
  }

  public void offer(T key) {
    if (keyPool.size() < MAX_SIZE) {
      keyPool.offer(key);
    }
  }

  abstract T create();
}


  static class KeyPool extends BaseKeyPool<Key> {

    public Key get(int size, Bitmap.Config config) {
      Key result = get();
      result.init(size, config);
      return result;
    }

    @Override
    protected Key create() {
      return new Key(this);
    }
  }
static final class Key implements Poolable {
    private final KeyPool pool;

    @Synthetic int size;
    private Bitmap.Config config;

    public Key(KeyPool pool) {
      this.pool = pool;
    }

    @VisibleForTesting
    Key(KeyPool pool, int size, Bitmap.Config config) {
      this(pool);
      init(size, config);
    }

    public void init(int size, Bitmap.Config config) {
      this.size = size;
      this.config = config;
    }

    @Override
    public void offer() {
      pool.offer(this);
    }

    @Override
    public String toString() {
      return getBitmapString(size, config);
    }

    @Override
    public boolean equals(Object o) {
      if (o instanceof Key) {
        Key other = (Key) o;
        return size == other.size
            && Util.bothNullOrEqual(config, other.config);
      }
      return false;
    }

    @Override
    public int hashCode() {
      int result = size;
      result = 31 * result + (config != null ? config.hashCode() : 0);
      return result;
    }
  }
发布了189 篇原创文章 · 获赞 25 · 访问量 22万+

猜你喜欢

转载自blog.csdn.net/lizhongyisailang/article/details/104474017