SerialExecutor クラスを詳しく見てみましょう。このクラスでは、最終的な
ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
これは実際には、異なるスレッドでの異なるリクエストのシリアライザーとして機能します。これは、Half Sync Half Async パターンの例です。
次に、シリアル エグゼキュータがこれを行う方法を調べてみましょう。次のように記述されている SerialExecutor のコードの部分を見てください。
if (mActive == null) {
scheduleNext();
}
そのため、Asynctask で execute が最初に呼び出されると、このコードがメイン スレッドで実行され (mActive が NULL に初期化されるため)、それによって scheduleNext() 関数が呼び出されます。ScheduleNext() 関数は次のように記述されています。
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
そのため、schedulenext() 関数では、デキューの最後に既に挿入した Runnable オブジェクトで mActive を初期化します。この Runnable オブジェクト (これは mActive に他なりません) は、スレッドプールから取得したスレッドで実行されます。そのスレッドで、「最後に」ブロックが実行されます。
現在、2 つのシナリオがあります。
別の AsyncTask インスタンスが作成され、最初のタスクが実行されているときに、そのインスタンスで execute メソッドを呼び出します。
Execute メソッドは、最初のタスクが実行されるときに、AsyncTask の同じインスタンスで 2 回目に呼び出されます。
シナリオ I : のexecute
関数を見るとSerialExecutor
、バックグラウンド タスクを処理するための新しい実行可能なスレッド (スレッド t とします) を実際に作成していることがわかります。そのスレッド t 内で、 の run 関数を実行しますmActive
。ただし、try ブロックにあるため、finally は、そのスレッドでバックグラウンド タスクが終了した後にのみ実行されます。(try と finally の両方が t のコンテキスト内で発生していることを思い出してください)。finally ブロック内で scheduleNext 関数を呼び出すと、mActive
既にキューを空にしているため、 は NULL になります。ただし、同じものの別のインスタンスが作成され、それらに対して execute を呼び出すと、executeの前に同期キーワードが指定されているため、AsyncTask
これらの execute 関数は実行されません。AsyncTask
SERIAL_EXECUTOR
は静的インスタンスです (したがって、同じクラスのすべてのオブジェクトは同じインスタンスを共有します... これはクラス レベルのロックの例です) つまり、同じ AsyncTask クラスのインスタンスは実行関数をプリエンプトできません (結果として、バックグラウンド タスクがスレッド t で実行されています)。つまり、タスクを実行しているアクティブなスレッドが 1 つしかないということです。このスレッドは、異なるタスクでは同じではないかもしれませんが、一度に 1 つのスレッドだけがタスクを実行します。したがって、最初のタスクが完了したときにのみ、後のタスクが次々に実行されます。これが と呼ばれる理由ですSerialExecutor
。
シナリオ II:この場合、例外エラーが発生します。execute 関数を同じ Asynctask オブジェクトで複数回呼び出すことができない理由を理解するには、以下のコード スニペットexecuteOnExecutor
のAsyncTask.java
特に以下の部分を参照してください。
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)");
}
}
上記のコード スニペットから明らかなように、タスクが実行中の状態にあるときに execute 関数を 2 回呼び出すと、「タスクを実行できません: タスクは既に実行されています。」という IllegalStateException がスローされます。
AsyncTask 内部に関する私の議論を読むことができます
https://docs.google.com/document/d/1_zihWXAwgTAdJc013-bOLUHPMrjeUBZnDuPkzMxEEj0/edit?usp=sharing