同時に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を同時に実行することはできませんか?
編集:私は問題の携帯電話はアンドロイド1.5を実行して実現し、私は問題の詳細を更新しました。それに応じて。私はAndroid 2.1を実行しているHTCヒーローにはこの問題はありません。うーん...
AsyncTaskはdoInBackground()からのものを実行するためにスレッドプールパターンを使用します。問題は、初期の(初期のAndroid OSバージョンでは)プールサイズが1だったことです。これは、たくさんのAsyncTaskに対して並列計算が行われないことを意味します。しかし、後で彼らはそれを修正し、現在のサイズは5なので、最大5つのAsyncTaskを同時に実行できます。残念ながら、どのバージョンで彼らがそれを変更したのか正確には思い出せません。
更新:
これは現在の(2012-01-27)APIがこれについて言うものです:
最初に導入されたとき、AsyncTasksは単一のバックグラウンドスレッドでシリアルに実行されました。 DONUTから始めて、これはスレッドのプールに変更され、複数のタスクが並行して動作できるようになりました。 HONEYCOMBの後、並列実行によって引き起こされる一般的なアプリケーションエラーを回避するために、これをシングルスレッドに戻すことが計画されています。本当に並列実行したい場合は、THREAD_POOL_EXECUTORでこのメソッドのexecuteOnExecutor(Executor、Params ...)バージョンを使用できます。ただし、その使用に関する警告については、そこにある解説を参照してください。
DONUTはAndroid 1.6、HONEYCOMBはAndroid 3.0です。
更新日:2
Mar 7 at 1:27
のkabuko
によるコメントを参照してください。
「複数のタスクを並行して操作できるスレッドのプール」(1.6から3.0まで)が使用されているAPIでは、同時に実行されるAsyncTaskの数は、実行に渡されたタスクの数によって異なりますが、まだdoInBackground()
を終えていません。
これは私によって2.2でテスト/確認されています。 doInBackground()
に1秒だけスリープするカスタムAsyncTaskがあるとします。 AsyncTasksは遅延タスクを格納するために内部的に固定サイズのキューを使用します。キューサイズはデフォルトで10です。 15個のカスタムタスクを続けて開始すると、最初の5個はそれらのdoInBackground()
を入力しますが、残りは空きワーカースレッドを待ちます。最初の5つのいずれかが終了してワーカースレッドが解放されるとすぐに、キューからのタスクが実行を開始します。したがって、この場合は最大5つのタスクが同時に実行されます。ただし、16個のカスタムタスクを続けて開始すると、最初の5個がdoInBackground()
を入力し、残りの10個がキューに入りますが、16日目は新しいワーカースレッドが作成されるため、すぐに実行されます。したがって、この場合は最大6つのタスクが同時に実行されます。
同時に実行できるタスクの数には制限があります。 AsyncTask
はワーカースレッドの最大数(128)に制限のあるスレッドプールエグゼキュータを使用し、遅延タスクキューのサイズは10に固定されているため、138を超えるカスタムタスクを実行しようとするとJava.util.concurrent.RejectedExecutionException
でアプリがクラッシュします。
3.0からAPIはAsyncTask.executeOnExecutor(Executor exec, Params... params)
メソッドを通してあなたのカスタムスレッドプールエグゼキュータを使うことを可能にします。これにより、たとえば、デフォルトの10が必要でない場合、遅延タスクキューのサイズを設定できます。
@Knossosが述べているように、APIレベルを気にせずにタスクを並行して実行するためにsupport v.4ライブラリからAsyncTaskCompat.executeParallel(task, params);
を使うオプションがあります。このメソッドは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に削減されます。アプリは引き続き同じ数のタスクを送信できます。
これにより、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
です。 これはnotになり、下位のAPIレベルとの下位互換性がなくなります。 minSdkVersion
より後に導入された機能を誤って使用した場合でも、Lintエラーが発生します。 minSdkVersion
より後に導入された機能を本当に使用したい場合は、アノテーションを使用してこれらのエラーを抑制することができますが、その場合は互換性に注意する必要があります self 。これはまさに上記のコードスニペットで起こったことです。
@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);
}
@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);
}
}
Android開発者によるビットマップのロードの例では、カスタムのasynctask(jellybeanからコピー)を効率的に使用しているため、executeOnExecutorを11未満のapisで使用できます。
http://developer.Android.com/training/displaying-bitmaps/index.html
コードをダウンロードしてutilパッケージに行ってください。
可能です。私の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();
これは完璧に機能しています。私のスピナーが一つずつロードしました。 executeOnExecutorを使用しませんでした。
これが私のAsync-taskクラスです。
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();
}
}
}
タスクを並行して実行したい場合は、Androidバージョン3.0以降にexecuteOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "your task name")
メソッドを呼び出す必要があります。しかし、このメソッドは単独で並列実行されるため、Android 3.0以前および1.6以降には存在しません。したがって、異なるAndroidバージョンで例外がスローされないように、プロジェクト内で独自のAsyncTaskクラスをカスタマイズすることをお勧めします。