एक ही समय में कई AsyncTasks चलाना - संभव नहीं है?


259

मैं एक ही समय में दो AsyncTasks चलाने की कोशिश कर रहा हूं। (प्लेटफ़ॉर्म एंड्रॉइड 1.5, एचटीसी हीरो है।) हालांकि, केवल पहले निष्पादित होता है। यहाँ मेरी समस्या का वर्णन करने के लिए एक सरल स्निपेट है:

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

दूसरा AsyncTask कभी निष्पादित नहीं होता है। यदि मैं निष्पादनों () कथनों के क्रम को बदल देता हूं, तो केवल foo कार्य आउटपुट का उत्पादन करेगा।

क्या मुझे यहाँ कुछ स्पष्ट याद आ रहा है और / या कुछ बेवकूफी कर रहा हूँ? क्या एक ही समय में दो AsyncTasks चलाना संभव नहीं है?

संपादित करें: मुझे लगा कि फोन में एंड्रॉइड 1.5 चलता है, मैंने समस्या को अपडेट किया। तदनुसार। Android 2.1 पर चलने वाले एचटीसी हीरो के साथ मुझे यह समस्या नहीं है। हम्म्म ...


आपका कोड मेरे लिए काम करता है, इसलिए समस्या कहीं और होनी चाहिए। क्या आपने अपने लॉगकैट दृश्य में एक फ़िल्टर दर्ज किया था? ;-)
mreichelt

हम्म, यह अजीब है। मेरे पास logcat में कोई फ़िल्टरिंग नहीं है। क्या आप 1.6 का भी उपयोग कर रहे हैं? यदि हां, तो कौन सा फोन?
कृंतक

ओह, बस एहसास हुआ कि यह (प्राचीन) Android 1.5
कृंतक

1
मैंने लक्ष्य के रूप में एंड्रॉइड 1.6 का उपयोग किया और एंड्रॉइड 2.1 एमुलेटर। तो अगर समस्या वास्तव में Android 1.5 के साथ केवल HTC हीरो पर होती है - उन्हें स्क्रू करें, तो आप ठीक हैं। ;-) एचटीसी हीरो के पास पहले से ही एक नए एंड्रॉइड वर्जन का अपडेट है। मैं इसके बारे में परेशान नहीं होता अगर कुछ निर्माता हैं जो चीजों को पेंच करते हैं। इसके अलावा मैं एंड्रॉइड 1.5 के बारे में बुरा नहीं मानूंगा।
mreichelt

5 मिनट की छोटी अवधि के कार्यों के लिए AsyncTask का उपयोग किया जाना चाहिए। ThreadPoolExecutor ( developer.android.com/reference/java/util/concurrent/… ) पर जाएं। संबंधित पोस्ट: stackoverflow.com/questions/6964011/…
रविंद्र बाबू

जवाबों:


438

AsyncTask doInBackground () से सामान चलाने के लिए एक थ्रेड पूल पैटर्न का उपयोग करता है। मुद्दा शुरू में (शुरुआती एंड्रॉइड ओएस संस्करणों में) पूल का आकार सिर्फ 1 था, जिसका अर्थ है AsyncTasks के एक गुच्छा के लिए कोई समानांतर गणना नहीं। लेकिन बाद में उन्होंने तय किया कि और अब आकार 5 है, इसलिए अधिकतम 5 AsyncTasks एक साथ चल सकते हैं। दुर्भाग्य से मुझे याद नहीं है कि वास्तव में उन्होंने किस संस्करण में बदलाव किया है।

अपडेट करें:

इस पर वर्तमान (2012-01-27) एपीआई क्या कहता है:

जब पहली बार पेश किया गया, तो AsyncTasks को एकल पृष्ठभूमि थ्रेड पर क्रमिक रूप से निष्पादित किया गया था। डोनट के साथ शुरू करके, इसे थ्रेड्स के एक पूल में बदल दिया गया, जिससे कई कार्यों को समानांतर में संचालित किया जा सके। हनीकॉम के बाद, समानांतर निष्पादन के कारण होने वाली सामान्य अनुप्रयोग त्रुटियों से बचने के लिए इसे वापस एक सूत्र में बदलने की योजना है। यदि आप वास्तव में समानांतर निष्पादन चाहते हैं, तो आप THREAD_POOL_EXECUTOR के साथ इस विधि के निष्पादन योग्य (निष्पादनकर्ता, परम ...) संस्करण का उपयोग कर सकते हैं; हालाँकि, इसके उपयोग पर चेतावनी के लिए कमेंट्री देखें।

डोनट एंड्रॉइड 1.6 है, हनीकॉम एंड्रॉइड 3.0 है।

अद्यतन: 2

द्वारा टिप्पणी देखें kabukoसे Mar 7 2012 at 1:27

यह पता चलता है कि एपीआई के लिए जहां "समानांतर में कई कार्यों को संचालित करने की अनुमति देने वाले थ्रेड्स का एक पूल" का उपयोग किया जाता है (1.6 से शुरू और 3.0 पर समाप्त होता है) साथ-साथ चलने वाले एसिंक्सटैक्सेस की संख्या इस बात पर निर्भर करती है कि पहले से ही निष्पादन के लिए कितने कार्य पारित किए गए हैं, लेकिन doInBackground()अभी तक उनका अंत नहीं किया है।

यह मेरे द्वारा 2.2 पर परीक्षण / पुष्टि की गई है। मान लीजिए कि आपके पास एक कस्टम AsyncTask है जो सिर्फ एक सेकंड में सोता है doInBackground()। AsyncTasks विलंबित कार्यों के भंडारण के लिए आंतरिक रूप से एक निश्चित आकार की कतार का उपयोग करता है। कतार का आकार डिफ़ॉल्ट रूप से 10 है। यदि आप एक पंक्ति में 15 अपने कस्टम कार्य शुरू करते हैं, तो पहले 5 उनके प्रवेश करेंगे doInBackground(), लेकिन बाकी एक मुक्त कर्मचारी थ्रेड के लिए एक कतार में इंतजार करेंगे। जैसे ही पहले 5 में से कोई भी खत्म होता है, और इस तरह एक श्रमिक धागा जारी होता है, कतार से एक कार्य निष्पादन शुरू हो जाएगा। तो इस मामले में अधिकतम 5 कार्य एक साथ चलेंगे। हालाँकि, यदि आप एक पंक्ति में 16 अपने कस्टम कार्य शुरू करते हैं, तो पहले 5 उनके प्रवेश करेंगे doInBackground(), बाकी 10 कतार में लग जाएंगे, लेकिन 16 वें के लिए एक नया वर्कर थ्रेड बनाया जाएगा ताकि यह तुरंत निष्पादन शुरू कर देगा। तो इस मामले में अधिकतम 6 कार्य एक साथ चलेंगे।

एक साथ कितने कार्यों को चलाया जा सकता है, इसकी एक सीमा है। चूँकि AsyncTaskथ्रेड पूल निष्पादक का उपयोग सीमित संख्या में वर्कर थ्रेड्स (128) के साथ होता है और विलंबित कार्य कतार का आकार 10 है, यदि आप 138 से अधिक को निष्पादित करने का प्रयास करते हैं तो ऐप आपके साथ क्रैश हो जाएगा java.util.concurrent.RejectedExecutionException

3.0 से शुरू होकर एपीआई आपके कस्टम थ्रेड पूल एक्ज़ीक्यूटर को AsyncTask.executeOnExecutor(Executor exec, Params... params)विधि के माध्यम से उपयोग करने की अनुमति देता है । यह अनुमति देता है, उदाहरण के लिए, विलंबित कार्य कतार के आकार को कॉन्फ़िगर करने के लिए यदि डिफ़ॉल्ट 10 वह नहीं है जो आपको चाहिए।

@Knossos का उल्लेख है, AsyncTaskCompat.executeParallel(task, params);एपीआई स्तर के साथ परेशान किए बिना समानांतर में कार्यों को चलाने के लिए समर्थन v.4 पुस्तकालय से उपयोग करने का एक विकल्प है। यह विधि एपीआई स्तर 26.0.0 में पदावनत हो गई।

अद्यतन: 3

कार्यों की संख्या, सीरियल बनाम समानांतर निष्पादन के साथ खेलने के लिए यहां एक सरल परीक्षण ऐप है: https://github.com/vitkhudenko/test_asynctask

अद्यतन: 4 (यह इंगित करने के लिए धन्यवाद @penkzhou)

एंड्रॉइड 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);

एंड्रॉइड 4.4 (एपीआई 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
बस FYI करें, कोर पूल का आकार 5 है, लेकिन अधिकतम 128 है। इसका मतलब है कि यदि आप अपनी कतार भरते हैं, तो 5 (128 से अधिक) एक साथ चल सकते हैं।
काबुको

2
@kabuko: आप बिल्कुल सही कह रहे हैं। बस इस पर 2.2 जांच की और पुष्टि की कि यदि 5 कार्य पहले से चल रहे हैं (कोर पूल का आकार 5 है), तो यह शेष कार्यों को विलंबित कार्यों की कतार में डालना शुरू कर देता है, हालांकि यदि कतार पहले से ही भरी हुई है (अधिकतम कतार का आकार 10 है ), तो यह कार्य के लिए एक अतिरिक्त कार्यकर्ता सूत्र शुरू करता है ताकि कार्य तुरंत हो जाए।
विट खूदेंको

1
बस मेरा दिन बचा लिया, कैप्शन नायक :) धन्यवाद
Karoly

2
कृपया इस जवाब को नए कंपेटिटिव फ़ंक्शंस के लिए अपडेट करें । उदाहरण:AsyncTaskCompat.executeParallel(task, params);
Knossos

1
@Arhimed कृपया, इस सवाल का जवाब अद्यतन के बाद से AsyncTaskCompat.executeParallel(task, params);अब है पदावनत
किरिल 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);
}

यह अरहमेड के उत्कृष्ट उत्तर का सारांश है।

कृपया सुनिश्चित करें कि आप अपने प्रोजेक्ट बिल्ड लक्ष्य के रूप में एपीआई स्तर 11 या उच्चतर का उपयोग करते हैं। ग्रहण में, वह है Project > Properties > Android > Project Build Targetयह एपीआई स्तर को कम करने के लिए पिछड़े संगतता को नहीं तोड़ेगा। चिंता न करें, यदि आपकी गलती से बाद में पेश की गई सुविधाओं का उपयोग करें, तो आपको लिंट त्रुटियां मिलेंगी minSdkVersion। यदि आप वास्तव में की तुलना में बाद में शुरू की गई सुविधाओं का उपयोग करना चाहते हैं minSdkVersion, तो आप एनोटेशन का उपयोग करके उन त्रुटियों को दबा सकते हैं, लेकिन उस स्थिति में, आपको स्वयं संगतता के बारे में ध्यान रखने की आवश्यकता है । ठीक यही ऊपर के स्निपेट में हुआ है।


3
TimerTask.THREAD_POOL_EXECUTOR को AsynTask.THREAD_POOL_EXECUTOR में बदलना पड़ा, तो यह उदाहरण काम करता है
Ronnie

2
मुद्दों को शून्य करने के लिए आप कॉल को सामान्य बना सकते हैं: void startMyTask(AsyncTask<Void, ?, ?> asyncTask) (यदि आपका doInBackground शून्य लेता है)
पीटर

यदि आपकी कक्षा AsyncTask <Void, Integer, Void> का विस्तार करती है तो आप उपरोक्त कोड को और भी सरल बना सकते हैं। यह महान sulai, धन्यवाद काम किया।
पीटर अरंडोरेंको

1
बहुत बहुत धन्यवाद। मेरे पास एक परिदृश्य था जहां मुझे वेब सर्वर से डेटा पढ़ने के लिए एक साथ दो async कार्यों को निष्पादित करने की आवश्यकता है, लेकिन मैंने पाया कि सर्वर को केवल एक URL प्राप्त होता है और जब तक कि विशेष अनुरोध पूरा नहीं होता है, तब तक दूसरा AsyncTask का url सर्वर को हिट नहीं करेगा। । आपके उत्तर ने मेरी समस्या हल कर दी :)
संथोष गुट्टा

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 (नया MyAsyncTask, myprams);
विकाश कुमार वर्मा

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, के लिए बेमानी है?
तस्लीम ओसेनी

4

एंड्रॉइड डेवलपर्स बिट्स को लोड करने का उदाहरण कुशलता से एक कस्टम एसिंक्टस्क (जेलीबीन से कॉपी किया गया) का उपयोग करता है ताकि आप एपिस में निष्पादनकर्ता का उपयोग कर सकते हैं <11 से कम

http://developer.android.com/training/displaying-bitmaps/index.html

कोड डाउनलोड करें और उपयोग पैकेज पर जाएं।


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

यह पूरी तरह से काम कर रहा है। एक-एक करके मेरे स्पिनरों ने लोड किया। मैंने उपयोगकर्ता को निष्पादित नहीं किया।

यहाँ मेरा 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();
        }
    }
}

2
आप कैसे देखते हैं कि वे क्रमिक रूप से निष्पादित नहीं हुए हैं?
बेहालिट

@behelit क्या बात है जब यह काम कर रहा है? कृपया अधिक विवरण के लिए स्वीकृत उत्तर पढ़ें stackoverflow.com/a/4072832/2345900
सजीथा रथनायके

2
क्षमा करें, मुझे समझ नहीं आया। यदि आप यह नहीं बता सकते हैं कि आपके एसिंक्शंस अनुक्रमिक हैं या वास्तव में एसिंक्स हैं तो इससे कोई फर्क नहीं पड़ता। साथ ही, जुड़ा हुआ उत्तर निष्पादक विधि का उपयोग करने के लिए कहता है, जिसे आप वास्तव में अपने उत्तर में उपयोग नहीं कर रहे हैं। क्षमा करें अगर मैं कुछ याद किया।
बेहेलिट

1
"एक-एक करके मेरे स्पिनरों ने लोड किया।" आपने सचमुच इसे स्वयं कहा है कि यह एक धारावाहिक निष्पादन है, न कि एक समानांतर निष्पादन। हमारे प्रत्येक AsyncTasks को एक कतार में जोड़ा जाता है और क्रमिक रूप से संसाधित किया जाता है। इससे ओपी के सवाल का जवाब नहीं मिलता।
जोशुआ पिंटर

यह बहुत पुराना उत्तर है। यदि यह आपके लिए काम नहीं करता है तो कृपया स्वीकृत उत्तर देखें
सजीथा रथनायके

3

यदि आप समानांतर कार्यों को निष्पादित करना चाहते हैं, तो आपको executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "your task name")3.0 के एंड्रॉइड संस्करण के बाद कॉल करने की आवश्यकता है ; लेकिन यह विधि एंड्रॉइड 3.0 से पहले और 1.6 के बाद मौजूद नहीं है क्योंकि यह अपने आप ही समानांतर चलता है, इसलिए मेरा सुझाव है कि आप अपने प्रोजेक्ट में अपनी खुद की AsyncTask क्लास को कस्टमाइज़ करें, ताकि अलग-अलग एंड्रॉइड वर्जन में थ्रो अपवाद से बचा जा सके।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.