2012-05-25 2 views
12

के पूरा होने पर आप डायलॉगफ्रैगमेंट (संगतता lib) को खारिज करने में कैसे संभाल सकते हैं, AsyncTask के दौरान कॉन्फ़िगरेशन परिवर्तन को संभालने के तरीके के बारे में कई पोस्ट हैं, लेकिन पृष्ठभूमि में मौजूद ऐप्स के बारे में मुझे कोई स्पष्ट समाधान नहीं मिला है (onPause()) जब एक AsyncTask समाप्त होता है और एक संवादफ्रेगमेंट (संगतता लाइब्रेरी) को खारिज करने का प्रयास करता है।AsyncTask

यहां समस्या है, अगर मेरे पास एक AsyncTask चल रहा है जो कि POOSTExecute() में डायलॉगफ्रैगमेंट को खारिज कर देना चाहिए, तो मुझे डायलॉगफ्रैगमेंट को खारिज करने का प्रयास करते समय पृष्ठभूमि में ऐप पृष्ठभूमि में है, तो मुझे एक अवैध स्टेटस एक्सेप्शन मिलता है। एप्लिकेशन स्विच जब तक

import android.support.v4.app.FragmentActivity; 
import android.support.v4.app.FragmentManager; 

public class SomeActivity extends FragmentActivity { 

    public void someMethod() { 
     ... 
     displaySomeDialog(); 
     new SomeTask(this).execute(); 
     ... 
    } 

    public void displaySomeDialog() { 
     DialogFragment someDialog = new SomeDialogFragment(); 
     someDialog.show(getFragmentManager(), "dialog"); 
    } 

    public void dismissSomeDialog() { 
     SomeDialogFragment someDialog = (SomeDialogFragment) getFragmentManager().findFragmentByTag("dialog"); 
     someDialog.dismiss(); 
    } 

    .... 

} 

वर्क्स ठीक पृष्ठभूमि जबकि SomeTask अभी भी चल रहा है:

private static class SomeTask extends AsyncTask<Void, Void, Boolean> { 

    public SomeTask(SomeActivity tActivity) 
    { 
     mActivity = tActivity; 
    } 

    private SomeActivity mActivity; 

    /** Set the view during/after config change */ 
    public void setView(Activity tActivity) { 
     mActivity tActivity; 
    } 

    @Override 
    protected Boolean doInBackground(Void... tParams) { 
     try { 
      //simulate some time consuming process 
      TimeUnit.SECONDS.sleep(3); 
     } catch (InterruptedException ignore) {} 
     return true; 
    } 

    @Override 
    protected void onPostExecute(Boolean tRouteFound) { 
     mActivity.dismissSomeDialog(); 
    } 

} 

गतिविधि इस तरह दिखता है। उस स्थिति में, जब कुछ टास्क SomeDialog() को खारिज करने का प्रयास करता है, तो मुझे एक IllegalStateException मिलता है।

05-25 16:36:02.237: E/AndroidRuntime(965): java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState 

मैंने जो भी पोस्ट देखी है, वह विस्तृत कामकाज के साथ कुछ क्लूजी दिशा में इंगित करती है। क्या इसे संभालने का कुछ एंड्रॉइड तरीका नहीं है? यदि यह डायलॉगफ्रैगमेंट के बजाय एक संवाद था, तो गतिविधि की बर्खास्तगी डायलॉग() इसे सही तरीके से संभाल लेगी। यदि यह एसीपी से एक के बजाय एक वास्तविक संवादफ्रेगमेंट था, तो dismissAllowingStateLoss() इसे संभाल लेंगे। क्या डायलॉगफ्रैगमेंट के एसीपी संस्करण के लिए ऐसा कुछ नहीं है?

+1

मेरी इस विषय पर (http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html) [** ब्लॉग पोस्ट **] पर एक नजर डालें ... शायद इस से मदद मिलेगी। –

उत्तर

15

गैरकानूनी राज्य अपवाद मुद्दे को पाने के लिए और अनिवार्य रूप से एक अस्वीकृति लागू करने के लिए निम्नलिखित का उपयोग करके किया जा सकता है।

getFragmentManager().beginTransaction().remove(someDialog).commitAllowingStateLoss(); 

यह हैकी कोड के बिना समस्या को हल करना चाहिए। यदि आपके पास dialog.show() का उपयोग करके यूआई थ्रेड के साथ हैंडलर के माध्यम से संचार करने वाले धागे हैं तो शो के लिए भी इसे लागू किया जा सकता है; कौन सा पोस्टर प्रश्न दिए गए साथ ही एक अवैध राज्य अपवाद पैदा कर सकता है

getFragmentManager().beginTransaction().add(someDialog).commitAllowingStateLoss(); 


@joneswah सही है, तो। आप समर्थन लाइब्रेरी का उपयोग कर रहे हैं, तो

getSupportFragmentManager() 


साथ

getFragmentManager() 

की जगह भविष्य Googlers के लिए: @Alex लॉकवुड इस समाधान के साथ अच्छा और वैध चिंताओं को जन्म देती है। समाधान त्रुटि को हल करता है और ज्यादातर मामलों में काम करेगा, लेकिन संकेत देता है कि मूल प्रश्न में दृष्टिकोण के साथ समस्याएं हैं, यूएक्स परिप्रेक्ष्य से।

गतिविधि को यह मानना ​​चाहिए कि async कार्य पूरा नहीं हो सकता है और यहPostExecute() पर नहीं करेगा। जो भी यूआई एक्शन (यानी स्पिनर, आदर्श नहीं है) एसिंक ऑपरेशन के उपयोगकर्ता को सूचित करने के लिए शुरू किया गया है, उसके पास समय-समय पर या राज्य को ट्रैक करके और रीस्टोर/ऑनर्यूम टाइप लाइफसाइकिल इवेंट्स को जांचने के लिए प्रावधान होना चाहिए। यूआई ठीक से अद्यतन किया गया है। सेवाएं जांच के लायक भी हो सकती हैं।

+0

बहुत बेहतर समाधान। इस पर ध्यान दिलाने के लिए धन्यवाद। –

+0

खुशी हुई यह मदद की। मुझे हाल ही में एक ही मुद्दे का सामना करना पड़ा और समाधान को ट्रैक करने में एक हास्यास्पद प्रयास किया। मुझे मिला एकमात्र अन्य मूल रूप से संगतता लाइब्रेरी स्रोत बदलता था, या कक्षा का विस्तार करता था और विधि को स्वयं लागू करता था। –

+1

यह getFrmentManager() – joneswah

0

जब एंड्रॉइड आपके ऐप को रोकता है क्योंकि उपयोगकर्ता बैक या होम बटन दबाता है, तो आपके संवाद आपके लिए बंद हो जाते हैं। आमतौर पर चाल ऑनस्टॉप()/ऑनस्टार्ट() के बीच संवाद को संरक्षित करना है। तो जब तक आपको संवाद को बंद करने से ज्यादा कुछ करने की आवश्यकता नहीं है, तो मैं कहूंगा कि इसके बारे में चिंता न करें।

संपादित करें: संवाद को होस्ट करने वाली आपकी गतिविधि पर, आप अभी भी संवाद बंद करना चाहेंगे यदि यह अभी भी STtop() के अंदर खुला है। यह स्मृति रिसाव को रोकने में मदद करता है। लेकिन इसे AsyncTask से ट्रिगर करने की आवश्यकता नहीं है।

जैसा कि मैंने उपरोक्त कहा है, समस्या तब होती है जब आप ऑनस्टार्ट() को फिर से दबाते हैं और आपका AsyncTask अभी तक समाप्त नहीं हुआ है। आपको यह निर्धारित करने के लिए एक तरीका पता लगाना होगा कि यदि आवश्यक हो तो उस संवाद को दोबारा खोलें।

+0

मेरे पास ऑनस्टॉप() और ऑनस्टार्ट() के लिए एक कार्य करने का दृष्टिकोण है। जहां समस्या आती है, पर रोकें(), यानी। मेरी गतिविधि अब दिखाई नहीं दे रही है। विराम के मामले में, संवाद खुला रहता है और AsyncTask निष्पादन जारी रहता है। एक बार कार्य पूरा होने के बाद, यह Activity.dismissDialog() का उपयोग करके प्रोग्रेसडिअलॉग को बंद करने का प्रयास करता है, जो रोके जाने पर भी ठीक काम करता है। फिर यह DialogFragment.dismiss() को कॉल करके डायलॉगफ्रैगमेंट (एसीपी संस्करण) को खारिज करने का प्रयास करता है। इसका परिणाम अमान्यस्टेट अपवाद में होता है। –

+0

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

+0

मैं यह भी सुझाव दूंगा कि आप एक सेवा पर विचार करें। सेवा तब परिणामों को आपकी गतिविधि में प्रसारित कर सकती है।इस मॉडल के साथ संवाद तक पहुंचने का कोई प्रयास आपकी गतिविधि के बाहर नहीं किया जाएगा। ईमानदार होने के लिए, कभी-कभी मैं चाहता हूं कि उन्होंने कभी भी इस तरह के कारणों के लिए AsyncTask नहीं बनाया है, यह वास्तव में किनारे के मामलों से निपटने में कठोर बनाता है। – Glaucus

3

यदि ऑनपोस्टएक्सक्यूट() यूआई को अपडेट करने जा रहा है तो आपको अपने AsyncTask को रोकें() में रद्द करना चाहिए। आपकी गतिविधि को रोक दिए जाने पर आपको UI को अपडेट करने का प्रयास नहीं करना चाहिए।

ईजी। अपने onPause() में:

if (task != null) task.cancel(true); 

आप अगली बार के लिए लागू करने के लिए काम से परिवर्तन चाहते हैं, तो doInBackground में डेटा/परिवर्तन() की दुकान और फिर यूआई अद्यतन जब अपनी गतिविधि/टुकड़ा/संवाद फिर से शुरू हो जाता है।

आप लागू करने के लिए काम से परिवर्तन नहीं करना चाहते हैं, तो onPostExecute जब तक परिवर्तन()

+0

मुझे लगता है कि एंड्रॉइड ओएस मुझे इसे स्पेगेटी में बदल रहा है, जहां गतिविधि को ट्रैक करना है कि कार्य अभी भी चल रहा है या नहीं, जब कार्य बदलता है और कार्य को पता होना चाहिए कि गतिविधि क्या है, यह तय करने के लिए कि क्या आगे आता है। अच्छा नही। मैं वास्तव में इससे बचना चाहूंगा। निश्चित रूप से इससे निपटने के लिए एक और सीधा तरीका है। और निश्चित रूप से मैं इस मुद्दे का सामना करने वाला पहला डेवलपर नहीं हूं। जब पृष्ठभूमि पृष्ठभूमि में जाती है तो किसी कार्य के परिदृश्य को संभालने के लिए किसी के पास एक साफ मुहावरे होना चाहिए। –

+0

उपरोक्त टिप्पणी को विस्तारित करने के लिए, स्पष्ट रूप से ढांचे पृष्ठभूमि में जब भी गतिविधि.dismissDialog() को सही तरीके से संभाल सकता है। DialogFragment.dismissAllowingStateLoss() पृष्ठभूमि में भी सही ढंग से काम करता है। हालांकि, एसीपी में कोई DialogFragment.dismissAllowingStateLoss() नहीं है। फिर भी, एंड्रॉइड लोगों को पहले इस स्थिति का सामना करना पड़ा होगा और इसे संभालने का एक सुझाव दिया जाना चाहिए। –

+1

बस डायलॉग फ्रैगमेंट का विस्तार करें और dismissAllowStateLoss() को खारिज करें, या जो मैं करता हूं उसे ओवरराइड करें। GetFragmentTransaction() का उपयोग करें, फिर (इसे) हटाएं, फिर commitAllowStateLoss()। ठीक काम करता है। –

0

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

  • onSaveInstanceState():

    सारांश में, मुझे क्या करना था यह है।

  • ऑनर्यूम() - मुझे विभिन्न रेज़्यूमे स्थितियों को संभालने के लिए एक स्विच स्टेटमेंट डालना पड़ा। यदि ऐप लॉन्च करना कुछ भी नहीं है, तो कुछ भी रोका नहीं गया है, अगर छिपाने के बाद या कॉन्फ़िगरेशन परिवर्तन के बाद पुनरारंभ करना है तो मैं लॉक को छोड़ देता हूं ताकि संरक्षित कुछ टास्क इंस्टेंस फिर से शुरू हो सके जहां इसे छोड़ा गया हो।
  • ऑनस्ट्रोय() - मैं कुछ टास्क रद्द करता हूं यह दौड़ रहा है।

मैं इस मूल समाधान में कोड के टुकड़े डाल दूंगा।

+0

लॉक का उपयोग करके गतिविधि के जीवन चक्र विधियों को निष्पादित करने से अवरुद्ध करना वास्तव में एक अच्छा समाधान नहीं है। –

+0

सहमत हुए। मैंने पोस्ट से हटा दिया। संकेत दिया गया सही उत्तर जाने का सही तरीका है। –

22

टुकड़े प्रत्येक गतिविधि के राज्य के हिस्से के रूप में सहेजे जाते हैं, इसलिए onSaveInstanceState() के बाद लेनदेन निष्पादित तकनीकी रूप से कहा जाता है।

आप निश्चित रूप से इस मामले में अपवाद से बचने के लिए commitAllowingStateLoss() का उपयोग नहीं करना चाहते हैं। इस परिदृश्य को उदाहरण के रूप में देखें:

  1. गतिविधि AsyncTask निष्पादित करती है। AsyncTaskonPreExecute() में दिखाता है और पृष्ठभूमि कार्य पर अपना कार्य निष्पादित करना प्रारंभ करता है।
  2. उपयोगकर्ता "होम" पर क्लिक करता है और Activity रोक दिया जाता है और पृष्ठभूमि में मजबूर हो जाता है। सिस्टम निर्णय लेता है कि डिवाइस मेमोरी पर बहुत कम है, इसलिए यह निर्णय लेता है कि इसे Activity भी नष्ट करना चाहिए।
  3. AsyncTask पूरा करता है और onPostExecute() कहा जाता है। onPostExecute() के अंदर आप अपवाद से बचने के लिए commitAllowingStateLoss() का उपयोग करके DialogFragment को खारिज कर दें।
  4. उपयोगकर्ता Activity पर वापस जाता है। FragmentManagerActivity की सहेजी गई स्थिति के आधार पर अपने टुकड़ों की स्थिति को पुनर्स्थापित करेगा। बचाया राज्य के बाद onSaveInstanceState() बुलाया गया है कुछ के बारे में पता नहीं है, तो अनुरोध को रद्द करने DialogFragment याद नहीं रखा जाएगा और DialogFragment भले ही AsyncTask पहले से ही पूरा कर लिया है बहाल हो जाएगा।

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

उम्मीद है कि कम से कम कुछ समझ में आया है। साथ ही, ध्यान दें कि Dialog एस Activity एस राज्य के हिस्से के रूप में भी मौजूद है, इसलिए एक सादे पुराने Dialog का उपयोग अपवाद से बच सकता है, तो आपको अनिवार्य रूप से एक ही समस्या होगी (यानी Dialog को खारिज कर दिया जाएगा जब Activity के बाद में राज्य बहाल किया जाता है)।

खुलकर होने के लिए, सबसे अच्छा समाधान AsyncTask की अवधि के दौरान एक संवाद प्रदर्शित होने से रोकें होगा। ActionBar (उदाहरण के लिए जी + और जीमेल ऐप्स की तरह) में एक अनिश्चित प्रगति स्पिनर दिखाने के लिए एक और अधिक उपयोगकर्ता के अनुकूल समाधान होगा। एसिंक्रोनस कॉलबैक के जवाब में उपयोगकर्ता इंटरफ़ेस में प्रमुख बदलावों के कारण उपयोगकर्ता अनुभव के लिए बुरा है क्योंकि यह अप्रत्याशित है और अचानक उपयोगकर्ता को जो कुछ भी कर रहा है उससे बाहर कर देता है।

अधिक जानकारी के लिए इस विषय पर this blog post देखें।

+0

एलेक्स, क्या आप "प्रतिबद्धAllowingStateLoss()" से बचने के लिए पृष्ठभूमि में असिंक कार्य की स्थिति को ट्रैक करने के लिए इस तरह के समाधान को कार्यान्वित करने के तरीके के बारे में स्पष्ट कर सकते हैं? आप यूआई प्रभाव और असंतुलित यूआई अनुभव की संभावना के बारे में निश्चित रूप से सही हैं। –

+0

@ एलेक्स, वास्तव में थोड़ी देर के लिए आपकी बहुत सारी चीज़ें पढ़ रहा है। मुझे लगभग एक ही समस्या है लेकिन अभी तक आपके किसी भी समाधान को लागू करने में सक्षम नहीं है। कृपया मुझे इस पर एक नज़र डालने में सहायता करें (http://stackoverflow.com/questions/22465289/cannot-perform-this-task-after-onsavedinstancestate-while-dismissing-dialog-frag), धन्यवाद। – KingBryan

+0

घूर्णन के बाद घर बटन दबाते समय, एक स्वतंत्र तरीके से लागू करने के लिए कोई विचार, गतिविधि स्थिति को ट्रैक किए बिना, एक ही समस्या का सामना करना? – karate

0

DialogFragment में getActivity().finish(); मेरे लिए काम किया।

 संबंधित मुद्दे

  • कोई संबंधित समस्या नहीं^_^