AsyncTaskの原理解析
11971 ワード
前言
定義#テイギ#
Params,Progress,Resultの3つの汎用パラメータを提供する抽象的な汎用クラス. a.Params:非同期タスクの実行を開始するときに入力されるパラメータタイプ.excute()で渡されるパラメータ に対応する. b.Progress:非同期タスク実行中に実行する進捗値のタイプ c.Result:非同期タスクの実行が完了すると、doInBackground()の戻り値タイプと一致する結果タイプが返されます.
コアメソッド
ソースコードの方式の方式を結び付けて
1.execute
手動呼び出し非同期タスクの実行を開始するには、UIスレッドで呼び出す必要があります.
まず
2.onPreExecute
構成方法では、前述した
3.doInBackground
抽象的な方法は、カスタムスレッドタスクを書き換え、
Handlerを利用してメッセージを送ります.MESSAGE_POST_PROGRESSフラグはUIを更新し、メッセージを受信するとメインスレッドで
4.onProgressUpdate
メインスレッドで実行し、バックグラウンドの進捗が変更された場合に呼び出され、書き換えを選択できます.
以上、doInBackground()
同様にメッセージを送信し、前にHandlerコードが貼られており、今回は
論理は簡単で、
5.onPostExecute
メインスレッドで実行すると、
6.onCancelled()
メインスレッドで実行すると、非同期タスクがキャンセルされるとonCancelled()が呼び出され、このときonPostExecuteは呼び出されません.
重要クラス
この2つのクラス
1.
ここでのCallableもタスクであり,Runnableとの違いは
2.
実は1つのタスクパッケージクラスで、中にはCallableが含まれていて、いくつかの状態標識を追加して、Callableのインタフェースを操作します.
3.THREAD_POOL_EXECUTOR
前述の
シリアルorパラレル
Android 1.6以前、
注意点
1ライフサイクルについて 結論 使用推奨
2メモリリークについて結論 を引き起こす.使用推奨 3スレッドタスクの実行結果が失われましたは、 を更新することができないと結論する.は、 を再起動する.
AsyncTask
は一般的にカプセル化された非同期タスククラスであり、バックグラウンドタスクをより容易に実行し、メインスレッドを切り替えてUIを更新することができる.実装上、Thread
(スレッドプール)およびHandler
がカプセル化されている.定義#テイギ#
Params,Progress,Resultの3つの汎用パラメータを提供する抽象的な汎用クラス.
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
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つのクラス
WorkerRunnable
とFutureTask
については、前述したように 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
前述の
SerialExecutor
のscheduleNext()
メソッドでは、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
またはFragment
でAsyncTask
を使用する場合、Activity
またはFragment
のonDestory()
でcancel(boolean)
を呼び出すことが望ましい.2メモリリークについて
AsyncTask
がActivity
の非静的内部クラスと宣言すると、Activity
が破棄する必要がある場合、AsyncTask
がActivity
への参照を保持しているため、Activity
が回収できず、最終的にメモリ漏洩AsyncTask
Activity
として宣言すべき静的内部クラスActivity
が再作成すると(画面回転/Activity
が予期せぬ破棄された場合に回復)、以前に実行するAsyncTask
(非静的な内部クラス)が保有していた前のActivity
参照が無効になるため、複写されたonPostExecute()
は有効にならず、UI操作Activity
のリカバリ時に推奨対応する方法を使用して、タスクスレッド