言いたいことはそれだけか

JavaとかAndroidとか調べたことをメモします。٩( 'ω' )و

AsyncTaskのStatusを理解したくてFrameworkのコードを読んだメモ

RX全盛期のいま、こんなことに需要があるのか…ということは気にしない。

AsyncTaskのライフサイクルのstatusがぼんやりとしか理解できずググっても出てこなかったので調べたメモ。 具体的な疑問としては、taskの実行が終わったあと、初期状態(PENDING)に戻るのか?それともFINISH のまま?という点。 APIにもこのように書いてあるんだけどなんとなく釈然としない。taskのlifetimeってどういうことなんだろう。

Each status will be set only once during the lifetime of a task.

それではFrameworkのコードを読んでいきます。 読んだコードは6.0のもの。

初期値

当然ながら初期値は PENDING.
synchronizeよりコストの低いvolatileを使ってるんですね。へぇぇ。

    private volatile Status mStatus = Status.PENDING;

タスクの実行

タスクの実行命令がくるとまずは状態チェック。
PENDING 以外は許容しません。
チェックが終わるとRUNNING 状態に遷移して、onPreExecute() が呼ばれます。

    @MainThread
     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;
     }

タスクの完了・キャンセル

AsyncTaskは内部でhandlerを持っておき、タスクが完了もくしはキャンセルされた時に終了処理を実行するためのMessage(MESSAGE_POST_RESULT)を投げます。

    private static class InternalHandler extends Handler {
        public InternalHandler() {
            super(Looper.getMainLooper());
        }

        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @Override
        public void handleMessage(Message msg) {
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
            switch (msg.what) {
                case MESSAGE_POST_RESULT:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }

ここで呼ばれるfinish()の中で状態が FINISHED になります。 このあとはもう状態操作を行わないので、FINISH のまま。

    private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

ということでわかったのは

  • onPreExecute()の直前にRUNNINGになって、onCancelled()onPostExecute()が完了したらFINISHEDになる
  • statusは循環するわけではなく、一方通行。一度FINISHになったらそこから遷移はしない
  • メンバ変数として持っておいて何度もそのままタスクを使い回すのも無理そう

まぁActivityのonPause()とかでcancelするためにメンバで参照保持しないとなんですけどね。
executeする前に状態チェックして FINISH だったらnewしなおしてやるということで。

最近読んだAsyncTaskのメモリリークの話も一緒にどうぞ。

medium.com