IllegalStateException: ViewPager के साथ onSaveInstanceState के बाद यह क्रिया नहीं कर सकता


496

मैं बाजार में अपने ऐप से उपयोगकर्ता रिपोर्ट प्राप्त कर रहा हूं, निम्न अपवाद वितरित कर रहा हूं:

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1109)
at android.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager.java:399)
at android.app.Activity.onBackPressed(Activity.java:2066)
at android.app.Activity.onKeyUp(Activity.java:2044)
at android.view.KeyEvent.dispatch(KeyEvent.java:2529)
at android.app.Activity.dispatchKeyEvent(Activity.java:2274)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.widget.TabHost.dispatchKeyEvent(TabHost.java:297)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2880)
at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2853)
at android.view.ViewRoot.handleMessage(ViewRoot.java:2028)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:132)
at android.app.ActivityThread.main(ActivityThread.java:4028)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:491)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
at dalvik.system.NativeStart.main(Native Method)

जाहिरा तौर पर यह एक FragmentManager के साथ कुछ करना है, जिसका मैं उपयोग नहीं करता हूं। स्टैकट्रेस मेरी अपनी किसी भी कक्षा को नहीं दिखाती है, इसलिए मुझे नहीं पता कि यह अपवाद कहां है और इसे कैसे रोका जाए।

रिकॉर्ड के लिए: मेरे पास एक टैबहोस्ट है, और प्रत्येक टैब में एक्टिविटीज के बीच एक एक्टिविटी ग्रुप है।


2
मुझे यह सवाल उसी मुद्दे पर चर्चा करते हुए मिला, लेकिन इसका कोई हल नहीं
निकला

3
जब आप उपयोग नहीं कर रहे हैं FragmentManager, तो हनीकॉम्ब निश्चित रूप से है। क्या यह असली हनीकॉम्ब टैबलेट पर हो रहा है? या यह हो सकता है कि कोई व्यक्ति हैक किए गए हनीकॉम्ब को फ़ोन या किसी चीज़ पर चला रहा हो और यह हैक किया गया संस्करण है जिसमें कठिनाई हो रही है?
कॉमन्सवेयर

1
मुझे पता नहीं है। यह एकमात्र जानकारी है जो मुझे मार्केट डेवलपर कंसोल में मिलती है, उपयोगकर्ता संदेश में कोई उपयोगी जानकारी नहीं है ..
nhaarman

मैं Flurry का उपयोग कर रहा हूं, जो मुझे एंड्रॉइड 3.0.1 के साथ 11 सत्र दिखाता है, और मेरे पास इस अपवाद की 11 रिपोर्टें हैं। हालांकि संयोग हो सकता है। Android 3.1 और 3.2 में क्रमशः 56 और 38 सत्र हैं।
नहरमन

मार्केट एरर रिपोर्ट में 'प्लेटफ़ॉर्म' अनुभाग होता है, कभी-कभी इसमें डिवाइस का Android संस्करण होता है।
बजे निकोले एलेनकोव

जवाबों:


720

कृपया मेरा उत्तर यहां देखें । मूल रूप से मुझे सिर्फ इतना करना था:

@Override
protected void onSaveInstanceState(Bundle outState) {
    //No call for super(). Bug on API Level > 11.
}

पद्धति super()पर कॉल न करें saveInstanceState। यह चीजों को गड़बड़ कर रहा था ...

यह सपोर्ट पैकेज में एक ज्ञात बग है।

यदि आपको उदाहरण सहेजने और अपने साथ कुछ जोड़ने की आवश्यकता है, तो आप outState Bundleनिम्नलिखित का उपयोग कर सकते हैं:

@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
    super.onSaveInstanceState(outState);
}

अंत में उचित समाधान (टिप्पणियों में देखा गया था) का उपयोग करने के लिए:

transaction.commitAllowingStateLoss();

जब जोड़ने या प्रदर्शन FragmentTransactionकि कारण था Exception


370
आप commitAllowingStateLoss (का उपयोग करना चाहिए) के बजाय प्रतिबद्ध ()
हुंह

18
कमेंटेटिंगस्टेटलॉस () के बारे में यह टिप्पणी अपने आप में एक जवाब है - आपको इसे उसी रूप में पोस्ट करना चाहिए।
रिसादिंह

20
'कमिटमेंटिंग' के बारे में '->> "यह खतरनाक है क्योंकि कमिट को खो दिया जा सकता है यदि गतिविधि को बाद में अपने राज्य से पुनर्स्थापित करने की आवश्यकता होती है, इसलिए इसका उपयोग केवल उन मामलों के लिए किया जाना चाहिए जहां UI स्थिति के लिए अप्रत्याशित रूप से बदलना ठीक है उपभोक्ता।"
कोड

10
अगर मैं popBackStackImmediateइसके लिए v4 स्रोत को देखता हूं तो तुरंत विफल हो जाता है अगर राज्य को बचा लिया गया है। पहले वाले टुकड़े को जोड़ने से commitAllowingStateLossकोई भाग नहीं चलता है। मेरा परीक्षण इसे सच दिखाता है। इस विशिष्ट अपवाद पर इसका कोई प्रभाव नहीं है। हमें जो चाहिए वह एक popBackStackImmediateAllowingStateLossविधि है।
सिनेसियो

3
@DanieleB हाँ, मैंने यहाँ एक उत्तर पोस्ट किया है। लेकिन मुझे वास्तव में ओटो संदेश बस का उपयोग करके एक और भी बेहतर समाधान मिला है: एक ग्राहक के रूप में टुकड़े को पंजीकृत करें और बस से async परिणाम के लिए सुनो। ठहराव पर अपंजीकृत और फिर से शुरू पर रजिस्टर। Async को उस समय के लिए एक Produce विधि की भी आवश्यकता होती है जब वह पूरा हो जाता है और टुकड़े को रोक दिया जाता है। जब मुझे समय मिलेगा तो मैं इस बारे में और अधिक विस्तार से अपना उत्तर दूंगा।
सियासियो

130

एक समान त्रुटि संदेश के साथ कई संबंधित समस्याएं हैं। इस विशेष स्टैक ट्रेस की दूसरी पंक्ति की जाँच करें। यह अपवाद विशेष रूप से कॉल से संबंधित है FragmentManagerImpl.popBackStackImmediate

यह विधि कॉल, जैसे popBackStack, हमेशा विफल रहेगी IllegalStateExceptionयदि सत्र राज्य पहले से ही सहेजा गया है। स्रोत की जाँच करें। इस अपवाद को रोकने के लिए आप कुछ नहीं कर सकते।

  • कॉल को हटाने से super.onSaveInstanceStateमदद नहीं मिलेगी।
  • फ्रैगमेंट बनाने से कोई फायदा commitAllowingStateLossनहीं होगा।

यहाँ मैंने समस्या को कैसे देखा:

  • सबमिट बटन के साथ एक फॉर्म है।
  • जब बटन पर क्लिक किया जाता है तो एक डायलॉग बनाया जाता है और एक एसिंक्स प्रक्रिया शुरू होती है।
  • प्रक्रिया समाप्त होने से पहले उपयोगकर्ता होम कुंजी पर क्लिक करता है - onSaveInstanceStateकहा जाता है।
  • प्रक्रिया पूरी हो जाती है, एक कॉलबैक किया जाता है और popBackStackImmediateप्रयास किया जाता है।
  • IllegalStateException फेंक दिया गया है।

यहाँ मैंने इसे हल करने के लिए क्या किया है:

चूंकि IllegalStateExceptionकॉलबैक से बचना संभव नहीं है , इसे पकड़ें और अनदेखा करें।

try {
    activity.getSupportFragmentManager().popBackStackImmediate(name);
} catch (IllegalStateException ignored) {
    // There's no way to avoid getting this if saveInstanceState has already been called.
}

यह ऐप को क्रैश होने से रोकने के लिए पर्याप्त है। लेकिन अब उपयोगकर्ता ऐप को पुनर्स्थापित करेगा और देखेगा कि उन्हें लगा कि बटन उन्हें दबाया गया है (उन्हें नहीं लगता)। फॉर्म का टुकड़ा अभी भी दिख रहा है!

इसे ठीक करने के लिए, जब संवाद बनाया जाता है, तो प्रक्रिया शुरू होने का संकेत देने के लिए कुछ स्थिति बनाएं।

progressDialog.show(fragmentManager, TAG);
submitPressed = true;

और इस अवस्था को बंडल में सहेजें।

@Override
public void onSaveInstanceState(Bundle outState) {
    ...
    outState.putBoolean(SUBMIT_PRESSED, submitPressed);
}

इसे फिर से लोड करने के लिए मत भूलना onViewCreated

फिर, शुरू करते समय, जमाओं को रोलबैक करें यदि सबमिट किया गया था तो पहले प्रयास किया गया था। यह उपयोगकर्ता को अन-सबमिट किए गए फ़ॉर्म की तरह वापस आने से रोकता है।

@Override
public void onResume() {
    super.onResume();
    if (submitPressed) {
        // no need to try-catch this, because we are not in a callback
        activity.getSupportFragmentManager().popBackStackImmediate(name);
        submitPressed = false;
    }
}

5
उसके बारे में यहाँ पढ़ने में दिलचस्प है: androiddesignpatterns.com/2013/08/…
पास्कल

यदि आप DialogFragment का उपयोग करते हैं, तो मैंने इसके लिए एक विकल्प बनाया है: github.com/AndroidDeveloperLB/DialogShard
Android डेवलपर

क्या होगा अगर popBackStackImmediateएंड्रॉइड द्वारा खुद को बुलाया गया था?
किमी चिउ

1
एक दम अच्छी। यह एक स्वीकृत उत्तर होना चाहिए। आपका बहुत बहुत धन्यवाद! हो सकता है कि मैं जमा जोड़ दूं = झूठी; popBackStackInmediate के बाद।
नियोनिग्मा

मैंने सार्वजनिक शून्य onSaveInstanceState (बंडल आउटस्टेट) विधि का उपयोग नहीं किया। क्या मुझे सार्वजनिक शून्य onSaveInstanceState (बंडल आउटस्टेट) के लिए खाली विधि सेट करने की आवश्यकता है?
फैक्स्रिडिन अब्दुल्लायेव

55

अगर isFinishing()टुकड़ा दिखाने से पहले गतिविधि की जाँच करें और ध्यान दें commitAllowingStateLoss()

उदाहरण:

if(!isFinishing()) {
FragmentManager fm = getSupportFragmentManager();
            FragmentTransaction ft = fm.beginTransaction();
            DummyFragment dummyFragment = DummyFragment.newInstance();
            ft.add(R.id.dummy_fragment_layout, dummyFragment);
            ft.commitAllowingStateLoss();
}

1
! फ़िनिशिंग () &&! isDestroyed () मेरे लिए काम नहीं करता है।
एलन वोर्क

isFinishing () &&! isDestroyed () ने मेरे लिए काम किया, लेकिन इसके लिए API 17 की आवश्यकता है। लेकिन यह केवल एक नहीं खेलता है DialogFragment। अन्य अच्छे समाधानों के लिए stackoverflow.com/questions/15729138/… देखें , stackoverflow.com/a/41813953/2914140 ने मेरी मदद की।
CoolMind

29

यह अक्टूबर 2017 है, और Google नई चीजों को जीवनचक्र घटक के साथ एंड्रॉइड सपोर्ट लाइब्रेरी बनाता है। यह इसके लिए कुछ नया विचार प्रदान करता है 'onSaveInstanceState' समस्या के बाद यह क्रिया नहीं कर सकता।

संक्षेप में:

  • यह निर्धारित करने के लिए जीवन चक्र घटक का उपयोग करें कि क्या यह आपके टुकड़े को पॉप करने के लिए सही समय है।

व्याख्या के साथ लंबा संस्करण:

  • यह समस्या क्यों सामने आई?

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

    आमतौर पर यह समस्या नहीं होनी चाहिए - हम हमेशा शुरुआत में ही खंड में गतिविधि को लोड करने की कोशिश करते हैं, जैसे कि onCreate()विधि इसके लिए एक आदर्श स्थान है। लेकिन कभी-कभी ऐसा होता है , खासकर जब आप यह तय नहीं कर पाते हैं कि आप उस गतिविधि में क्या टुकड़ा लोड करेंगे, या आप खंड से खंड को लोड करने की कोशिश कर रहे हैं AsyncTask(या कुछ भी थोड़ा समय लगेगा)। समय, टुकड़ा लेनदेन से पहले वास्तव में होता है, लेकिन गतिविधि की onCreate()विधि के बाद , उपयोगकर्ता कुछ भी कर सकता है। यदि उपयोगकर्ता होम बटन दबाता है, जो गतिविधि की onSavedInstanceState()विधि को ट्रिगर करता है , तो एक can not perform this actionदुर्घटना होगी।

    यदि कोई इस मुद्दे को गहराई से देखना चाहता है, तो मैं उन्हें इस ब्लॉग पोस्ट पर एक नज़र डालने का सुझाव देता हूं । यह स्रोत कोड परत के अंदर गहरा दिखता है और इसके बारे में बहुत कुछ समझाता है। इसके अलावा, यह कारण है कि आप commitAllowingStateLoss()इस दुर्घटना को हल करने के लिए विधि का उपयोग नहीं करना चाहिए (मुझे विश्वास है कि यह आपके कोड के लिए कुछ भी अच्छा नहीं है)

  • इसे कैसे ठीक करें?

    • क्या मुझे commitAllowingStateLoss()टुकड़े को लोड करने के लिए विधि का उपयोग करना चाहिए ? नहीं तुम्हें नहीं करना चाहिए ;

    • क्या मुझे onSaveInstanceStateविधि को ओवरराइड करना चाहिए , superइसके अंदर विधि को अनदेखा करना चाहिए ? नहीं तुम्हें नहीं करना चाहिए ;

    • क्या मुझे isFinishingगतिविधि के अंदर जादुई का उपयोग करना चाहिए , यह जांचने के लिए कि क्या टुकड़ा गतिविधि के लिए मेजबान गतिविधि सही समय पर है? हाँ यह ऐसा करने का सही तरीका है।

  • एक नज़र डालें कि जीवनचक्र घटक क्या कर सकता है।

    मूल रूप से, Google AppCompatActivityवर्ग (और कई अन्य आधार वर्ग जो आपको अपनी परियोजना में उपयोग करना चाहिए) के अंदर कुछ कार्यान्वयन करता है , जो वर्तमान जीवन चक्र की स्थिति को निर्धारित करना आसान बनाता है । हमारी समस्या पर एक नज़र डालें: यह समस्या क्यों होगी? ऐसा इसलिए है क्योंकि हम गलत समय पर कुछ करते हैं। इसलिए हम इसे नहीं करने की कोशिश करते हैं, और यह समस्या दूर हो जाएगी।

    मैं अपने स्वयं के प्रोजेक्ट के लिए थोड़ा कोड करता हूं, यहां मैं वह उपयोग कर रहा हूं LifeCycle। मैं कोटलिन में कोड करता हूं।

val hostActivity: AppCompatActivity? = null // the activity to host fragments. It's value should be properly initialized.

fun dispatchFragment(frag: Fragment) {
    hostActivity?.let {
       if(it.lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED)){
           showFragment(frag)
       }
    }
}

private fun showFragment(frag: Fragment) {
    hostActivity?.let {
        Transaction.begin(it, R.id.frag_container)
                .show(frag)
                .commit()
    }

जैसा कि मैं ऊपर दिखा रहा हूं। मैं होस्ट गतिविधि के जीवन चक्र की स्थिति की जांच करूंगा। समर्थन पुस्तकालय के भीतर जीवनचक्र घटक के साथ, यह अधिक विशिष्ट हो सकता है। कोड का lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED)मतलब है, यदि वर्तमान स्थिति कम से कम है onResume, तो बाद में नहीं? यह सुनिश्चित करता है कि मेरी विधि किसी अन्य जीवन स्थिति (जैसे onStop) के दौरान निष्पादित नहीं होगी ।

  • यह सब किया है?

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

    तो यहाँ मैं चाहता हूँ कि कुछ अच्छा हो सकता है: अगर यह जीवन की स्थिति की तुलना में बाद में दुर्घटनाग्रस्त हो जाता है onResume, तो लेन-देन विधि जीवन स्थिति से अवगत है; इसके अलावा, उपयोगकर्ता द्वारा हमारे ऐप पर वापस आने के बाद गतिविधि उस खंड लेनदेन कार्रवाई को समाप्त करने का प्रयास करती रहेगी।

    मैं इस विधि में कुछ और जोड़ता हूं:

class FragmentDispatcher(_host: FragmentActivity) : LifecycleObserver {
    private val hostActivity: FragmentActivity? = _host
    private val lifeCycle: Lifecycle? = _host.lifecycle
    private val profilePendingList = mutableListOf<BaseFragment>()

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun resume() {
        if (profilePendingList.isNotEmpty()) {
            showFragment(profilePendingList.last())
        }
    }

    fun dispatcherFragment(frag: BaseFragment) {
        if (lifeCycle?.currentState?.isAtLeast(Lifecycle.State.RESUMED) == true) {
            showFragment(frag)
        } else {
            profilePendingList.clear()
            profilePendingList.add(frag)
        }
    }

    private fun showFragment(frag: BaseFragment) {
        hostActivity?.let {
            Transaction.begin(it, R.id.frag_container)
                    .show(frag)
                    .commit()
        }
    }
}

मैं इस dispatcherवर्ग के अंदर एक सूची बनाए रखता हूं , उन अंशों को संग्रहीत करने के लिए लेन-देन की कार्रवाई को पूरा करने का मौका नहीं है। और जब उपयोगकर्ता होम स्क्रीन से वापस आता है और पाया जाता है कि अभी भी लॉन्च होने का इंतजार है, तो यह एनोटेशन के resume()तहत विधि में जाएगा @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)। अब मुझे लगता है कि मुझे उम्मीद की तरह काम करना चाहिए।



1
FragmentDispatcherलंबित अंशों को संग्रहीत करने के लिए सूची का उपयोग करने पर आपका क्रियान्वयन क्यों होता है यदि केवल एक ही खंड बहाल होगा?
१६

21

यहाँ इस समस्या का एक अलग समाधान है।

एक निजी सदस्य चर का उपयोग करके आप लौटाए गए डेटा को एक इरादे के रूप में सेट करने में सक्षम होते हैं जिसे तब सुपर.ऑन्यूमे () के बाद संसाधित किया जा सकता है;

इस तरह:

private Intent mOnActivityResultIntent = null; 

@Override
protected void onResume() {
    super.onResume();
    if(mOnActivityResultIntent != null){
        ... do things ...
        mOnActivityResultIntent = null;
    }
 }

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data){
    if(data != null){
        mOnActivityResultIntent = data;
    }
}

7
इस बात पर निर्भर करता है कि आप जो कार्रवाई कर रहे थे, वह अनुमति नहीं थी, इसके लिए इसे पहले से कहीं अधिक समय के लिए स्थानांतरित करना पड़ सकता है। Insatcne के लिए, यदि FragmentTransaction.commit () समस्या है, तो इसके बजाय onPostResume () में जाने की आवश्यकता है।
pjv

1
यह मेरे लिए इस सवाल का जवाब है। चूंकि मुझे पिछली गतिविधि के लिए प्राप्त एनएफसी टैग को अग्रेषित करने की आवश्यकता थी, यही मेरे लिए था।
Janis Peisenieks

7
मेरे लिए यह हो रहा था क्योंकि मैं फोन नहीं कर रहा था super.onActivityResult()
सूफियान

20

लघु और काम कर समाधान:

सरल चरणों का पालन करें

कदम

चरण 1: onSaveInstanceStateसंबंधित खंड में ओवरराइड स्थिति। और इससे सुपर मेथड को हटा दें।

 @Override
public void onSaveInstanceState( Bundle outState ) {

}  

चरण 2: उपयोग करें fragmentTransaction.commitAllowingStateLoss( );

fragmentTransaction.commit( ); जबकि टुकड़े के संचालन के बजाय ।


उत्तर को कॉपी या रेफरी फॉर्म में नहीं भेजा गया है। जहां मुझे मेरे काम करने वाले समाधान द्वारा लोगों की मदद करने के लिए पोस्ट किया गया था, जो कई परीक्षण और त्रुटि से मिला है
विनायक

12

बीवेयर , उपयोग transaction.commitAllowingStateLoss()करने से उपयोगकर्ता के लिए एक बुरा अनुभव हो सकता है। यह अपवाद क्यों फेंका गया है, इसकी अधिक जानकारी के लिए, इस पोस्ट को देखें ।


6
यह प्रश्न का उत्तर प्रदान नहीं करता है, आपको प्रश्न के लिए एक वैध उत्तर प्रदान करना होगा
उमर अता

10

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

public void onBackPressed() {}

अपने में Activityऔर backवहाँ में कुछ कोड करते हैं । भले ही पुराने उपकरणों पर ऐसी कोई विधि नहीं है, इस विधि को नए लोगों द्वारा बुलाया जाता है।


6

प्रतिबद्धता का उपयोग न करेंAllowingStateLoss (), इसका उपयोग केवल उन मामलों के लिए किया जाना चाहिए जहां उपयोगकर्ता पर अनपेक्षित रूप से बदलने के लिए UI स्थिति के लिए ठीक है।

https://developer.android.com/reference/android/app/FragmentTransaction.html#commitAllowingStateLoss ()

यदि लेनदेन पैरेंटफ्रैगमेंट के चाइल्डफ्रैगमेंट मैनजर में होता है, तो इसके बजाय चेक करने के लिए पेरेंटफ्रैगमेंट.आईएसओम () का उपयोग करें।

if (parentFragment.isResume()) {
    DummyFragment dummyFragment = DummyFragment.newInstance();
    transaction = childFragmentManager.BeginTransaction();
    trans.Replace(Resource.Id.fragmentContainer, startFragment);
}

5

मुझे एक समान समस्या थी, परिदृश्य इस प्रकार था:

  • मेरी गतिविधि सूची के टुकड़े जोड़ / बदल रही है।
  • प्रत्येक सूची के टुकड़े में गतिविधि के लिए एक संदर्भ होता है, गतिविधि को सूचित करने के लिए जब एक सूची आइटम पर क्लिक किया जाता है (पर्यवेक्षक पैटर्न)।
  • प्रत्येक सूची के टुकड़े को कॉल सेट करता है अपने onCreate विधि में।

OnCreate की विधि गतिविधि इस तरह था:

mMainFragment = (SelectionFragment) getSupportFragmentManager()
                .findFragmentByTag(MAIN_FRAGMENT_TAG);
        if (mMainFragment == null) {
            mMainFragment = new SelectionFragment();

            mMainFragment.setListAdapter(new ArrayAdapter<String>(this,
                    R.layout.item_main_menu, getResources().getStringArray(
                            R.array.main_menu)));
mMainFragment.setOnSelectionChangedListener(this);
            FragmentTransaction transaction = getSupportFragmentManager()
                    .beginTransaction();
            transaction.add(R.id.content, mMainFragment, MAIN_FRAGMENT_TAG);
            transaction.commit();
        }

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

इसे लागू करने से समस्या हल हो गई:

mMainFragment = (SelectionFragment) getSupportFragmentManager()
                .findFragmentByTag(MAIN_FRAGMENT_TAG);
        if (mMainFragment == null) {
            mMainFragment = new SelectionFragment();

            mMainFragment.setListAdapter(new ArrayAdapter<String>(this,
                    R.layout.item_main_menu, getResources().getStringArray(
                            R.array.main_menu)));
            FragmentTransaction transaction = getSupportFragmentManager()
                    .beginTransaction();
            transaction.add(R.id.content, mMainFragment, MAIN_FRAGMENT_TAG);
            transaction.commit();
        }
        mMainFragment.setOnSelectionChangedListener(this);

आपको हर बार अपने श्रोताओं को सेट करने की आवश्यकता होती है जब गतिविधि उस स्थिति से बचने के लिए बनाई जाती है जहां टुकड़ों में गतिविधि के पुराने नष्ट हुए उदाहरणों के संदर्भ होते हैं।


5

यदि आपको विरासत में मिली है FragmentActivity, तो आपको सुपरक्लास में कॉल करना होगा onActivityResult():

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    super.onActivityResult(requestCode, resultCode, intent);
    ...
}

यदि आप ऐसा नहीं करते हैं और उस पद्धति में एक खंड संवाद बॉक्स दिखाने की कोशिश करते हैं, तो आप ओपी प्राप्त कर सकते हैं IllegalStateException। (ईमानदार होने के लिए, मुझे यह समझ में नहीं आया कि सुपर कॉल समस्या को क्यों ठीक करता है। onActivityResult()इसे पहले कहा जाता है onResume(), इसलिए इसे अभी भी खंड संवाद बॉक्स दिखाने की अनुमति नहीं दी जानी चाहिए।)


1
यह जानना पसंद करेंगे कि यह समस्या को क्यों ठीक करता है।
बिग मैक्लेरजैग

3

मुझे यह अपवाद तब मिल रहा था जब मैं अपने नक्शे विखंडन गतिविधि पर इरादा चयनकर्ता को रद्द करने के लिए बैक बटन दबा रहा था। मैंने onResume (जहां मैं टुकड़े को इनिशियलाइज़ कर रहा था) को onstart () में बदलकर इसे हल किया और ऐप ठीक काम कर रहा है। हालांकि यह मदद करता है।


2

मुझे लगता है कि उपयोग transaction.commitAllowingStateLoss();करना सबसे अच्छा समाधान नहीं है। जब गतिविधि का कॉन्फ़िगरेशन बदल onSavedInstanceState()जाता है और खंड को कॉल किया जाता है और इसके बाद आपके async कॉलबैक विधि के टुकड़े करने की कोशिश करता है, तो यह अपवाद फेंक दिया जाएगा ।

सरल समाधान यह जांच सकता है कि गतिविधि कॉन्फ़िगरेशन बदल रही है या नहीं

उदाहरण के लिए जाँच करें isChangingConfigurations()

अर्थात

if(!isChangingConfigurations()) { //commit transaction. }

इस लिंक को भी चेकआउट करें


किसी तरह मुझे यह अपवाद तब मिला जब उपयोगकर्ता किसी चीज़ पर क्लिक करता है (लेन-देन करने के लिए ट्रिगर ट्रिगर है)। यह कैसे हो सकता है? क्या आपका समाधान यहां होगा?
एंड्रॉयड डेवलपर

@androiddeveloper उपयोगकर्ता क्लिक पर और क्या कर रहे हैं। किसी भी तरह का टुकड़ा लेन
अमोल देसाई

अपवाद लेन-देन की प्रतिबद्ध लाइन पर फेंक दिया गया था। इसके अलावा, मेरे पास एक अजीब टाइपो था: "यहाँ यहाँ" के बजाय मेरा मतलब था "यहाँ काम"।
एंड्रॉयड डेवलपर

@androiddeveloper आप सही हैं! लेकिन लेन-देन करने से पहले आप किसी भी पृष्ठभूमि धागा या कुछ और पैदा कर रहे हैं?
अमोल देसाई

मुझे ऐसा नहीं लगता (माफ करना, मैं कार्यालय से बाहर हूं), लेकिन यह क्यों मायने रखेगा? यह सब यूआई सामान यहां है ... अगर मैं कभी भी पृष्ठभूमि के धागे पर कुछ बनाता हूं, तो मेरे पास वहां अपवाद होंगे, साथ ही मैं पृष्ठभूमि से संबंधित यूआई से संबंधित सामान नहीं रखता, क्योंकि यह बहुत जोखिम भरा है।
एंड्रॉइड डेवलपर

2

संभवतः मेरे मामले में मुझे जो सबसे आसान और सरल उपाय मिला, वह था एक्टिविटी रिजल्ट के जवाब में स्टैक से आपत्तिजनक टुकड़ा निकालने से बचने के लिए। इसलिए इस कॉल को मेरे में बदलना onActivityResult():

popMyFragmentAndMoveOn();

इसके लिए:

new Handler(Looper.getMainLooper()).post(new Runnable() {
    public void run() {
        popMyFragmentAndMoveOn();
    }
}

मेरे मामले में मदद की।


2

यदि आप onActivityResult में कुछ FragmentTransaction कर रहे हैं, तो आप क्या कर सकते हैं आप onActivityResult के अंदर कुछ बूलियन मान सेट कर सकते हैं फिर onResume में आप अपने FragmentTransaction को बूलियन मान के आधार पर कर सकते हैं। कृपया नीचे दिए गए कोड को देखें।

@Override
protected void onResume() {
    super.onResume;
    if(isSwitchFragment){
        isSwitchFragment=false;
        bottomNavigationView.getTabAt(POS_FEED).select();
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == FilterActivity.FILTER_REQUEST_EVENT && data != null) {
        isSwitchFragment=true;
    }
}

कृपया कोड को छवि के रूप में पोस्ट न करें, इसके बजाय कोड स्वरूपण का उपयोग करें।
मीचर

1
हाँ इसने मेरी मदद की .., मार्शमैलो डिवाइस का यह सटीक और उचित समाधान है, मुझे यह अपवाद मिला और इस सरल चाल से हल किया .. !! इसलिए मतदान हुआ।
संध्या

2

सौजन्य: IllegalStateException के लिए समाधान

इस मुद्दे ने मुझे बहुत समय के लिए परेशान किया था लेकिन सौभाग्य से मैं इसके लिए एक ठोस समाधान के साथ आया था। इसकी एक विस्तृत व्याख्या यहाँ है

प्रतिबद्धता का उपयोग करते हुएAllowStateloss () इस अपवाद को रोक सकता है, लेकिन UI अनियमितताओं को जन्म देगा। अब तक हम समझ चुके हैं कि जब हम राज्य को बहाल करते हैं, तब तक अवैध गतिविधि का सामना करने के बाद IllegalStateException का सामना करना पड़ता है। .यह बस ऐसे ही किया जा सकता है

दो निजी बूलियन चर घोषित करें

 public class MainActivity extends AppCompatActivity {

    //Boolean variable to mark if the transaction is safe
    private boolean isTransactionSafe;

    //Boolean variable to mark if there is any transaction pending
    private boolean isTransactionPending;

अब onPostResume () और onPause में हम अपने बूलियन वैरिएबल isTransactionSafe को सेट और अनसेट करते हैं। आइडिया को ट्रैसनेशन को तभी सुरक्षित करना है जब गतिविधि अग्रभूमि में हो, ताकि स्टेटलॉस का कोई मौका न हो।

/*
onPostResume is called only when the activity's state is completely restored. In this we will
set our boolean variable to true. Indicating that transaction is safe now
 */
public void onPostResume(){
    super.onPostResume();
    isTransactionSafe=true;
}
/*
onPause is called just before the activity moves to background and also before onSaveInstanceState. In this
we will mark the transaction as unsafe
 */

public void onPause(){
    super.onPause();
    isTransactionSafe=false;

}

private void commitFragment(){
    if(isTransactionSafe) {
        MyFragment myFragment = new MyFragment();
        FragmentManager fragmentManager = getFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.add(R.id.frame, myFragment);
        fragmentTransaction.commit();
    }
}

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

public void onPostResume(){
   super.onPostResume();
   isTransactionSafe=true;
/* Here after the activity is restored we check if there is any transaction pending from
the last restoration
*/
   if (isTransactionPending) {
      commitFragment();
   }
}


private void commitFragment(){

 if(isTransactionSafe) {
     MyFragment myFragment = new MyFragment();
     FragmentManager fragmentManager = getFragmentManager();
     FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
     fragmentTransaction.add(R.id.frame, myFragment);
     fragmentTransaction.commit();
     isTransactionPending=false;
 }else {
     /*
     If any transaction is not done because the activity is in background. We set the
     isTransactionPending variable to true so that we can pick this up when we come back to
foreground
     */
     isTransactionPending=true;
 }
}

2

विखंडन लेनदेन के बाद निष्पादित नहीं किया जाना चाहिए Activity.onStop()! जांचें कि आपके पास कोई कॉलबैक नहीं है जो लेनदेन के बाद निष्पादित कर सकता है onStop()। इस तरह के दृष्टिकोण के साथ समस्या के चारों ओर चलने की कोशिश करने के बजाय कारण को ठीक करना बेहतर है.commitAllowingStateLoss()


1

समर्थन लाइब्रेरी संस्करण 24.0.0 से शुरू करके आप उस FragmentTransaction.commitNow()पद्धति को कॉल कर सकते हैं जो commit()बाद में कॉल करने के बजाय इस लेनदेन को सिंक्रोनाइज़ करती है executePendingTransactions()। जैसा कि प्रलेखन कहता है कि यह दृष्टिकोण और भी बेहतर है:

कमिटिंग कॉल करना कमिटेड कॉल करने के लिए बेहतर है () निष्पादित करने के बाद निष्पादित करेंट्रेनट्रांस () के रूप में बाद में सभी लंबित लेनदेन करने के प्रयास का दुष्प्रभाव होगा कि क्या वांछित व्यवहार है या नहीं।


1

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

आप टुकड़ा लोड करने के लिए transaction.commit () के बजाय transaction.commitAllowingStateLoss () का उपयोग कर सकते हैं

या

एक बूलियन बनाएं और जांचें कि क्या गतिविधि को रोकना नहीं है

@Override
public void onResume() {
    super.onResume();
    mIsResumed = true;
}

@Override
public void onPause() {
    mIsResumed = false;
    super.onPause();
}

तब टुकड़ा जाँच लोड करते समय

if(mIsResumed){
//load the your fragment
}

1

इस समस्या को दरकिनार करने के लिए, हम द नेविगेशन आर्किटेक्चर घटक का उपयोग कर सकते हैं , जिसे Google I / O 2018 में पेश किया गया था। नेविगेशन आर्किटेक्चर घटक एंड्रॉइड ऐप में नेविगेशन के कार्यान्वयन को सरल बनाता है।


यह काफी अच्छा नहीं है, और बगिया (खराब deeplink हैंडलिंग, राज्य शो को बचाने / टुकड़े को छिपाने और कुछ महत्वपूर्ण मुद्दा जो अभी भी खुला नहीं है)
do01

1

@Anthonyeef के महान जवाब के संबंध में, यहां जावा में एक नमूना कोड दिया गया है:

private boolean shouldShowFragmentInOnResume;

private void someMethodThatShowsTheFragment() {

    if (this.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
        showFragment();
    } else {
        shouldShowFragmentInOnResume = true;
    }
}

private void showFragment() {
    //Your code here
}

@Override
protected void onResume() {
    super.onResume();

    if (shouldShowFragmentInOnResume) {
        shouldShowFragmentInOnResume = false;
        showFragment();
    }
}

1

यदि आपके पास popBackStack () या popBackStackImmediate () विधि है तो कृपया इसके साथ ठीक करने का प्रयास करें:

        if (!fragmentManager.isStateSaved()) {
            fragmentManager.popBackStackImmediate();
        }

यह मेरे लिए भी काम किया है।


ध्यान दें कि इसके लिए API 26 और इसके बाद के संस्करण की आवश्यकता है
Itay Feldman

1

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

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data); //<--- THIS IS THE SUPPER CALL
    if (resultCode == Activity.RESULT_OK && requestCode == 0) {
        mostrarFragment(FiltroFragment.newInstance())
    }

}

हो सकता है कि आपको अपने कोड से पहले कर रहे किसी भी ओवरराइड पर बस 'सुपर' जोड़ना होगा।


1

कोटलिन एक्सटेंशन

fun FragmentManager?.replaceAndAddToBackStack(
    @IdRes containerViewId: Int,
    fragment: () -> Fragment,
    tag: String
) {
    // Find and synchronously remove a fragment with the same tag.
    // The second transaction must start after the first has finished.
    this?.findFragmentByTag(tag)?.let {
        beginTransaction().remove(it).commitNow()
    }
    // Add a fragment.
    this?.beginTransaction()?.run {
        replace(containerViewId, fragment, tag)
        // The next line will add the fragment to a back stack.
        // Remove if not needed.
        // You can use null instead of tag, but tag is needed for popBackStack(), 
        // see https://stackoverflow.com/a/59158254/2914140
        addToBackStack(tag)
    }?.commitAllowingStateLoss()
}

उपयोग:

val fragment = { SomeFragment.newInstance(data) }
fragmentManager?.replaceAndAddToBackStack(R.id.container, fragment, SomeFragment.TAG)

आप Fragment से पहले () -> को हटा सकते हैं
क्लेयर

@ धन्यवाद, धन्यवाद! क्या आप बदलने का मतलब है fragment: Fragment? हां, मैंने इस प्रकार की कोशिश की, लेकिन इस मामले में सभी मामलों में एक टुकड़ा बनाया जाएगा (तब भी जब टुकड़ा प्रबंधन == अशक्त हो, लेकिन मैंने इस स्थिति का सामना नहीं किया)। मैंने जवाब अपडेट किया और नल को टैग में बदल दिया addToBackStack()
कूलमाइंड

1

यह दुर्घटना एक FragmentTransaction के कारण होने वाली है क्योंकि इसकी गतिविधि का जीवनचक्र पहले ही onSaveInstanceState चला चुका है। यह अक्सर एक अतुल्यकालिक कॉलबैक से FragmentTransactions करने के कारण होता है। अधिक जानकारी के लिए लिंक किए गए संसाधन की जाँच करें।

टुकड़ा लेन-देन और गतिविधि राज्य हानि

http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html


0

इसे अपनी गतिविधि में जोड़ें

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    if (outState.isEmpty()) {
        // Work-around for a pre-Android 4.2 bug
        outState.putBoolean("bug:fix", true);
    }
}

0

मैंने भी इस मुद्दे का अनुभव किया है और समस्या हर बार तब होती है जब आपका संदर्भ FragmentActivityबदल जाता है (जैसे स्क्रीन ओरिएंटेशन बदल जाता है, आदि)। तो इसके लिए सबसे अच्छा फिक्स अपने से संदर्भ को अद्यतन करना है FragmentActivity


0

मैं एक आधार टुकड़ा बनाने के साथ समाप्त हो गया और अपने ऐप में सभी टुकड़ों को विस्तारित कर दिया

public class BaseFragment extends Fragment {

    private boolean mStateSaved;

    @CallSuper
    @Override
    public void onSaveInstanceState(Bundle outState) {
        mStateSaved = true;
        super.onSaveInstanceState(outState);
    }

    /**
     * Version of {@link #show(FragmentManager, String)} that no-ops when an IllegalStateException
     * would otherwise occur.
     */
    public void showAllowingStateLoss(FragmentManager manager, String tag) {
        // API 26 added this convenient method
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            if (manager.isStateSaved()) {
                return;
            }
        }

        if (mStateSaved) {
            return;
        }

        show(manager, tag);
    }
}

फिर जब मैं एक टुकड़े को दिखाने की कोशिश करता हूं तो मैं उपयोग करता हूं showAllowingStateLoss बजायshow

इस तरह:

MyFragment.newInstance()
.showAllowingStateLoss(getFragmentManager(), MY_FRAGMENT.TAG);

मैं इस पीआर से इस समाधान के लिए आया था: https://github.com/googlesamples/easypermissions/pull/170/files


0

एक और संभव समाधान, जो मुझे यकीन नहीं है कि अगर सभी मामलों में मदद मिलती है ( यहां मूल ):

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        final View rootView = findViewById(android.R.id.content);
        if (rootView != null) {
            rootView.cancelPendingInputEvents();
        }
    }
}

0

मुझे पता है कि @Ovidiu Latcu द्वारा एक स्वीकृत उत्तर है लेकिन कुछ समय बाद, त्रुटि अभी भी बनी हुई है।

@Override
protected void onSaveInstanceState(Bundle outState) {
     //No call for super(). Bug on API Level > 11.
}

Crashlytics अभी भी मुझे यह अजीब त्रुटि संदेश भेज रहा है।

हालाँकि अब केवल 7+ ( नूगाट ) संस्करण पर होने वाली त्रुटि मेरे फिक्स का उपयोग करने के लिए किया गया था। के रूप में कमिट () के बजाय ।

यह पोस्ट कमिटेटिंगस्टेटलॉस () के लिए मददगार है और कभी भी इसके टुकड़े का मुद्दा कभी सामने नहीं आया।

इसे सम्‍मिलित करने के लिए, यहां स्‍वीकार किया गया उत्तर पूर्व नूगट Android संस्‍करणों पर काम कर सकता है।

यह खोज के कुछ घंटों तक किसी को बचा सकता है। खुश कोडिंग। <3 चीयर्स

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