AsyncTaskの原理解析

11971 ワード

前言AsyncTaskは一般的にカプセル化された非同期タスククラスであり、バックグラウンドタスクをより容易に実行し、メインスレッドを切り替えてUIを更新することができる.実装上、Thread(スレッドプール)およびHandlerがカプセル化されている.
定義#テイギ#
Params,Progress,Resultの3つの汎用パラメータを提供する抽象的な汎用クラス.
  • a.Params:非同期タスクの実行を開始するときに入力されるパラメータタイプ.excute()で渡されるパラメータ
  • に対応する.
  • b.Progress:非同期タスク実行中に実行する進捗値のタイプ
  • c.Result:非同期タスクの実行が完了すると、doInBackground()の戻り値タイプと一致する結果タイプが返されます.
    public abstract class AsyncTask {
    }
    

    コアメソッド
    ソースコードの方式の方式を結び付けてAsyncTaskのいくつかの核心の方法を見てみます
    1.execute
    手動呼び出し非同期タスクの実行を開始するには、UIスレッドで呼び出す必要があります.
       public final AsyncTask execute(Params... params) {
            return executeOnExecutor(sDefaultExecutor, params);
        }
    
    executeOnExecutor()メソッドが内部的に呼び出されました
     public final AsyncTask 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;
        }
    

    まずonPreExecute()メソッドが呼び出され、その後exec.execute(mFuture)メソッドが呼び出されて実行が開始され、ここでexecはシリアルスレッドプールsDefaultExecutorであり、定義は一言も言わない.
     private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
     public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
         private static class SerialExecutor implements Executor {
            final ArrayDeque mTasks = new ArrayDeque();
            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);
                }
            }
        }
    
    AsyncTaskには、2つのスレッドプールSerialExecutor(タスクのキュー用)とTHREAD_POOL_EXECUTOR(タスクの実行用)がある.
    2.onPreExecute onPreExecuteメソッドに戻り、メインスレッドが実行され、非同期タスクが実行される前に実行され、いくつかの準備作業に使用されます.選択的に書き換えることができます.
       @MainThread
        protected void onPreExecute() {
        }
    
    AsyncTaskの構造方法を見てみましょう
       public AsyncTask(@Nullable Looper callbackLooper) {
            mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
                ? getMainHandler()
                : new Handler(callbackLooper);
    
            mWorker = new WorkerRunnable() {
                public Result call() throws Exception {
                    mTaskInvoked.set(true);
                    Result result = null;
                    try {
                        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                        //noinspection unchecked
                        result = doInBackground(mParams);
                        Binder.flushPendingCommands();
                    } catch (Throwable tr) {
                        mCancelled.set(true);
                        throw tr;
                    } finally {
                        postResult(result);
                    }
                    return result;
                }
            };
    
            mFuture = new FutureTask(mWorker) {
                @Override
                protected void done() {
                    try {
                        postResultIfNotInvoked(get());
                    } catch (InterruptedException e) {
                        android.util.Log.w(LOG_TAG, e);
                    } catch (ExecutionException e) {
                        throw new RuntimeException("An error occurred while executing doInBackground()",
                                e.getCause());
                    } catch (CancellationException e) {
                        postResultIfNotInvoked(null);
                    }
                }
            };
        }
    

    構成方法では、前述したexecuteOnExecutorの方法で説明したmWorkerおよびmFutureを初期化し、doInBackground()の方法を実行した後、postResult(result)の方法を実行した.次に、この2つの方法について説明します.
    3.doInBackground
        @WorkerThread
        protected abstract Result doInBackground(Params... params);
    

    抽象的な方法は、カスタムスレッドタスクを書き換え、Paramsパラメータを受け入れ、Result結果を返し、進捗情報を更新するためにpublishProgress()を呼び出すことができる.
        @WorkerThread
        protected final void publishProgress(Progress... values) {
            if (!isCancelled()) {
                getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                        new AsyncTaskResult(this, values)).sendToTarget();
            }
        }
    

    Handlerを利用してメッセージを送ります.MESSAGE_POST_PROGRESSフラグはUIを更新し、メッセージを受信するとメインスレッドでonProgressUpdate ()メソッドを呼び出し、スレッド切替操作を完璧に実現します.
        private static class InternalHandler extends Handler {
            public InternalHandler(Looper looper) {
                super(looper);
            }
    
            @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;
                }
            }
        }
    

    4.onProgressUpdate
    メインスレッドで実行し、バックグラウンドの進捗が変更された場合に呼び出され、書き換えを選択できます.
        @MainThread
        protected void onProgressUpdate(Progress... values) {
        }
    

    以上、doInBackground() postResult(result)の実行方法について説明する
        private Result postResult(Result result) {
            @SuppressWarnings("unchecked")
            Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                    new AsyncTaskResult(this, result));
            message.sendToTarget();
            return result;
        }
    

    同様にメッセージを送信し、前にHandlerコードが貼られており、今回はresult.mTask.finish(result.mData[0])メソッドが呼び出されます.に続く
        private void finish(Result result) {
            if (isCancelled()) {
                onCancelled(result);
            } else {
                onPostExecute(result);
            }
            mStatus = Status.FINISHED;
        }
    

    論理は簡単で、AsyncTaskが実行をキャンセルされた場合、onCancelled()が呼び出され、そうでなければonPostExecute()が呼び出され、doInBackground()の戻り結果がonPostExecute()メソッドに渡される.
    5.onPostExecute
    メインスレッドで実行すると、doInBackground()の実行結果がUIに表示されます.
        @MainThread
        protected void onPostExecute(Result result) {
        }
    

    6.onCancelled()
    メインスレッドで実行すると、非同期タスクがキャンセルされるとonCancelled()が呼び出され、このときonPostExecuteは呼び出されません.
        @MainThread
        protected void onCancelled() {
        }
    

    重要クラス
    この2つのクラスWorkerRunnableFutureTaskについては、前述したように
        private final WorkerRunnable mWorker;
        private final FutureTask mFuture;
    

    1. WorkerRunnable
     private static abstract class WorkerRunnable implements Callable {
            Params[] mParams;
        }
    

    ここでのCallableもタスクであり,Runnableとの違いはCallableに戻り値がある.
    2. FutureTask
        public FutureTask(Callable callable) {
            if (callable == null)
                throw new NullPointerException();
            this.callable = callable;
            this.state = NEW;       // ensure visibility of callable
        }
    

    実は1つのタスクパッケージクラスで、中にはCallableが含まれていて、いくつかの状態標識を追加して、Callableのインタフェースを操作します.
    3.THREAD_POOL_EXECUTOR
    前述のSerialExecutorscheduleNext()メソッドでは、THREAD_POOL_EXECUTOR.execute(mActive)が呼び出される
    /**
      *     :THREAD_POOL_EXECUTOR.execute()
      *   :
      *     a. THREAD_POOL_EXECUTOR    1                 
      *     b.   THREAD_POOL_EXECUTOR.execute()          execute()         
      *     c.           2    WorkerRunnable        call()
      *  :                  ,        2      call()
      */
    
        //   1:    
            //    CPU    
            private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
            //           2-4  ,     CPU  
            private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
            //             CPU  *2+1
            private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
            //             30s
            private static final int KEEP_ALIVE_SECONDS = 30;
    
            //       
            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());
                }
            };
    
            //           LinkedBlockingQueue      128
            private static final BlockingQueue sPoolWorkQueue =
                    new LinkedBlockingQueue(128);
    
        //   2:              ,  THREAD_POOL_EXECUTOR
        public static final Executor THREAD_POOL_EXECUTOR;
    
        static {
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                    CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                    sPoolWorkQueue, sThreadFactory);
            //                30s
            threadPoolExecutor.allowCoreThreadTimeOut(true);
            THREAD_POOL_EXECUTOR = threadPoolExecutor;
        }
    

    シリアルorパラレル
    Android 1.6以前、AsyncTaskはシリアルで実行する、Android 1.6以降はスレッドプールタスクを並行して実行し、Android 3.0からAsyncTask からシリアル実行が開始され、次々と実行される.もちろん、executeOnExecutor(Executor)を設けることによって、複数のAsyncTaskの並列化を実現することもできる.
    注意点AsyncTaskを使用する場合、いくつかの問題に注意する必要があります.
    1ライフサイクルについて
  • 結論AsyncTaskコンポーネントにバインドされないライフサイクル
  • 使用推奨ActivityまたはFragmentAsyncTaskを使用する場合、ActivityまたはFragmentonDestory()cancel(boolean)を呼び出すことが望ましい.

  • 2メモリリークについて
  • 結論AsyncTaskActivityの非静的内部クラスと宣言すると、Activityが破棄する必要がある場合、AsyncTaskActivityへの参照を保持しているため、Activityが回収できず、最終的にメモリ漏洩
  • を引き起こす.
  • 使用推奨AsyncTask Activityとして宣言すべき静的内部クラス
  • 3スレッドタスクの実行結果が失われました
  • は、Activityが再作成すると(画面回転/Activityが予期せぬ破棄された場合に回復)、以前に実行するAsyncTask(非静的な内部クラス)が保有していた前のActivity参照が無効になるため、複写されたonPostExecute()は有効にならず、UI操作
  • を更新することができないと結論する.
  • は、Activityのリカバリ時に推奨対応する方法を使用して、タスクスレッド
  • を再起動する.