うさがにっき

読書感想文とプログラムのこと書いてきます

AndroidバージョンでのAsyncTaskの注意点

概要

AsyncTaskのAndroidバージョンによる違いをまとめる

AsyncTaskには以下の二つの実行モードがある

  • THREAD_POOL_EXECUTOR
  • SERIAL_EXECUTOR
API Level11未満 THREAD_POOL_EXECUTORのみ
API Level11以上 THREAD_POOL_EXECUTOR、SERIAL_EXECUTOR(デフォルト)

詳細

AsyncTaskはExecutorによってAsyncTaskを複数実行したときの挙動が決まる
API11以降ではTHREAD_POOL_EXECUTOR, SERIAL_EXECUTORが選べ、デフォルトはSERIAL_EXECUTOR
API10まではTHREAD_POOL_EXECUTORしか使えないので、ICS用などで開発してGBなどで見るとAsyncTaskの挙動が違うときがある

THREAD_POOL_EXECUTORでは平行して処理を行う
API11以降のTHREAD_POOL_EXECUTOR

// CPU数
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// threadPool最小数
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
// threadPool最大数
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
// 新しく作ったスレッドを破棄するまでの時間
private static final int KEEP_ALIVE = 1;

// 新しくスレッドを作るときに使う、threadFactory
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
    private final AtomicInteger mCount = new AtomicInteger(1);

    public Thread newThread(Runnable r) {
        return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
    }
};

// AsynkTaskが使う内部queue
private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(128);

public static final Executor THREAD_POOL_EXECUTOR
    = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

SERIAL_EXECUTORでは平行して処理せず、1つずつ処理を行う
SERIAL_EXECUTOR

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
…
// デフォルトはSERIAL_EXECUTOR
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
…
private static class SerialExecutor implements Executor {
	final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
	Runnable mActive;

	public synchronized void execute(final Runnable r) {
	    mTasks.offer(new Runnable() {
	        public void run() {
	            try {
	                r.run();
	            } finally {
	                scheduleNext();
	            }
	        }
	    });
	    if (mActive == null) {
	        scheduleNext();
	    }
	}

	protected synchronized void scheduleNext() {
	    if ((mActive = mTasks.poll()) != null) {
	        THREAD_POOL_EXECUTOR.execute(mActive);
	    }
	}
}

この二つのExecutorをAPI11以降では使い分けられるが、API11未満ではTHREAD_POOL_EXECUTORしか使えない
API11未満のAsyncTaskの実装

private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
        MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);

…
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
if (mStatus != Status.PENDING) {
    switch (mStatus) {
        case RUNNING:
            throw new IllegalStateException("Cannot execute task:"
                    + " the task is already running.");
        case FINISHED:
            throw new IllegalStateException("Cannot execute task:"
                    + " the task has already been executed "
                    + "(a task can be executed only once)");
    }
}

mStatus = Status.RUNNING;

onPreExecute();

mWorker.mParams = params;
sExecutor.execute(mFuture);

API11以上のAsyncTaskの実装

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
…
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
…
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}
…
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,Params... params) {
    if (mStatus != Status.PENDING) {
        switch (mStatus) {
            case RUNNING:
                throw new IllegalStateException("Cannot execute task:"
                        + " the task is already running.");
            case FINISHED:
                throw new IllegalStateException("Cannot execute task:"
                        + " the task has already been executed "
                        + "(a task can be executed only once)");
        }
    }

    mStatus = Status.RUNNING;

    onPreExecute();

    mWorker.mParams = params;
    exec.execute(mFuture);

    return this;
}

つまりAPI11未満では並列処理が行われるが、API11以上ではserial処理でAsyncTaskが実行されるということ
API11以上でも並列処理がしたい場合は以下のようにする

MyAsyncTask task = new AsyncTask();
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
	task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"msg");
}else {
	task.execute("msg");
}

API11未満でSERIAL実行したい時には独自Executorを作成する必要がある