Android開発AsyncTaskソース分析【テンプレートメソッドモード】
テンプレートメソッドの説明
テンプレートメソッドは、アルゴリズムのフレームワークを定義し、フレームワークのいくつかのステップをサブクラスに遅延させて実装し、サブクラスがアルゴリズムのフレームワーク構造を変更せずにフレームワークのいくつかのステップを再定義できるようにします.テンプレートメソッドは実際には固定されたプロセスをカプセル化し、プロセスのステップは抽象クラスで定義されており、サブクラスは異なるアルゴリズムで実現することができ、フレームワークプロセスが修正されずにアルゴリズムの置換を実現することができる.
AsyncTaskのテンプレートメソッド
AsyncTaskを使用するには、AsyncTaskオブジェクトを作成し、executeメソッドを呼び出すだけです.executeメソッドは固定フローをカプセル化しており,サブクラスはこのメソッドを修正できないため,executeメソッドはfinalであり,書き換えられてはいけない.AsyncTaskは固定フローを抽象的にカプセル化しただけであり,具体的なステップはサブクラスによって決定されるため,AsyncTaskクラスは抽象クラスとして定義される.executeを呼び出すと、onPreExecute、doInBackground、onPostExecuteの固定プロセスがトリガーされます.これらのステップの具体的な実装はサブクラスによって決定されなければならないが、実行の順序タイミングはテンプレートメソッドによって決定されており、サブクラスは変更できない.
protected abstract Result doInBackground(Params… params); protected void onPreExecute() {} protected void onPostExecute(Result result) {} protected void onProgressUpdate(Progress… values) {
AsyncTaskソース分析
executeメソッドは固定されたプロセスをカプセル化しており、executeメソッドを呼び出してこれらのプロセスを自動的にトリガーする必要があります.
executeを呼び出すと内部でexecuteOnExecuterメソッドが呼び出されます
executeOnExecuterメソッドはまず非同期タスクの状態をチェックし,非同期タスクは一度しか実行できないため,RUNNING状態とFINISHED状態呼び出しexecuteメソッドは不正であり,異常を投げ出す.上記のステータスチェックに合格した後、非同期タスクステータスをRUNNINGに設定し、onPreExecuteメソッドを呼び出していくつかの準備を行います.これは最初の固定ステップです.次にexecuteメソッドから渡されたパラメータをmWorkerに渡し,mWorkerはWorkerRunnableであり,Callableインタフェースを実現し,パラメータをカプセル化した.(しばらくは関係なく、以下で分析する)
mWorkerの初期化はコンストラクション関数で初期化されます
タスク処理のcallメソッドでResult result=doInBackground(mParams)が呼び出されます.ここでmWorkerはスレッドタスクを定義するだけで、まだ実行されていません.実行されるとdoInBackgroundメソッドが呼び出されます.これは2番目の固定ステップです.次に、mWorkerがスレッドプールにどのように配置されて実行されるかを見ます.mWorkerにパラメータを設定した後、executeOnExecuteorメソッドはスレッドプールのexecuteメソッドを呼び出します.
exec.execute(mFuture);
execはexecuteOnExecutorメソッドパラメータが渡されたスレッドプールオブジェクトsDefaultExecutorであり、sDefaultExecutorは主にタスクのキューを担当する
ここでexecはsDefaultExecutorのデフォルトのスレッドプールSerialExecutorであり、ネーミングから見ればタスクを順次実行するスレッドプールであるはずです
SerialExecutor内部では、1つのArrayDequeを使用して複数のタスクを管理し、ArrayDequeの双方向順序ストレージ、スタックとキューメソッドの統合、アクセスが速く、挿入と削除が遅い.SerialExecuterのexecute(final Runnable)メソッドを呼び出すと、ArrayDequeキューの末尾にタスクrを追加し、scheduleNext()メソッドを呼び出し、scheduleNext()メソッドはArrayDequeキューヘッダからRunnableタスクを取り出し、THREAD_POOL_EXECUTORスレッドプールで実行します.上のコードからSerialExecutorがタスクのキューのみを担当していることがわかります.THREAD_POOL_EXECUTORこそ本格的なスレッドプール
AsyncTask内部マルチタスク操作は、ThreadPoolExecutorスレッドプールによって実現され、THREAD_POOL_EXECUTORスレッドプールは、コアスレッド数がCPUコア数に等しく、非コアスレッド数がCPUコア数+1に等しく、スレッドプール容量がCPUコア数*2+1に等しく、空スレッドタイムアウトが1秒で空スレッドを閉じるスレッドプールである.
AsyncTaskのexecuteOnExecutorメソッドに戻り、最後に2つの文があります.
mWorker.mParams = params; exec.execute(mFuture);
この2つのスパンは比較的大きいので,mWorkerからmFutureへの変換の場所を見つける必要があり,AsyncTask構造関数でmFutureを定義した.
このようにmWorkerを渡すと、mWorkerはcallableインタフェースを実現し、executorメソッドが渡すパラメータをカプセル化するので、mWorkerは実際にcallableであり、FutureTaskが実行済みのときにdoneメソッドをコールし、doneメソッドの中でgetを通じてスレッドの実行結果を取得し、postResultIfNotInvoked(get()に渡す.
ここでは、mFutureをスレッドプール処理に追加し、mFutureはcallableをパッケージしています.このcallableはmWorkerです.そのため、スレッドプールで処理されるタスクはmWorkerのcallメソッドです.
PostResultメソッドでHandlerのobtainMessageでメッセージを取得
Message obtainMessage(int what,Object obj)このMessageのwhatフィールドをMESSAGE_に設定POST_RESULT,objフィールドはnew AsyncTaskResult(this,result)オブジェクト
AsyncTaskResultオブジェクトには、メッセージ処理の最終結果またはメッセージ処理の進捗値を表すことができる、AsyncTaskオブジェクトと汎用不定長パラメータがカプセル化されています.
これにより、計算結果は、UIスレッドであるhandlerが存在するスレッドに送られる.getHandlerメソッドは、単一のインスタンスモードでInternalHandlerを作成します.
InternalHandlerのメッセージ処理方法
InternalHandlerはUIスレッドにバインドされています
super(Looper.getMainLooper());
メッセージのwhatフィールドがMESSAGEである場合POST_RESULTは、タスクが完了したことを示すので、onPostExecuteを呼び出すべきです.ここではまず、AsyncTaskResultを保存するMessageオブジェクトのobjフィールドを取得します.
従って、メッセージ処理方法においてmTask(AsyncTask)のfinishメソッドが呼び出される
このタスクがキャンセルされていない場合はonPostExecute(result)メソッドをコールバックし、タスクがキャンセルされている場合はonCancelled(result)メソッドをコールバックします.これは3番目の固定ステップです
テンプレートメソッドは、アルゴリズムのフレームワークを定義し、フレームワークのいくつかのステップをサブクラスに遅延させて実装し、サブクラスがアルゴリズムのフレームワーク構造を変更せずにフレームワークのいくつかのステップを再定義できるようにします.テンプレートメソッドは実際には固定されたプロセスをカプセル化し、プロセスのステップは抽象クラスで定義されており、サブクラスは異なるアルゴリズムで実現することができ、フレームワークプロセスが修正されずにアルゴリズムの置換を実現することができる.
AsyncTaskのテンプレートメソッド
AsyncTaskを使用するには、AsyncTaskオブジェクトを作成し、executeメソッドを呼び出すだけです.executeメソッドは固定フローをカプセル化しており,サブクラスはこのメソッドを修正できないため,executeメソッドはfinalであり,書き換えられてはいけない.AsyncTaskは固定フローを抽象的にカプセル化しただけであり,具体的なステップはサブクラスによって決定されるため,AsyncTaskクラスは抽象クラスとして定義される.executeを呼び出すと、onPreExecute、doInBackground、onPostExecuteの固定プロセスがトリガーされます.これらのステップの具体的な実装はサブクラスによって決定されなければならないが、実行の順序タイミングはテンプレートメソッドによって決定されており、サブクラスは変更できない.
protected abstract Result doInBackground(Params… params); protected void onPreExecute() {} protected void onPostExecute(Result result) {} protected void onProgressUpdate(Progress… values) {
AsyncTaskソース分析
executeメソッドは固定されたプロセスをカプセル化しており、executeメソッドを呼び出してこれらのプロセスを自動的にトリガーする必要があります.
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
executeを呼び出すと内部でexecuteOnExecuterメソッドが呼び出されます
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;
}
executeOnExecuterメソッドはまず非同期タスクの状態をチェックし,非同期タスクは一度しか実行できないため,RUNNING状態とFINISHED状態呼び出しexecuteメソッドは不正であり,異常を投げ出す.上記のステータスチェックに合格した後、非同期タスクステータスをRUNNINGに設定し、onPreExecuteメソッドを呼び出していくつかの準備を行います.これは最初の固定ステップです.次にexecuteメソッドから渡されたパラメータをmWorkerに渡し,mWorkerはWorkerRunnableであり,Callableインタフェースを実現し,パラメータをカプセル化した.(しばらくは関係なく、以下で分析する)
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
mWorkerの初期化はコンストラクション関数で初期化されます
mWorker = new WorkerRunnable<Params, Result>() {
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);
}
};
タスク処理のcallメソッドでResult result=doInBackground(mParams)が呼び出されます.ここでmWorkerはスレッドタスクを定義するだけで、まだ実行されていません.実行されるとdoInBackgroundメソッドが呼び出されます.これは2番目の固定ステップです.次に、mWorkerがスレッドプールにどのように配置されて実行されるかを見ます.mWorkerにパラメータを設定した後、executeOnExecuteorメソッドはスレッドプールのexecuteメソッドを呼び出します.
exec.execute(mFuture);
execはexecuteOnExecutorメソッドパラメータが渡されたスレッドプールオブジェクトsDefaultExecutorであり、sDefaultExecutorは主にタスクのキューを担当する
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
ここでexecはsDefaultExecutorのデフォルトのスレッドプールSerialExecutorであり、ネーミングから見ればタスクを順次実行するスレッドプールであるはずです
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);
}
}
}
SerialExecutor内部では、1つのArrayDequeを使用して複数のタスクを管理し、ArrayDequeの双方向順序ストレージ、スタックとキューメソッドの統合、アクセスが速く、挿入と削除が遅い.SerialExecuterのexecute(final Runnable)メソッドを呼び出すと、ArrayDequeキューの末尾にタスクrを追加し、scheduleNext()メソッドを呼び出し、scheduleNext()メソッドはArrayDequeキューヘッダからRunnableタスクを取り出し、THREAD_POOL_EXECUTORスレッドプールで実行します.上のコードからSerialExecutorがタスクのキューのみを担当していることがわかります.THREAD_POOL_EXECUTORこそ本格的なスレッドプール
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
AsyncTask内部マルチタスク操作は、ThreadPoolExecutorスレッドプールによって実現され、THREAD_POOL_EXECUTORスレッドプールは、コアスレッド数がCPUコア数に等しく、非コアスレッド数がCPUコア数+1に等しく、スレッドプール容量がCPUコア数*2+1に等しく、空スレッドタイムアウトが1秒で空スレッドを閉じるスレッドプールである.
AsyncTaskのexecuteOnExecutorメソッドに戻り、最後に2つの文があります.
mWorker.mParams = params; exec.execute(mFuture);
この2つのスパンは比較的大きいので,mWorkerからmFutureへの変換の場所を見つける必要があり,AsyncTask構造関数でmFutureを定義した.
mFuture = new FutureTask<Result>(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 occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
このようにmWorkerを渡すと、mWorkerはcallableインタフェースを実現し、executorメソッドが渡すパラメータをカプセル化するので、mWorkerは実際にcallableであり、FutureTaskが実行済みのときにdoneメソッドをコールし、doneメソッドの中でgetを通じてスレッドの実行結果を取得し、postResultIfNotInvoked(get()に渡す.
exec.execute(mFuture);
ここでは、mFutureをスレッドプール処理に追加し、mFutureはcallableをパッケージしています.このcallableはmWorkerです.そのため、スレッドプールで処理されるタスクはmWorkerのcallメソッドです.
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);
}
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
PostResultメソッドでHandlerのobtainMessageでメッセージを取得
Message obtainMessage(int what,Object obj)このMessageのwhatフィールドをMESSAGE_に設定POST_RESULT,objフィールドはnew AsyncTaskResult(this,result)オブジェクト
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
AsyncTaskResultオブジェクトには、メッセージ処理の最終結果またはメッセージ処理の進捗値を表すことができる、AsyncTaskオブジェクトと汎用不定長パラメータがカプセル化されています.
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
これにより、計算結果は、UIスレッドであるhandlerが存在するスレッドに送られる.getHandlerメソッドは、単一のインスタンスモードでInternalHandlerを作成します.
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
InternalHandlerのメッセージ処理方法
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;
}
}
}
InternalHandlerはUIスレッドにバインドされています
super(Looper.getMainLooper());
メッセージのwhatフィールドがMESSAGEである場合POST_RESULTは、タスクが完了したことを示すので、onPostExecuteを呼び出すべきです.ここではまず、AsyncTaskResultを保存するMessageオブジェクトのobjフィールドを取得します.
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
従って、メッセージ処理方法においてmTask(AsyncTask)のfinishメソッドが呼び出される
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
このタスクがキャンセルされていない場合はonPostExecute(result)メソッドをコールバックし、タスクがキャンセルされている場合はonCancelled(result)メソッドをコールバックします.これは3番目の固定ステップです