1. Qiita
  2. 投稿
  3. Android

Androidの主要画像系ライブラリのTread Poolをみる

  • 3
    いいね
  • 2
    コメント

Androidで画像の非同期読み込みでスレッド管理をしたくなったので、参考に調べた主要な画像系ライブラリのTread Pool周りのメモです。
調べ方は各ライブラリのレポジトリでExecutorThreadPoolExecutorなどで検索してヒットしたものを載せています。(詳しく読み込んでいないので、細かい動作による初期値の変化についてはあまり触れません)

※調べたのはExecutorを自分で設定しない場合のデフォルトの値です。

今回調べた対象は以下のライブラリです。

早見表

比較用にAsyncTaskも調べました。

picasso glide fresco AsyncTask
core pool size 1~4 1~ CPU数 3 2~4
maximum pool size 同上 同上 同上 1~ (CPU数 * 2 + 1)
work queue size 11 Integer.MAX_VALUE Integer.MAX_VALUE 128
queue PriorityBlockingQueue PriorityBlockingQueue LinkedBlockingQueue LinkedBlockingQueue

CPU数はRuntime.getRuntime().availableProcessors()の値です

上の表だけでは説明が不十分なのでそれぞれ少しだけ補足します。

picasso

参照クラスはこちら
PicassoExecutorService
picassoはデフォルトでは3ですが、WiFiでは4にするなど通信状況に応じてpool sizeを変更しています。

PicassoExecutorService.java

private static final int DEFAULT_THREAD_COUNT = 3;

 void adjustThreadCount(NetworkInfo info) {
    if (info == null || !info.isConnectedOrConnecting()) {
      setThreadCount(DEFAULT_THREAD_COUNT);
      return;
    }
    switch (info.getType()) {
      case ConnectivityManager.TYPE_WIFI:
      case ConnectivityManager.TYPE_WIMAX:
      case ConnectivityManager.TYPE_ETHERNET:
        setThreadCount(4);
        break;
      case ConnectivityManager.TYPE_MOBILE:
        switch (info.getSubtype()) {
          case TelephonyManager.NETWORK_TYPE_LTE:  // 4G
          case TelephonyManager.NETWORK_TYPE_HSPAP:
          case TelephonyManager.NETWORK_TYPE_EHRPD:
            setThreadCount(3);
            break;
          case TelephonyManager.NETWORK_TYPE_UMTS: // 3G
          case TelephonyManager.NETWORK_TYPE_CDMA:
          case TelephonyManager.NETWORK_TYPE_EVDO_0:
          case TelephonyManager.NETWORK_TYPE_EVDO_A:
          case TelephonyManager.NETWORK_TYPE_EVDO_B:
            setThreadCount(2);
            break;
          case TelephonyManager.NETWORK_TYPE_GPRS: // 2G
          case TelephonyManager.NETWORK_TYPE_EDGE:
            setThreadCount(1);
            break;
          default:
            setThreadCount(DEFAULT_THREAD_COUNT);
        }
        break;
      default:
        setThreadCount(DEFAULT_THREAD_COUNT);
    }
  }

Glide

参照クラスはこちら
FifoPriorityThreadPoolExecutor.java
GlideBuilder.java

3系以前はGlideExecutorというクラスでしたが、FifoPriorityThreadPoolExecutorに置き換わっています。

FifoPriorityThreadPoolExecutor.java
public FifoPriorityThreadPoolExecutor(int poolSize) {
        this(poolSize, UncaughtThrowableStrategy.LOG);
}

public FifoPriorityThreadPoolExecutor(int poolSize, UncaughtThrowableStrategy uncaughtThrowableStrategy) {
        this(poolSize, poolSize, 0, TimeUnit.MILLISECONDS, new DefaultThreadFactory(),
            uncaughtThrowableStrategy);
}

public FifoPriorityThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAlive, TimeUnit timeUnit,
            ThreadFactory threadFactory, UncaughtThrowableStrategy uncaughtThrowableStrategy) {
        super(corePoolSize, maximumPoolSize, keepAlive, timeUnit, new PriorityBlockingQueue<Runnable>(), threadFactory);
        this.uncaughtThrowableStrategy = uncaughtThrowableStrategy;
}

コンストラクタはこのようになっており、poolSize == maximumPoolSizeになるようですね。
このコンストラクタを呼び出しているのはGlideBuilderです。

GlideBuulder.java

Glide createGlide() {
        if (sourceService == null) {
            final int cores = Math.max(1, Runtime.getRuntime().availableProcessors());
            sourceService = new FifoPriorityThreadPoolExecutor(cores);
        }
        if (diskCacheService == null) {
            diskCacheService = new FifoPriorityThreadPoolExecutor(1);
        }
        
        return new Glide(engine, memoryCache, bitmapPool, context, decodeFormat);
}

sourceServiceが画像読み込みに使われるプールです。よってここでは1~ availableProcessorsの数ですね。

fresco

参照クラスはこちら
HttpUrlConnectionNetworkFetcher.java

frescoではファイルIOやデコード、バックグラウンド処理など細かくExecutorを分けており、探すのが大変でした…これが正確でない場合はコメントいただきたいです。スレッド数以外は特に指定していないのでExecutorServicenewFixedThreadPoolで指定される値が入るようです。

HttpUrlConnectionNetworkFetcher.java
private static final int NUM_NETWORK_THREADS = 3;

public HttpUrlConnectionNetworkFetcher() {
    this(Executors.newFixedThreadPool(NUM_NETWORK_THREADS));
  }

  @VisibleForTesting
  HttpUrlConnectionNetworkFetcher(ExecutorService executorService) {
    mExecutorService = executorService;
}

AsyncTask

※APIレベルによって挙動が異なるので詳しくはドキュメントを読んでください
https://developer.android.com/reference/android/os/AsyncTask.html

AsyncTask.java
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);

実装を見る限りとても便利ですが、使用する上でいくつか注意点はあります。
@hkurokawa さんのこちら資料が参考になりました。
http://www.slideshare.net/hiroshikurokawa79/asynctask


参考になる記事を見つけたら貼ります
モバイルアプリのスレッドプールサイズの最適化(画像読み込み編)