पृष्ठभूमि कार्य, प्रगति संवाद, अभिविन्यास परिवर्तन - क्या कोई 100% कार्य समाधान है?


235

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

इस तरह की समस्या को संभालने का सबसे अच्छा तरीका क्या है (यूआई को पृष्ठभूमि थ्रेड से अद्यतन करना जो उपयोगकर्ता के अभिविन्यास में भी काम करता है)? क्या Google के किसी व्यक्ति ने कुछ "आधिकारिक समाधान" प्रदान किया है?


4
इस विषय पर मेरा ब्लॉग पोस्ट मदद कर सकता है। यह कॉन्फ़िगरेशन परिवर्तनों के दौरान लंबे समय तक चलने वाले कार्यों को बनाए रखने के बारे में है।
एलेक्स लॉकवुड

1
यह प्रश्न भी संबंधित है।
एलेक्स लॉकवुड

बस एफटीआर यहाँ एक संबंधित रहस्य है .. stackoverflow.com/q/23742412/294884
फेटी

जवाबों:


336

चरण # 1: अपना AsyncTaskएक staticनेस्टेड वर्ग, या एक पूरी तरह से अलग वर्ग बनाएं , न कि केवल एक आंतरिक (गैर-स्थिर नेस्टेड) ​​वर्ग।

चरण # 2: एक डेटा सदस्य AsyncTaskके Activityमाध्यम से पकड़ है , निर्माता और एक सेटर के माध्यम से निर्धारित किया है।

चरण # 3: निर्माण करते समय AsyncTask, Activityनिर्माता को करंट की आपूर्ति करें ।

चरण # 4: इसमें onRetainNonConfigurationInstance(), AsyncTaskमूल, अब-दूर जाने वाली गतिविधि से अलग करने के बाद , वापस लौटें ।

चरण # 5: onCreate()यदि getLastNonConfigurationInstance()ऐसा नहीं है , तो nullइसे अपनी AsyncTaskकक्षा में डालें और अपने सेटर को कॉल करें कि वह आपकी नई गतिविधि को कार्य के साथ जोड़े।

चरण # 6: गतिविधि डेटा सदस्य से संदर्भित न करें doInBackground()

यदि आप उपरोक्त नुस्खा का पालन करते हैं, तो यह सब काम करेगा। onProgressUpdate()और onPostExecute()बाद की शुरुआत onRetainNonConfigurationInstance()और अंत के बीच निलंबित हैं onCreate()

यहाँ एक नमूना परियोजना है जो तकनीक का प्रदर्शन कर रही है।

एक और तरीका यह है कि AsyncTaskअपने काम को खोदो और अपने काम को आगे बढ़ाओ IntentService। यह विशेष रूप से उपयोगी है अगर किया जाने वाला कार्य लंबा हो सकता है और उपयोगकर्ता की गतिविधियों के संदर्भ में क्या करता है (जैसे, एक बड़ी फ़ाइल को डाउनलोड करना) की परवाह किए बिना जाना चाहिए। आप Intentया तो काम करने के लिए गतिविधि का जवाब देने के लिए एक आदेशित प्रसारण का उपयोग कर सकते हैं (यदि यह अभी भी अग्रभूमि में है) या Notificationउपयोगकर्ता को यह बताने के लिए उठाएं कि क्या काम हो गया है। यहाँ इस पैटर्न पर अधिक के साथ एक ब्लॉग पोस्ट है


8
इस सामान्य मुद्दे पर आपके शानदार उत्तर के लिए बहुत बहुत धन्यवाद! बस पूरी तरह से होने के लिए, आप चरण # 4 में जोड़ सकते हैं जिसे हमें एस्किंस्कस्क में गतिविधि को अलग करना है। यह अच्छी तरह से नमूना परियोजना में चित्रित किया गया है, हालांकि।
केविन गौडिन

3
लेकिन क्या होगा अगर मुझे गतिविधि के सदस्यों तक पहुंच की आवश्यकता है?
यूजीन

3
@Andrew: एक स्टैटिक इनर क्लास या ऐसी कोई चीज़ बनाएँ, जो कई ऑब्जेक्ट्स को पकड़ती है, और उसे लौटा देती है।
कॉमन्सवेयर

11
onRetainNonConfigurationInstance()पदावनत है और सुझाए गए विकल्प का उपयोग करना है setRetainInstance(), लेकिन यह किसी वस्तु को वापस नहीं करता है। क्या asyncTaskकॉन्फ़िगरेशन परिवर्तन के साथ इसे संभालना संभव है setRetainInstance()?
इंद्रेक कुए

10
@SYLARRR: बिल्कुल लो Fragmentपकड़ AsyncTask। खुद ही Fragmentबुला लो setRetainInstance(true)। से AsyncTaskही बात करनी है Fragment। अब, कॉन्फ़िगरेशन परिवर्तन पर, Fragmentनष्ट नहीं किया जाता है और फिर से बनाया जाता है (भले ही गतिविधि हो), और इसलिए AsyncTaskकॉन्फ़िगरेशन परिवर्तन के दौरान इसे बरकरार रखा जाता है।
कॉमंसवेयर

13

स्वीकृत उत्तर बहुत उपयोगी था, लेकिन इसमें प्रगति संवाद नहीं है।

सौभाग्य से आपके लिए, पाठक, मैंने एक प्रगति संवाद के साथ एक AsyncTask का एक बहुत व्यापक और कामकाजी उदाहरण बनाया है !

  1. रोटेशन काम करता है, और संवाद बचता है।
  2. आप बैक बटन दबाकर कार्य और संवाद को रद्द कर सकते हैं (यदि आप यह व्यवहार चाहते हैं)।
  3. यह टुकड़ों का उपयोग करता है।
  4. उपकरण के घूमने पर गतिविधि के नीचे के टुकड़े का लेआउट ठीक से बदल जाता है।

स्वीकृत उत्तर स्थैतिक वर्गों (सदस्य नहीं) के बारे में है। और उन लोगों से बचने के लिए आवश्यक है कि AsyncTask में बाहरी वर्ग उदाहरण के लिए एक (छिपा हुआ) सूचक है जो गतिविधि को नष्ट करने पर एक स्मृति रिसाव बन जाता है।
Bananeweizen

हाँ मुझे यकीन नहीं है कि मैंने स्थैतिक सदस्यों के बारे में क्यों कहा, क्योंकि मैंने वास्तव में उनका उपयोग किया है ... अजीब। संपादित उत्तर।
तम्मम्

क्या आप अपना लिंक अपडेट कर सकते हैं? मुझे वास्तव में इसकी आवश्यकता है।
रोमेन पेलरिन

क्षमा करें, मेरी वेबसाइट को पुनर्स्थापित करने के लिए नहीं मिला है - मैं इसे जल्द ही करूँगा! लेकिन इस समय में यह मूल रूप से इस उत्तर में कोड के समान है: stackoverflow.com/questions/8417885/…
टिमम्मी

1
लिंक फर्जी है; जहां कोड है, वहां बिना किसी संकेत के बेकार सूचकांक की ओर जाता है।
फ्रैक्टलबोब

9

मैंने एक सप्ताह के लिए इस दुविधा का हल ढूंढने के लिए टाल दिया है कि मैनिफ़ेस्ट फ़ाइल का संपादन किए बिना। इस समाधान की धारणाएं हैं:

  1. आपको हमेशा एक प्रगति संवाद का उपयोग करने की आवश्यकता होती है
  2. एक समय में केवल एक ही कार्य किया जाता है
  3. जब फोन घुमाया जाता है और प्रगति संवाद स्वचालित रूप से खारिज हो जाता है, तो आपको कार्य जारी रखने की आवश्यकता होती है।

कार्यान्वयन

आपको इस पोस्ट के नीचे मिली दो फाइलों को अपने कार्यक्षेत्र में कॉपी करना होगा। बस यह सुनिश्चित करें कि:

  1. आपके सभी का Activityविस्तार होना चाहिएBaseActivity

  2. में onCreate(), super.onCreate()आप किसी भी सदस्य को प्रारंभ बाद भी आपके द्वारा पहुँचा जा करने की आवश्यकता पर बुलाया जाना चाहिए ASyncTaskहै। इसके अलावा, getContentViewId()फ़ॉर्म लेआउट आईडी प्रदान करने के लिए ओवरराइड करें।

  3. गतिविधि द्वारा प्रबंधित संवाद बनाने के लिए onCreateDialog() हमेशा की तरह ओवरराइड करें ।

  4. अपने AsyncTasks बनाने के लिए एक नमूना स्थिर आंतरिक वर्ग के लिए नीचे दिया गया कोड देखें। आप बाद में एक्सेस करने के लिए अपना परिणाम mResult में स्टोर कर सकते हैं।


final static class MyTask extends SuperAsyncTask<Void, Void, Void> {

    public OpenDatabaseTask(BaseActivity activity) {
        super(activity, MY_DIALOG_ID); // change your dialog ID here...
                                       // and your dialog will be managed automatically!
    }

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

        // your task code

        return null;
    }

    @Override
    public boolean onAfterExecute() {
        // your after execute code
    }
}

और अंत में, अपने नए कार्य को शुरू करने के लिए:

mCurrentTask = new MyTask(this);
((MyTask) mCurrentTask).execute();

बस! मुझे उम्मीद है कि यह मजबूत समाधान किसी की मदद करेगा।

BaseActivity.java (स्वयं आयात व्यवस्थित करें)

protected abstract int getContentViewId();

public abstract class BaseActivity extends Activity {
    protected SuperAsyncTask<?, ?, ?> mCurrentTask;
    public HashMap<Integer, Boolean> mDialogMap = new HashMap<Integer, Boolean>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(getContentViewId());

        mCurrentTask = (SuperAsyncTask<?, ?, ?>) getLastNonConfigurationInstance();
        if (mCurrentTask != null) {
            mCurrentTask.attach(this);
            if (mDialogMap.get((Integer) mCurrentTask.dialogId) != null
                && mDialogMap.get((Integer) mCurrentTask.dialogId)) {
        mCurrentTask.postExecution();
            }
        }
    }

    @Override
    protected void onPrepareDialog(int id, Dialog dialog) {
    super.onPrepareDialog(id, dialog);

        mDialogMap.put(id, true);
    }

    @Override
    public Object onRetainNonConfigurationInstance() {
        if (mCurrentTask != null) {
            mCurrentTask.detach();

            if (mDialogMap.get((Integer) mCurrentTask.dialogId) != null
                && mDialogMap.get((Integer) mCurrentTask.dialogId)) {
                return mCurrentTask;
            }
        }

        return super.onRetainNonConfigurationInstance();
    }

    public void cleanupTask() {
        if (mCurrentTask != null) {
            mCurrentTask = null;
            System.gc();
        }
    }
}

SuperAsyncTask.java

public abstract class SuperAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
    protected BaseActivity mActivity = null;
    protected Result mResult;
    public int dialogId = -1;

    protected abstract void onAfterExecute();

    public SuperAsyncTask(BaseActivity activity, int dialogId) {
        super();
        this.dialogId = dialogId;
        attach(activity);
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        mActivity.showDialog(dialogId); // go polymorphism!
    }    

    protected void onPostExecute(Result result) {
        super.onPostExecute(result);
        mResult = result;

        if (mActivity != null &&
                mActivity.mDialogMap.get((Integer) dialogId) != null
                && mActivity.mDialogMap.get((Integer) dialogId)) {
            postExecution();
        }
    };

    public void attach(BaseActivity activity) {
        this.mActivity = activity;
    }

    public void detach() {
        this.mActivity = null;
    }

    public synchronized boolean postExecution() {
        Boolean dialogExists = mActivity.mDialogMap.get((Integer) dialogId);
        if (dialogExists != null || dialogExists) {
            onAfterExecute();
            cleanUp();
    }

    public boolean cleanUp() {
        mActivity.removeDialog(dialogId);
        mActivity.mDialogMap.remove((Integer) dialogId);
        mActivity.cleanupTask();
        detach();
        return true;
    }
}

4

क्या Google के किसी व्यक्ति ने कुछ "आधिकारिक समाधान" प्रदान किया है?

हाँ।

समाधान एक आवेदन वास्तुकला प्रस्ताव के बजाय अधिक है कि सिर्फ कुछ कोड है

उन्होंने 3 डिज़ाइन पैटर्न प्रस्तावित किए जो किसी एप्लिकेशन को सर्वर के साथ-साथ काम करने की अनुमति देता है, चाहे एप्लिकेशन स्टेट की परवाह किए बिना (यह तब भी काम करेगा जब उपयोगकर्ता ऐप को समाप्त कर देता है, उपयोगकर्ता स्क्रीन बदलता है, ऐप समाप्त हो जाता है, हर दूसरे संभव राज्य जहां एक पृष्ठभूमि डेटा ऑपरेशन को अंतर्विरोधित किया जा सकता है, यह इसे कवर करता है)

प्रस्ताव को Google I / O 2010 के दौरान Android REST क्लाइंट अनुप्रयोगों के भाषण में समझाया गया है, जो Virgil Dobjanschi द्वारा किया गया है। यह 1 घंटे लंबा है, लेकिन यह देखने लायक है।

इसका आधार नेटवर्क संचालन का सार है जो Serviceकि Activityअनुप्रयोग में किसी भी स्वतंत्र रूप से काम करता है । आप डेटाबेस के साथ काम कर रहे हैं, के उपयोग ContentResolverऔर Cursorआप एक बाहर के बॉक्स देना होगा पर्यवेक्षक पैटर्न है कि किसी भी aditional तर्क के बिना यूआई अद्यतन करने के लिए, एक बार आप प्राप्त किए गए दूरस्थ डेटा के साथ अपने स्थानीय डेटाबेस अद्यतन सुविधाजनक है। किसी भी अन्य ऑपरेशन के बाद कोड एक कॉलबैक के माध्यम से चलाया जाएगा Service(मैं इसके लिए एक ResultReceiverउपवर्ग का उपयोग करता हूं )।

वैसे भी, मेरा स्पष्टीकरण वास्तव में बहुत अस्पष्ट है, आपको निश्चित रूप से भाषण देखना चाहिए।


2

जबकि मार्क का (कॉमन्सवेयर) उत्तर वास्तव में अभिविन्यास परिवर्तन के लिए काम करता है, यह विफल रहता है यदि गतिविधि सीधे नष्ट हो जाती है (जैसे फोन कॉल के मामले में)।

आप अपने ASyncTask को संदर्भित करने के लिए एक एप्लिकेशन ऑब्जेक्ट का उपयोग करके अभिविन्यास परिवर्तन और दुर्लभ नष्ट की गई गतिविधि घटनाओं को संभाल सकते हैं।

समस्या और समाधान का एक उत्कृष्ट विवरण यहाँ है :

यह पूरी तरह से पता लगाने के लिए क्रेडिट पूरी तरह से रयान को जाता है।


1

4 साल बाद Google ने गतिविधि onCreate में setRetainInstance (सत्य) को कॉल करने की समस्या को हल किया। यह उपकरण के घूमने के दौरान आपकी गतिविधि के उदाहरण को संरक्षित करेगा। मेरे पास पुराने एंड्रॉइड के लिए एक सरल समाधान भी है।


1
लोगों द्वारा पर्यवेक्षक के लिए समस्या इसलिए होती है क्योंकि एंड्रॉइड रोटेशन, कीबोर्ड एक्सटेंशन और अन्य घटनाओं में एक गतिविधि वर्ग को नष्ट कर देता है, लेकिन एक async कार्य अभी भी नष्ट किए गए उदाहरण के लिए एक संदर्भ रखता है और UI अपडेट के लिए इसका उपयोग करने का प्रयास करता है। आप एंड्रॉइड को निर्देश दे सकते हैं कि वह गतिविधि को प्रकट या व्यावहारिक रूप से नष्ट न करें। इस स्थिति में एक async कार्य संदर्भ मान्य रहता है और कोई समस्या नहीं देखी गई है। चूंकि रोटेशन को विचारों को पुनः लोड करने के रूप में कुछ अतिरिक्त काम की आवश्यकता हो सकती है और इसी तरह, Google गतिविधि को संरक्षित करने की अनुशंसा नहीं करता है। तो आप तय करें।
सिंगागिरल

धन्यवाद, मैं स्थिति के बारे में जानता था, लेकिन setRainainststance () के बारे में नहीं। जो मुझे समझ नहीं आ रहा है, वह आपका दावा है कि Google ने इसका उपयोग प्रश्न में पूछे गए मुद्दों को हल करने के लिए किया था। क्या आप सूचना के स्रोत को लिंक कर सकते हैं? धन्यवाद।
jj_


onRetainNonConfigurationInstance () इस फ़ंक्शन को विशुद्ध रूप से अनुकूलन के रूप में कहा जाता है, और आपको इस पर भरोसा नहीं किया जाना चाहिए। <इसी स्रोत से: developer.android.com/reference/android/app/…
धनंजय एम

0

आपको गतिविधि हैंडलर का उपयोग करके सभी गतिविधि क्रियाओं को कॉल करना चाहिए। तो अगर आप कुछ धागे में हैं, तो आपको एक रननेबल और एक्टिविटी के हैंडलर का उपयोग करके पोस्ट करना चाहिए। अन्यथा आपका ऐप कभी-कभी घातक अपवाद के साथ क्रैश हो जाएगा।


0

यह मेरा समाधान है: https://github.com/Gotchamoh/Android-AsyncTask-ProgressDialog

मूल रूप से कदम हैं:

  1. onSaveInstanceStateयदि यह अभी भी प्रसंस्करण है तो मैं कार्य को बचाने के लिए उपयोग करता हूं ।
  2. में onCreateअगर यह बचा लिया गया था मैं काम मिलता है।
  3. में onPauseमैं त्यागने ProgressDialogअगर यह दिखाया गया है।
  4. में onResumeमैं दिखाने ProgressDialogयदि कार्य अभी भी जारी है।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.