Birden fazla AsyncTasks'ı aynı anda çalıştırmak - mümkün değil mi?


259

Aynı anda iki AsyncTasks çalıştırmaya çalışıyorum. (Platform Android 1.5, HTC Hero.) Ancak, sadece ilk çalıştırılır. İşte sorunumu tanımlamak için basit bir pasaj:

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.");
    }
}

Beklediğim çıktı:

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

Ve bunun gibi. Ancak, ne olsun:

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

İkinci AsyncTask asla idam edilmez. Execute () ifadelerinin sırasını değiştirirsem, sadece foo görevi çıktı üretir.

Burada bariz bir şeyi mi kaçırıyorum ve / veya aptalca bir şey mi yapıyorum? İki AsyncTasks'ı aynı anda çalıştırmak mümkün değil mi?

Düzenleme: Ben söz konusu telefon Android 1.5 çalışır fark, descr güncelledi. buna göre. Android 2.1 çalıştıran bir HTC Hero ile bu sorunum yok. Hmmm ...


Kodunuz benim için çalışıyor, bu yüzden sorun başka bir yerde olmalı. LogCat görünümünüze bir filtre girdiniz mi? ;-)
mreichelt

Hm, bu çok garip. Logcat'te filtrelemem yok. 1.6 da mı kullanıyorsunuz? Varsa, hangi telefon?
rodion

Hata! Sadece çalıştığını fark etti (eski) Android 1.5
rodion

1
Android 1.6'yı hedef ve Android 2.1 öykünücüsü olarak kullandım. Yani sorun gerçekten sadece Android 1.5 yüklü bir HTC Hero'da ortaya çıkıyorsa - vidalayın, sorun yok. ;-) HTC Hero zaten daha yeni bir Android sürümüne sahip. Bazı şeyleri berbat eden üreticiler varsa, bu konuda rahatsız olmaz. Buna ek olarak, Android 1.5 hakkında artık sorun olmaz.
mreichelt

AsyncTask, 5 ms'lik daha kısa süreli görevler için kullanılmalıdır. ThreadPoolExecutor'a ( developer.android.com/reference/java/util/concurrent/… ) gidin. İlgili yazı: stackoverflow.com/questions/6964011/…
Ravindra babu

Yanıtlar:


438

AsyncTask, öğeleri doInBackground () öğesinden çalıştırmak için bir iş parçacığı havuzu deseni kullanır. Sorun başlangıçta (erken Android işletim sistemi sürümlerinde) havuz boyutu sadece 1 idi, bu da bir grup AsyncTasks için paralel hesaplamalar olmadığı anlamına geliyor. Ancak daha sonra bunu düzelttiler ve şimdi boyut 5, bu yüzden en fazla 5 AsyncTasks aynı anda çalışabilir. Ne yazık ki tam olarak hangi versiyonda bunu değiştirdiklerini hatırlamıyorum.

GÜNCELLEME:

Mevcut (2012-01-27) API'sı bu konuda şunları söylüyor:

İlk tanıtıldığında, AsyncTasks tek bir arka plan iş parçacığında seri olarak yürütüldü. DONUT ile başlayarak, bu, birden fazla görevin paralel olarak çalışmasına izin veren bir iş parçacığı havuzuna değiştirildi. HONEYCOMB sonrasında, paralel yürütmeden kaynaklanan yaygın uygulama hatalarından kaçınmak için bunu tek bir iş parçacığına geri döndürmek planlanmaktadır. Gerçekten paralel yürütme istiyorsanız, bu yöntemin executeOnExecutor (Executor, Params ...) sürümünü THREAD_POOL_EXECUTOR; ancak, kullanımıyla ilgili uyarılar için oradaki açıklamaya bakınız.

DONUT Android 1.6, HONEYCOMB Android 3.0'dır.

GÜNCELLEME: 2

Tarafından yoruma bakın kabukodan Mar 7 2012 at 1:27.

"Birden fazla görevin paralel olarak çalışmasına izin veren bir iş parçacığı havuzunun" kullanıldığı (1.6'dan başlayıp 3.0'da biten) API'larda, eşzamanlı olarak çalışan AsyncTasks sayısının, yürütme için zaten kaç görevin geçtiğine bağlı olduğu ortaya çıkar, ancak henüz bitirmediler doInBackground().

Bu benim tarafımdan 2.2'de test edildi / onaylandı. Bir saniye içinde uyuyan özel bir AsyncTask'ınız olduğunu varsayalım doInBackground(). AsyncTasks, gecikmeli görevleri depolamak için dahili olarak sabit boyutlu bir kuyruk kullanır. Kuyruk boyutu varsayılan olarak 10'dur. Özel görevlerinizi arka arkaya 15 olarak başlatırsanız, ilk 5 görevlerini girecek doInBackground(), ancak geri kalanı ücretsiz bir işçi iş parçacığı için kuyrukta bekleyecektir. İlk 5 işlemden herhangi biri biter ve böylece bir işçi iş parçacığını serbest bırakır bırakmaz, kuyruktaki bir görev yürütülmeye başlar. Bu durumda, en fazla 5 görev aynı anda çalışır. Bununla birlikte, özel görevlerinizi arka arkaya 16 olarak başlatırsanız, ilk 5 tanesi bunlara girecek doInBackground(), geri kalan 10 sıraya girecek, ancak 16'ncı için yeni bir iş parçacığı oluşturulacak, böylece hemen yürütmeye başlayacak. Bu durumda, en fazla 6 görev aynı anda çalışır.

Aynı anda kaç görevin çalıştırılabileceğinin bir sınırı vardır. Yana AsyncTaskkullanımlar çalışan iş parçacığı (128) ile kilitlenmesine fazla 138 özel görevleri uygulaması yürütmek çalışırsanız kuyruğu, boyutu 10 belirlemiştir gecikmiş görevlerin sınırlı maksimum sayı ile bir iş parçacığı havuzu vasisi java.util.concurrent.RejectedExecutionException.

3.0'dan başlayarak API, özel iş parçacığı havuzu yürütücünüzü AsyncTask.executeOnExecutor(Executor exec, Params... params)yöntemle kullanmanızı sağlar. Bu, örneğin, varsayılan 10 ihtiyacınız olan şey değilse, gecikmiş görev kuyruğunun boyutunu yapılandırmanıza izin verir.

@Knossos'un belirttiği gibi, AsyncTaskCompat.executeParallel(task, params);destek v.4 kitaplığından API düzeyi ile uğraşmadan görevleri paralel olarak çalıştırmak için bir seçenek vardır . Bu yöntem API seviyesi 26.0.0'da kullanımdan kaldırıldı.

GÜNCELLEME: 3

İşte seri veya paralel yürütme ile görevlerin sayısı ile oynamak için basit bir test uygulaması: https://github.com/vitkhudenko/test_asynctask

GÜNCELLEME: 4 (bunu işaret ettiğiniz için @penkzhou teşekkürler)

Android 4.4'ten başlayarak, GÜNCELLEME: 2 bölümünde AsyncTaskaçıklanandan farklı davranır . Çok fazla iş parçacığı oluşturmayı önlemek için bir düzeltme var .AsyncTask

Android 4.4'ten önce (API 19) AsyncTaskaşağıdaki alanlar vardı:

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'te (API 19) yukarıdaki alanlar şu şekilde değiştirilmiştir:

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);

Bu değişiklik, kuyruğun boyutunu 128 öğeye çıkarır ve CPU çekirdeği sayısının maksimum iş parçacığı sayısını azaltır * 2 + 1. Uygulamalar yine de aynı sayıda görev gönderebilir.


11
Sadece FYI, çekirdek havuz boyutu 5, ancak max 128'dir.
kabuko

2
@kabuko: kesinlikle haklısın. Bunu 2.2'de araştırdım ve zaten 5 görev çalışıyorsa (çekirdek havuz boyutu 5 ise), diğer görevleri gecikmiş görevler kuyruğuna koymaya başlar, ancak kuyruk zaten doluysa (maksimum kuyruk boyutu 10'dur) ), daha sonra görev için ek bir işçi iş parçacığı başlatır, böylece görev hemen başlar.
Vit Khudenko

1
az önce günümü kurtardı, altyazı kahraman :) teşekkür ederim
Karoly

2
Lütfen yeni uyumlu işlevler için bu yanıtı güncelleyin . Örnek:AsyncTaskCompat.executeParallel(task, params);
Knossos

1
@Arhimed Lütfen, AsyncTaskCompat.executeParallel(task, params);şu anda kullanımdan kaldırıldığı
Kirill Starostin

218

Bu, API 4+ (Android 1.6+) ile tüm android sürümlerinde paralel yürütmeye izin verir:

@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);
}

Bu Arhimed'in mükemmel cevabının bir özetidir.

Lütfen proje oluşturma hedefiniz olarak API düzey 11 veya üzerini kullandığınızdan emin olun. Eclipse'de bu Project > Properties > Android > Project Build Target. Bu olacak olmayan düşük API düzeyleri için geriye dönük uyumluluk bölünürler. Endişelenmeyin, yanlışlıkla daha sonra tanıtılan özellikleri kullanırsanız Lint hataları alırsınız minSdkVersion. Daha sonra sunulan özellikleri gerçekten kullanmak minSdkVersionistiyorsanız, ek açıklamaları kullanarak bu hataları engelleyebilirsiniz, ancak bu durumda uyumluluğa kendiniz dikkat etmeniz gerekir . Yukarıdaki kod snippet'inde olan tam olarak budur.


3
TimerTask.THREAD_POOL_EXECUTOR öğesini AsynTask.THREAD_POOL_EXECUTOR olarak değiştirmek zorunda kaldım
Ronnie

2
Sorunları geçersiz kılmak için çağrıyı genel yapabilirsiniz: void startMyTask(AsyncTask<Void, ?, ?> asyncTask) (doInBackground'nuz Void alırsa)
Peter

Sınıfınız AsyncTask <Void, Integer, Void> 'yı genişletiyorsa yukarıdaki kodu daha da basitleştirebilirsiniz. Harika sulai çalıştı, teşekkürler.
Peter Arandorenko

1
Çok teşekkürler. Web sunucusundan veri okumak için aynı anda iki zaman uyumsuz görevi yürütmem gereken bir senaryo vardı, ancak sunucunun yalnızca bir URL aldığını ve bu özel istek tamamlanmadığı sürece ikinci AsyncTask'ın URL'si sunucuya çarpmayacağını buldum . Cevabınız sorunumu çözdü :)
Santhosh Gutta

1
Bunun için oyları almaya devam ediyorum, teşekkür ederim. :) Ama birçok insanın AsyncTask'ı Volley , Glide veya Swagger gibi mükemmel kütüphanelerin daha iyi seçimler olduğu ağlarda kullanmaya çalıştığı izlenimini edindim . AsyncTask'ı kullanmadan önce bunları kontrol etmeyi unutma :)
sulai

21

@Sulai önerisini daha genel hale getirme:

@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);
}   

daha genel çözüm: AsyncTaskCompat.executeParallel (yeni MyAsyncTask, myprams);
Vikash Kumar Verma

11

@Arhimed'in kusursuz yanıtında @sulai'nin çok iyi özetine en son güncellemeyi (UPDATE 4) eklemek için:

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);
    }
}

Oh vay canına, harika. Bu .executeOnExecutor(), API> = KITKAT için artık gereksiz demektir, evet?
Taslim Oseni


3

Mümkün. Android cihazımın sürümü 4.0.4 ve android.os.Build.VERSION.SDK_INT 15

3 iplikçim var

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

Ayrıca bir Async-Tack sınıfım var.

İşte benim spinner yükleme kodum

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();

Bu mükemmel çalışıyor. İplikçilerim tek tek yüklendi. Ben executeOnExecutor kullanıcı yoktu.

İşte Async-task sınıfım

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
sıralı olarak yürütülmediklerini nasıl görüyorsunuz?
18'de behelit

@behelit Çalışırken önemli mi? Daha fazla bilgi için lütfen Kabul edilen yanıtı okuyun stackoverflow.com/a/4072832/2345900
Sajitha Rathnayake

2
Üzgünüm anlamadım. Eşzamansız dizilerinizin sıralı mı yoksa gerçekten eşzamansız mı olduğunu söyleyemiyorsanız, bunun önemi vardır. Ayrıca, bağlantılı cevap, cevabınızda gerçekten kullanmadığınız executeonexecutor yöntemini kullandığını söyler. Bir şey kaçırırsam özür dilerim.
18'de behelit

1
"İplikçilerim tek tek yüklendi." Kelimenin tam anlamıyla, bunun paralel bir yürütme değil, seri bir yürütme olduğunu söylediniz. AsyncTasks'ımızın her biri bir kuyruğa eklenir ve sırayla işlenir. Bu OP'nin sorusuna cevap vermiyor.
Joshua Pinter

Bu çok eski bir cevap. Bu sizin için işe yaramazsa lütfen kabul edilen cevaba bakın
Sajitha Rathnayake

3

görevleri paralel yürütmek istiyorsanız executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "your task name"), 3.0 sürümü Android sürümü sonra yöntemi çağırmanız gerekir ; ancak bu yöntem Android 3.0'dan önce ve 1.6'dan sonra mevcut değildir, çünkü kendi başına paralel yürütür, Bu yüzden farklı Android sürümlerinde istisna atmaktan kaçınmak için projenizde kendi AsyncTask sınıfınızı özelleştirmenizi öneririm.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.