एक निष्पादित AsyncTask को रद्द करने का आदर्श तरीका


108

मैं एक दूरस्थ थ्रेड में दूरस्थ ऑडियो-फाइल-फ़ेचिंग और ऑडियो फ़ाइल प्लेबैक ऑपरेशन चला रहा हूं AsyncTask। एक Cancellableप्रगति बार समय आपरेशन रन लाने के लिए दिखाया गया है।

AsyncTaskजब उपयोगकर्ता कैंसिल करता है (ऑपरेशन के खिलाफ फैसला करता है) मैं रन को रद्द / निरस्त करना चाहता हूं । ऐसे मामले को संभालने का आदर्श तरीका क्या है?

जवाबों:


76

बस पता चला कि AlertDialogs'एस boolean cancel(...);मैं हर जगह वास्तव में उपयोग कर रहे हैं कुछ नहीं करता है। महान।
इसलिए...

public class MyTask extends AsyncTask<Void, Void, Void> {

    private volatile boolean running = true;
    private final ProgressDialog progressDialog;

    public MyTask(Context ctx) {
        progressDialog = gimmeOne(ctx);

        progressDialog.setCancelable(true);
        progressDialog.setOnCancelListener(new OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {
                // actually could set running = false; right here, but I'll
                // stick to contract.
                cancel(true);
            }
        });

    }

    @Override
    protected void onPreExecute() {
        progressDialog.show();
    }

    @Override
    protected void onCancelled() {
        running = false;
    }

    @Override
    protected Void doInBackground(Void... params) {

        while (running) {
            // does the hard work
        }
        return null;
    }

    // ...

}

55
दौड़ने के लिए बूलियन ध्वज बनाने के बजाय, क्या आप इसे हटा नहीं सकते और इसे बना सकते हैं ((isCanceled)?) ???
कन्फ्यूशियस

36
डॉक्स से onCancelled () के बारे में: " रद्द (बूलियन) के बाद UI थ्रेड पर चलाया जाता है और doInBackground (ऑब्जेक्ट []) समाप्त हो गया है।" इस 'के बाद' का मतलब है कि onCancelled में फ्लैग सेट करना और doInBackground में चेक करना कोई मतलब नहीं है।
लोपेक

2
@confucius सही है, लेकिन इस तरह से बैकग्राउंड थ्रेड बाधित नहीं होता है, मान लें कि इमेज अपलोड करते समय, अपलोडिंग प्रक्रिया बैकग्राउंड में जारी रहती है और हमें onPostExecute कहा जाता है।
उमेश

1
@DanHulme मेरा मानना ​​है कि मैंने उत्तर में प्रदान किए गए कोड स्निपेट का उल्लेख किया, कन्फ्यूशियस की टिप्पणी (जो सही है) के लिए नहीं।
लोपेक

4
हां, यह जवाब काम नहीं करता है । DoInBackground में, की जगह while(running)के साथ while(!isCancelled())के रूप में दूसरों की टिप्पणियों में यहाँ कहा है।
मैट 5784

76

यदि आप गणना कर रहे हैं :

  • आपको isCancelled()समय-समय पर जांच करनी होगी ।

यदि आप एक HTTP अनुरोध कर रहे हैं :

  • अपने HttpGetया HttpPostकहीं के उदाहरण (जैसे एक सार्वजनिक क्षेत्र) को सहेजें ।
  • कॉल करने के बाद कॉल cancelकरें request.abort()। यह IOExceptionआपके अंदर फेंक दिया जाएगा doInBackground

मेरे मामले में, मेरे पास एक कनेक्टर क्लास था जो मैंने विभिन्न AsyncTasks में उपयोग किया था। इसे सरल रखने के लिए, मैंने abortAllRequestsउस कक्षा में एक नई विधि जोड़ी और कॉल करने के बाद सीधे इस विधि को बुलाया cancel


धन्यवाद, यह काम करता है, लेकिन इस मामले में अपवाद से कैसे बचें?
बेवीपस

आपको HttpGet.abort()बैकग्राउंड थ्रेड से कॉल करना होगा या आपको ए android.os.NetworkOnMainThreadException
हीथ बॉर्डर्स

@wrygiel यदि आप एक HTTP अनुरोध कर रहे हैं, cancel(true)तो अनुरोध को बाधित नहीं करना चाहिए ? प्रलेखन से:If the task has already started, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task.
Storo

HttpURLConnection.disconnect();
ओडेड ब्रेइनर

यदि आपके पास AsyncTask में एक सीपीयू खपत ऑपरेशन है, तो आपको कॉल करना होगा cancel(true)। मैंने इसका इस्तेमाल किया और यह काम करता है।
SMMousavi 12

20

बात यह है कि AsyncTask.cancel () कॉल केवल आपके कार्य में onCancel फ़ंक्शन को कॉल करता है। यह वह जगह है जहां आप रद्द अनुरोध को संभालना चाहते हैं।

यहां एक छोटा कार्य है जो मैं एक अपडेट विधि को ट्रिगर करने के लिए उपयोग करता हूं

private class UpdateTask extends AsyncTask<Void, Void, Void> {

        private boolean running = true;

        @Override
        protected void onCancelled() {
            running = false;
        }

        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
            onUpdate();
        }

        @Override
        protected Void doInBackground(Void... params) {
             while(running) {
                 publishProgress();
             }
             return null;
        }
     }

2
यह काम करेगा, लेकिन तार्किक रूप से जब आप सर्वर की प्रतिक्रिया की प्रतीक्षा कर रहे हैं, और आपने बस डीबी ऑपरेशन किया है, तो आपको अपनी गतिविधि में सही परिवर्तन को प्रतिबिंबित करना चाहिए। मैंने उस पर एक ब्लॉग लिखा, मेरा उत्तर देखें।
विकास

4
जैसा कि स्वीकृत उत्तर पर टिप्पणियों में उल्लेख किया गया है, अपना स्वयं का runningझंडा बनाने की कोई आवश्यकता नहीं है । AsyncTask में एक आंतरिक ध्वज होता है जिसे तब सेट किया जाता है जब कार्य रद्द कर दिया गया हो। बदलें while (running)के साथ while (!isCancelled())developer.android.com/reference/android/os/… तो इस साधारण मामले में, आपको onCancelled()ओवरराइड की आवश्यकता नहीं है ।
टूलमेकरस्टेव

11

सरल: एक का उपयोग नहीं करते AsyncTaskAsyncTaskछोटे परिचालनों के लिए डिज़ाइन किया गया है जो जल्दी से समाप्त होते हैं (दसियों सेकंड) और इसलिए इसे रद्द करने की आवश्यकता नहीं है। "ऑडियो फ़ाइल प्लेबैक" योग्य नहीं है। आपको साधारण ऑडियो फ़ाइल प्लेबैक के लिए पृष्ठभूमि थ्रेड की भी आवश्यकता नहीं है।


क्या आप यह सुझाव दे रहे हैं कि हम नियमित जावा थ्रेड का उपयोग करते हैं और पारंपरिक बूलियन वैरिएबल बूलियन वैरिएबल का उपयोग करके थ्रेड रन करते हैं?
सामुह

34
कोई अपराध नहीं माइक, लेकिन यह एक स्वीकार्य जवाब नहीं है। AsyncTask में एक रद्द विधि है, और इसे काम करना चाहिए। जहाँ तक मैं बता सकता हूँ, यह नहीं है - लेकिन फिर भी अगर मैं इसे गलत कर रहा हूँ, तो किसी कार्य को रद्द करने का एक सही तरीका होना चाहिए। विधि अन्यथा मौजूद नहीं होगी। और यहां तक ​​कि छोटे कार्यों को रद्द करने की आवश्यकता हो सकती है - मेरे पास एक गतिविधि है जहां यह लोड होने पर तुरंत एक AsyncTask शुरू करता है, और यदि उपयोगकर्ता कार्य को खोलने के तुरंत बाद वापस हिट करता है, तो वे एक फोर्स को देखेंगे एक दूसरा बंद करें जब कार्य समाप्त हो जाएगा लेकिन कोई संदर्भ नहीं इसके onPostExecute में उपयोग के लिए मौजूद है।
एरिक मिल

10
@ कोंडाइक: मुझे नहीं पता कि "माइक" कौन है। "लेकिन यह स्वीकार्य उत्तर नहीं है" - आपकी राय में आपका स्वागत है। "AsyncTask में एक रद्द विधि है, और इसे काम करना चाहिए।" - जावा में धागे को रद्द करना ~ 15 वर्षों से एक समस्या है। इसका एंड्रॉइड के साथ बहुत कुछ नहीं है। आपके "फोर्स क्लोज़" परिदृश्य के संबंध में, यह एक बूलियन चर द्वारा हल किया जा सकता है, जिसे आप onPostExecute()यह देखने के लिए परीक्षण करते हैं कि आपको काम के साथ आगे बढ़ना चाहिए या नहीं।
कॉमन्सवेयर जूल 27'10

1
@Tejaswi Yerukalapudi: यह अधिक है कि यह स्वचालित रूप से कुछ भी नहीं करेगा। इस प्रश्न पर स्वीकृत उत्तर देखें।
कॉमन्सवेयर

10
आप समय-समय पर अपने doInBackground में AsyncTask पर isCancelled विधि की जांच करने वाले हैं। डॉक्स में यह वहीं है: developer.android.com/reference/android/os/…
क्रिस्टोफर पेरी

4

इसे करने का एकमात्र तरीका यह है कि isCancelled () विधि के मान की जाँच करके और प्लेबैक को रोकते समय यह सच हो।


4

यह है कि मैं अपना AsyncTask कैसे लिखता हूं
मुख्य बिंदु थ्रेड है। स्लीप (1);

@Override   protected Integer doInBackground(String... params) {

        Log.d(TAG, PRE + "url:" + params[0]);
        Log.d(TAG, PRE + "file name:" + params[1]);
        downloadPath = params[1];

        int returnCode = SUCCESS;
        FileOutputStream fos = null;
        try {
            URL url = new URL(params[0]);
            File file = new File(params[1]);
            fos = new FileOutputStream(file);

            long startTime = System.currentTimeMillis();
            URLConnection ucon = url.openConnection();
            InputStream is = ucon.getInputStream();
            BufferedInputStream bis = new BufferedInputStream(is);

            byte[] data = new byte[10240]; 
            int nFinishSize = 0;
            while( bis.read(data, 0, 10240) != -1){
                fos.write(data, 0, 10240);
                nFinishSize += 10240;
                **Thread.sleep( 1 ); // this make cancel method work**
                this.publishProgress(nFinishSize);
            }              
            data = null;    
            Log.d(TAG, "download ready in"
                  + ((System.currentTimeMillis() - startTime) / 1000)
                  + " sec");

        } catch (IOException e) {
                Log.d(TAG, PRE + "Error: " + e);
                returnCode = FAIL;
        } catch (Exception e){
                 e.printStackTrace();           
        } finally{
            try {
                if(fos != null)
                    fos.close();
            } catch (IOException e) {
                Log.d(TAG, PRE + "Error: " + e);
                e.printStackTrace();
            }
        }

        return returnCode;
    }

1
मैंने पाया कि एसिंक्स टास्क पर बस कैंसिल (ट्रू) को कॉल करना और समय-समय पर चेक करना कैंसिल है () समय-समय पर काम करता है, लेकिन यह निर्भर करता है कि आपका कार्य क्या कर रहा है, इससे 60 सेकंड तक का समय लग सकता है। Thread.sleep (1) को जोड़ने से यह तुरंत टूटने में सक्षम होता है। (Async टास्क बजाय एक प्रतीक्षा स्थिति में चला जाता है और तुरंत खारिज नहीं किया जाता है)। इसके लिए धन्यवाद।
जॉन जे स्मिथ

0

हमारे वैश्विक AsyncTask वर्ग चर

LongOperation LongOperationOdeme = new LongOperation();

और KEYCODE_BACK क्रिया जो AsyncTask को बाधित करती है

   @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            LongOperationOdeme.cancel(true);
        }
        return super.onKeyDown(keyCode, event);
    }

इससे मेरा काम बनता है।


0

मैं cancel(true)अनावश्यक रूप से अपने async कार्यों को बाधित करने के लिए मजबूर नहीं करना चाहता, क्योंकि उनके पास मुक्त होने के लिए संसाधन हो सकते हैं, जैसे कि सॉकेट या फ़ाइल स्ट्रीम, स्थानीय डेटाबेस पर डेटा लिखना आदि। दूसरी तरफ, मैंने उन स्थितियों का सामना किया है जिनमें async कार्य अपने आप को समय का हिस्सा खत्म करने से इंकार करता है, उदाहरण के लिए कभी-कभी जब मुख्य गतिविधि बंद हो रही है और मैं async कार्य को गतिविधि की onPause()विधि के अंदर से समाप्त करने का अनुरोध करता हूं । तो यह केवल कॉल करने की बात नहीं है running = false। दोनों कॉल: मैं एक मिश्रित समाधान के लिए जाना है running = false, तो समाप्त करने के लिए async कार्य में कुछ मिलीसेकेंड दे रही है, और फिर फोन या तो cancel(false)या cancel(true)

if (backgroundTask != null) {
    backgroundTask.requestTermination();
    try {
        Thread.sleep((int)(0.5 * 1000));
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    if (backgroundTask.getStatus() != AsyncTask.Status.FINISHED) {
        backgroundTask.cancel(false);
    }
    backgroundTask = null;
}

एक परिणाम के रूप में, doInBackground()खत्म होने के बाद , कभी-कभी onCancelled()विधि कहा जाता है, और कभी-कभी onPostExecute()। लेकिन कम से कम async कार्य समाप्ति की गारंटी है।


एक दौड़ की स्थिति की तरह देखो।
मिसेज़ेल

0

29 अप्रैल '10 पर यानचेन के जवाब के संदर्भ में: 'डूइनब्लैकग्राउंड' के तहत आपके कोड को एसिंक्सटस्क के प्रत्येक निष्पादन के दौरान कई बार निष्पादित करने पर 'चल रहा है (चल रहा है)' दृष्टिकोण साफ है। यदि 'doInBackground' के तहत आपके कोड को AsyncTask के निष्पादन के अनुसार केवल एक बार निष्पादित किया जाना है, तो अपने सभी कोड को 'doInBackground' के तहत '' (चल रहा है) '' लूप में रखने पर बैकग्राउंड कोड (बैकग्राउंड थ्रेड) को चलने से रोका नहीं जाएगा जब AsyncTask को ही रद्द कर दिया गया है, क्योंकि T जबकि (चल रही है) ’स्थिति का मूल्यांकन केवल तब होगा जब लूप के अंदर के सभी कोड को कम से कम एक बार निष्पादित किया गया हो। आपको इस प्रकार या तो (a।) अपने कोड को 'doInBackground' के तहत एकाधिक में तोड़ देना चाहिए जबकि (चल रहा है) '(या) (b) कई' निष्पादित 'है।https://developer.android.com/reference/android/os/AsyncTask.html

विकल्प (ए) के लिए इस प्रकार यानचेन के उत्तर को इस प्रकार संशोधित किया जा सकता है:

public class MyTask extends AsyncTask<Void, Void, Void> {

private volatile boolean running = true;

//...

@Override
protected void onCancelled() {
    running = false;
}

@Override
protected Void doInBackground(Void... params) {

    // does the hard work

    while (running) {
        // part 1 of the hard work
    }

    while (running) {
        // part 2 of the hard work
    }

    // ...

    while (running) {
        // part x of the hard work
    }
    return null;
}

// ...

विकल्प के लिए (b।) 'DoInBackground' में आपका कोड कुछ इस तरह दिखाई देगा:

public class MyTask extends AsyncTask<Void, Void, Void> {

//...

@Override
protected Void doInBackground(Void... params) {

    // part 1 of the hard work
    // ...
    if (isCancelled()) {return null;}

    // part 2 of the hard work
    // ...
    if (isCancelled()) {return null;}

    // ...

    // part x of the hard work
    // ...
    if (isCancelled()) {return null;}
}

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