android AsyncTask原理

9336 ワード

AndroidシステムはUIスレッドのリフレッシュを16 ms程度に維持しており、メインスレッドでいくつかの操作を行う場合は、ユーザーが操作中にカートンを感じないように時間を制限する必要があります.では、時間のかかる操作はメインスレッドで行わないほうがいいです.強引に操作するといろいろな問題がある可能性がありますので、サブスレッドで時間のかかる操作を実現し、メインスレッドにUIを更新するように通知する必要があります.前の記事では、スレッドを作成したり、スレッドプールを作成したりして、この機能を実現することができます.jdk 1で.5の時、1つのAsyncTaskのクラスを出して、このクラスは効果的に私達が上述の要求の機能を実現することを助けて、彼は比較的にいくつかの時間の比較的に短い任務に適用して、内部はスレッドの池をカプセル化して、実現の原理はFutureTask+Callable+SerialExecutor(線程の池)です.まずプロセス全体を説明すると、AsyncTaskの構築方法ではFutureオブジェクトとCallableオブジェクトが作成され、executeメソッドではonPreExecute()メソッドとdoInBackgroundメソッドが実行され、doInbackgroundの結果はMessageにカプセル化され、handlerによってスレッド間通信が行われ、このmessageを通過する.whatは、onProgressUpdateまたはfinishメソッドを呼び出す必要があるかどうかを識別します.finishメソッドではonPostExecuteメソッドが呼び出されます.また,publishProgress()メソッドによりonProgressUpdate()メソッドをアクティブに呼び出すことができ,内部でもこのメソッドによりonProgressUpdateを呼び出すようなメッセージを発行することができる.上のコード:
 class MyAsyncTask extends AsyncTask {
//     Params :     ,          doInBackground   
//     Progress: onProgressUpdate   ,          
//     Result : doInBackground          , onPostExecute   

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            //                 
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            //                       UI   
        }

        @Override
        protected Boolean doInBackground(Void... params) {
            //                 
            return null;
        }

        @Override
        protected void onPostExecute(Boolean aBoolean) {
            super.onPostExecute(aBoolean);
            //                        UI           UI   
        }
    }

使用するときは簡単です.上記の方法で需要を書き、直接呼び出します.
 new MyAsyncTask().execute();//     +                 

次に、ソースコードの分析(android 7.0ベース)を開始します.まず、AsyncTaskの構造関数を見てみましょう.
 /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     *                   ui  
     */
    public AsyncTask() {
      //    Callable  
        mWorker = new WorkerRunnable() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                Result result = doInBackground(mParams);
                Binder.flushPendingCommands();
                return postResult(result);
            }
        };
        //    FuntureTask         Callable         
        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);
                }
            }
        };
    }

executeメソッドを実行するには、次の手順に従います.
//  execute  :
//This method must be invoked on the UI thread.
    @MainThread
    public final AsyncTask execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);//         
    }
//            ,    
 private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;//       
 public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
//               AsyncTask      SerialExecutor 


executeOnExecutorメソッドを見てみましょう.
 @MainThread
    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(); //     onPreExecute           UI     
        mWorker.mParams = params;
        //  exec   SerialExecutor
        exec.execute(mFuture);//             futureTask
        return this;
    }

このexecuteがどのように呼び出されるか見てみましょう
  private static class SerialExecutor implements Executor {
        final ArrayDeque mTasks = new ArrayDeque();  //    ArrayDeque,            
        Runnable mActive;//    
     //  Runnable            mFuture  
        public synchronized void execute(final Runnable r) {
       //  offer  ,                  
            mTasks.offer(new Runnable() {
                public void run() {
                  //      
                    try {
                        r.run();//       mFuture   mWorker 
                      //     call      doInBackground  
                    } finally {
                        scheduleNext();
                    }
                }
            });
          //             null      scheduleNext  
            if (mActive == null) {
                scheduleNext();
            }
        }

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

以上、mWorkerのcallメソッドを再実行しました.このcallメソッドはサブスレッドで実行されていることがわかります.postResultメソッドを見てみましょう.
   private Result postResult(Result result) {
//   result     Message   ,  handler    
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult(this, result));
        message.sendToTarget();//  message     
        return result;
    }
//  handler
 private static Handler getHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            return sHandler;
        }
    }
//              Handler
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: //       publishProgress()       message
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }

最後にこのfinishメソッドを見てみましょう.
private void finish(Result result) {
        if (isCancelled()) {//   true         
            onCancelled(result);
        } else {//        
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

その後、handlerのMESSAGEを実行する方法を見てみましょう.POST_PROGRESSブランチができました.
@WorkerThread
    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult(this, values)).sendToTarget();
//       MESSAGE_POST_PROGRESS  ,  onProgressUpdate   。
        }
    }

プロセス全体が順調に進みました.最後に、現在実行中のタスクをキャンセルする方法を見てみましょう.
execute.cancel(true);//           
//                   ,  false         ,      
//                               
    public final boolean cancel(boolean mayInterruptIfRunning) {
        mCancelled.set(true);//   true,         value   1
        return mFuture.cancel(mayInterruptIfRunning);//  future   callable   
    }
//  value 1      finish       onCancelled()    ,          

まとめ:アプリケーション全体のすべてのAsyncTaskインスタンスは、同じSerialExecutorを共有します.彼はArrayDueueですべてのRunnableを管理しています.それでは、すべてのタスクは1つのキューの中にあります.彼はexecuteメソッドで、scheduleNextを通じて、1つ1つのキューからヘッダタスクを取り出して実行します.その後に追加されたタスクは、キューの最後に配置されます.全体的に見ると、このSerialExecutorは、機能的には単一のスレッドプールに属しており、多くのタスクを迅速に開始すると、同じ時点で1つのスレッドしか実行されず、残りは待機状態にあります.4.0以前のバージョンでは、ThreadPoolExecutorを直接使用しており、コアスレッド数が5、バススレッド数が128のスレッドプールが使用されていました.現在のAsyncTaskでは、スレッドプールをカスタマイズして、対応する操作を行うことができます.柔軟性が高いです.要するに、AsyncTaskも非同期メッセージ処理メカニズムを使用しており、非常に良いパッケージを作っただけです.