複数のAsyncTaskを同時に実行する—不可能ですか?


259

2つのAsyncTaskを同時に実行しようとしています。(プラットフォームはAndroid 1.5、HTC Heroです。)ただし、最初にのみ実行されます。これが私の問題を説明する簡単なスニペットです:

public class AndroidJunk extends Activity {
 class PrinterTask extends AsyncTask<String, Void, Void> {
     protected Void doInBackground(String ... x) {
      while (true) {
       System.out.println(x[0]);
       try {
        Thread.sleep(1000);
       } catch (InterruptedException ie) {
        ie.printStackTrace();
       }
      }
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        new PrinterTask().execute("bar bar bar");
        new PrinterTask().execute("foo foo foo");

        System.out.println("onCreate() is done.");
    }
}

私が期待する出力は:

onCreate() is done.
bar bar bar
foo foo foo
bar bar bar
foo foo foo

等々。しかし、私が得るものは:

onCreate() is done.
bar bar bar
bar bar bar
bar bar bar

2番目のAsyncTaskは実行されません。execute()ステートメントの順序を変更すると、fooタスクのみが出力を生成します。

ここに明らかな何かが欠けていたり、愚かなことをしていますか?2つのAsyncTaskを同時に実行することはできませんか?

編集:問題の電話がAndroid 1.5を実行していることに気付き、問題の説明を更新しました。それに応じて。Android 2.1を実行しているHTC Heroではこの問題は発生しません。うーん...


あなたのコードは私にとってはうまくいくので、問題はどこかにあるはずです。LogCatビューにフィルターを入力しましたか?;-)
mreichelt 2010年

うーん、それは変だ。logcatにはフィルタリングがありません。1.6も使用していますか?もしそうなら、どの電話?
ロディオン2010年

おっと、ちょうどそれが実行されていることに気づきました(古代)Android 1.5
ロディオン

1
ターゲットとしてAndroid 1.6を使用し、Android 2.1エミュレーターを使用しました。したがって、問題がAndroid 1.5のみのHTCヒーローで本当に発生する場合-それらを台無しにしても、問題ありません。;-) HTC Heroにはすでに新しいAndroidバージョンへのアップデートがあります。物事を台無しにするメーカーがいくつかあれば、私はそれについて気にしないでしょう。さらに、Android 1.5についてもう気になりません。
mreichelt 2010年

AsyncTaskは、5 msのより短い期間のタスクに使用する必要があります。ThreadPoolExecutor(developer.android.com/reference/java/util/concurrent/…)に移動します。関連のポスト:stackoverflow.com/questions/6964011/...
ラビンドラバブー

回答:


438

AsyncTaskは、doInBackground()からの実行にスレッドプールパターンを使用します。この問題は、最初(初期のAndroid OSバージョン)のプールサイズが1であったことを意味し、一連のAsyncTaskの並列​​計算がないことを意味します。しかし、後で修正し、サイズは5になりました。最大5つのAsyncTaskを同時に実行できます。残念ながら、どのバージョンで変更されたのか正確には覚えていません。

更新:

これは、現在の(2012-01-27)AP​​Iがこれについて言っていることです。

最初に導入されたとき、AsyncTasksは単一のバックグラウンドスレッドで順次実行されました。DONUTから、これはスレッドのプールに変更され、複数のタスクが並行して動作できるようになりました。HONEYCOMBの後で、並列実行によって引き起こされる一般的なアプリケーションエラーを回避するために、これを単一スレッドに戻すことが計画されています。本当に並列実行が必要な場合は、THREAD_POOL_EXECUTORでこのメソッドのexecuteOnExecutor(Executor、Params ...)バージョンを使用できます。ただし、その使用に関する警告については、解説を参照してください。

DONUTはAndroid 1.6、HONEYCOMBはAndroid 3.0です。

更新:2

kabukoからのコメントを参照してくださいMar 7 2012 at 1:27

「複数のタスクを並行して実行できるスレッドのプール」が使用されているAPI(1.6から始まり、3.0で終わる)の場合、同時に実行されるAsyncTasksの数は、実行のために渡されたタスクの数によって異なりますが、doInBackground()まだ終わっていません。

これは、2.2でテスト/確認済みです。で1秒スリープするカスタムAsyncTaskがあるとしますdoInBackground()。AsyncTasksは、遅延したタスクを格納するために内部で固定サイズのキューを使用します。キューのサイズはデフォルトで10です。15個のカスタムタスクを続けて開始すると、最初の5個がに入るdoInBackground()が、残りはキュー内で空きワーカースレッドを待機します。最初の5つのいずれかが終了してワーカースレッドが解放されるとすぐに、キューからのタスクが実行を開始します。したがって、この場合、最大5つのタスクが同時に実行されます。ただし、16個のカスタムタスクを続けて開始すると、最初の5個がに入りdoInBackground()、残りの10個がキューに入れられますが、16日目は新しいワーカースレッドが作成されるため、すぐに実行が開始されます。したがって、この場合、最大6つのタスクが同時に実行されます。

同時に実行できるタスクの数には制限があります。以来AsyncTask用途ワーカースレッド(128)と、アプリがでクラッシュします以上138個のカスタムタスクを実行しようとした場合、キューは、サイズ10を固定した遅延タスクの限定された最大数のスレッドプールエグゼキュータjava.util.concurrent.RejectedExecutionException

3.0以降のAPIでは、AsyncTask.executeOnExecutor(Executor exec, Params... params)メソッドを介してカスタムスレッドプールエグゼキューターを使用できます。これにより、たとえば、デフォルトの10が必要でない場合に、遅延タスクキューのサイズを設定できます。

@Knossosが言及しているように、AsyncTaskCompat.executeParallel(task, params);APIレベルに煩わされることなくタスクを並行して実行するために、サポートv.4ライブラリから使用するオプションがあります。このメソッドは、APIレベル26.0.0で廃止されました。

更新:3

これは、シリアル実行とパラレル実行のタスクの数を試す簡単なテストアプリです。https//github.com/vitkhudenko/test_asynctask

更新:4(これを指摘してくれた@penkzhouに感謝)

Android 4.4以降のAsyncTask動作は、UPDATE:2セクションで説明したものとは異なります。あまりにも多くのスレッドを作成しないようにする修正がありAsyncTaskます。

Android 4.4(API 19)より前のバージョンにAsyncTaskは、次のフィールドがありました。

private static final int CORE_POOL_SIZE = 5;
private static final int MAXIMUM_POOL_SIZE = 128;
private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(10);

Android 4.4(API 19)では、上記のフィールドは次のように変更されています。

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 BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(128);

この変更により、キューのサイズが128アイテムに増加し、スレッドの最大数がCPUコアの数* 2 + 1に減少します。アプリは引き続き同じ数のタスクを送信できます。


11
参考までに、コアプールのサイズは5ですが、最大は128です。つまり、キューがいっぱいになった場合、5を超える(最大128)同時に実行できます。
kabuko 2012年

2
@kabuko:あなたは絶対的に正しいです。これを2.2で調査し、5つのタスクが既に実行されている(コアプールサイズが5)場合、残りのタスクを遅延タスクのキューに入れ始めますが、キューがすでにいっぱいの場合(最大キューサイズは10) )、その後、タスクの追加のワーカースレッドを開始して、タスクをすぐに開始します。
Vit Khudenko、2012年

1
ちょうど私の日を保存しました、キャプションヒーロー:)ありがとう
Karoly

2
新しいcompat関数については、この回答を更新してください。例:AsyncTaskCompat.executeParallel(task, params);
クノッソ2016

1
@Arhimedください、AsyncTaskCompat.executeParallel(task, params);現在非推奨になっている
Kirill Starostin

218

これにより、API 4+(Android 1.6+)を使用するすべてのAndroidバージョンで並列実行が可能になります。

@TargetApi(Build.VERSION_CODES.HONEYCOMB) // API 11
void startMyTask(AsyncTask asyncTask) {
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
        asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
    else
        asyncTask.execute(params);
}

これはArhimedの優れた答えの要約です。

プロジェクトのビルドターゲットとして、APIレベル11以上を使用していることを確認してください。Eclipseでは、それはProject > Properties > Android > Project Build Targetです。これにより、下位APIレベルへの下位互換性が失われることはありません後で導入された機能を誤って使用すると、Lintエラーが発生しますのでご安心くださいminSdkVersion。以降に導入された機能を本当に使用したい場合はminSdkVersion、アノテーションを使用してそれらのエラーを抑制できますが、その場合は互換性について自分で注意する必要があります。これはまさに上記のコードスニペットで起こったことです。


3
TimerTask.THREAD_POOL_EXECUTORをAsynTask.THREAD_POOL_EXECUTORに変更する必要があった場合、この例は機能します
Ronnie

2
問題を無効にするには、呼び出しをジェネリックにすることができ void startMyTask(AsyncTask<Void, ?, ?> asyncTask) ます(doInBackgroundがVoidの場合)
Peter

クラスがAsyncTask <Void、Integer、Void>を拡張する場合は、上記のコードをさらに簡略化できます。それは素晴らしいスライのおかげでした。
Peter Arandorenko、2014年

1
どうもありがとう。Webサーバーからデータを読み取るために2つの非同期タスクを同時に実行する必要があるシナリオがありましたが、サーバーが受信するURLは1つだけで、その特定の要求が完了しない限り、2番目のAsyncTaskのURLはサーバーにヒットしません。あなたの答えが私の問題を解決しました:)
Santhosh Gutta 2014

1
私はこれに賛成投票を続けています、ありがとう。:)しかし、私は多くの人々のような優れたライブラリどこネットワーキングのためのAsyncTaskを使用しようとする印象きたバレーボールグライド闊歩がより良い選択ですが。AsyncTaskを使用する前に、これらを必ずチェックしてください:)
sulai

21

@sulaiの提案をより一般的なものにする:

@TargetApi(Build.VERSION_CODES.HONEYCOMB) // API 11
public static <T> void executeAsyncTask(AsyncTask<T, ?, ?> asyncTask, T... params) {
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
        asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
    else
        asyncTask.execute(params);
}   

より一般的なソリューション:AsyncTaskCompat.executeParallel(new MyAsyncTask、myprams);
Vikash Kumar Verma 2017

11

@sulaiの非常に優れた要約の@Arhimedの完全な回答に最新の更新(UPDATE 4)を含めるだけです。

void doTheTask(AsyncTask task) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // Android 4.4 (API 19) and above
        // Parallel AsyncTasks are possible, with the thread-pool size dependent on device
        // hardware
        task.execute(params);
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // Android 3.0 to
        // Android 4.3
        // Parallel AsyncTasks are not possible unless using executeOnExecutor
        task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
    } else { // Below Android 3.0
        // Parallel AsyncTasks are possible, with fixed thread-pool size
        task.execute(params);
    }
}

わあ、すごい。これは.executeOnExecutor()、API> = KITKATに冗長になることを意味しますね。
Taslim Oseni


3

可能です。私のAndroidデバイスのバージョンは4.0.4で、android.os.Build.VERSION.SDK_INTは15です

3つのスピナーがあります

Spinner c_fruit=(Spinner) findViewById(R.id.fruits);
Spinner c_vegetable=(Spinner) findViewById(R.id.vegetables);
Spinner c_beverage=(Spinner) findViewById(R.id.beverages);

また、Async-Tackクラスもあります。

これが私のスピナーロードコードです

RequestSend reqs_fruit = new RequestSend(this);
reqs_fruit.where="Get_fruit_List";
reqs_fruit.title="Loading fruit";
reqs_fruit.execute();

RequestSend reqs_vegetable = new RequestSend(this);
reqs_vegetable.where="Get_vegetable_List";
reqs_vegetable.title="Loading vegetable";
reqs_vegetable.execute();

RequestSend reqs_beverage = new RequestSend(this);
reqs_beverage.where="Get_beverage_List";
reqs_beverage.title="Loading beverage";
reqs_beverage.execute();

これは完全に機能しています。スピナーを1つずつロードしました。executeOnExecutorは使用しませんでした。

これが私の非同期タスククラスです

public class RequestSend  extends AsyncTask<String, String, String > {

    private ProgressDialog dialog = null;
    public Spinner spin;
    public String where;
    public String title;
    Context con;
    Activity activity;      
    String[] items;

    public RequestSend(Context activityContext) {
        con = activityContext;
        dialog = new ProgressDialog(activityContext);
        this.activity = activityContext;
    }

    @Override
    protected void onPostExecute(String result) {
        try {
            ArrayAdapter<String> adapter = new ArrayAdapter<String> (activity, android.R.layout.simple_spinner_item, items);       
            adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
            spin.setAdapter(adapter);
        } catch (NullPointerException e) {
            Toast.makeText(activity, "Can not load list. Check your connection", Toast.LENGTH_LONG).show();
            e.printStackTrace();
        } catch (Exception e)  {
            Toast.makeText(activity, "Can not load list. Check your connection", Toast.LENGTH_LONG).show();
            e.printStackTrace();
        }
        super.onPostExecute(result);

        if (dialog != null)
            dialog.dismiss();   
    }

    protected void onPreExecute() {
        super.onPreExecute();
        dialog.setTitle(title);
        dialog.setMessage("Wait...");
        dialog.setCancelable(false); 
        dialog.show();
    }

    @Override
    protected String doInBackground(String... Strings) {
        try {
            Send_Request();
            } catch (NullPointerException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        return null;
    }

    public void Send_Request() throws JSONException {

        try {
            String DataSendingTo = "http://www.example.com/AppRequest/" + where;
            //HttpClient
            HttpClient httpClient = new DefaultHttpClient();
            //Post header
            HttpPost httpPost = new HttpPost(DataSendingTo);
            //Adding data
            List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);

            nameValuePairs.add(new BasicNameValuePair("authorized","001"));

            httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
            // execute HTTP post request
            HttpResponse response = httpClient.execute(httpPost);

            BufferedReader reader;
            try {
                reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
                StringBuilder builder = new StringBuilder();
                String line = null;
                while ((line = reader.readLine()) != null) {
                    builder.append(line) ;
                }

                JSONTokener tokener = new JSONTokener(builder.toString());
                JSONArray finalResult = new JSONArray(tokener);
                items = new String[finalResult.length()]; 
                // looping through All details and store in public String array
                for(int i = 0; i < finalResult.length(); i++) {
                    JSONObject c = finalResult.getJSONObject(i);
                    items[i]=c.getString("data_name");
                }

            } catch (ClientProtocolException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2
それらが順番に実行されないことをどのようにして確認しますか?
2018年

@behelitいつ動作するかは重要ですか?詳細は、のために受け入れ答え読みくださいstackoverflow.com/a/4072832/2345900
Sajitha Rathnayake

2
すみません、よくわかりません。非同期がシーケンシャルであるか、本当に非同期であるかを判断できない場合は、問題になります。また、リンクされた回答では、executeonexecutorメソッドを使用するように指示されていますが、これは実際には回答で使用されていません。私が何かを逃した場合は申し訳ありません。
ベヘリット

1
「スピナーが1つずつ読み込まれました。」これは文字通り、これは並列実行ではなく逐次実行であると自分で言いました。AsyncTasksのそれぞれがキューに追加され、順次処理されます。これはOPの質問には答えません。
Joshua Pinter

これは非常に古い答えです。これがうまくいかない場合は、承認された回答を参照してください
Sajitha Rathnayake

3

タスクを並列で実行する場合はexecuteOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "your task name")、Androidバージョン3.0以降でメソッドを呼び出す必要があります。ただし、このメソッドは単独で並列実行されるため、Android 3.0以前および1.6以降には存在しません。異なるAndroidバージョンで例外がスローされないように、プロジェクトで独自のAsyncTaskクラスをカスタマイズすることをお勧めします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.