फ्रैगमेंट MyFragment एक्टिविटी से जुड़ा नहीं है


393

मैंने एक छोटा परीक्षण ऐप बनाया है जो मेरी समस्या का प्रतिनिधित्व करता है। मैं (शर्लक) टुकड़े के साथ टैब को लागू करने के लिए ActionBarSherlock का उपयोग कर रहा हूं।

मेरा कोड: TestActivity.java

public class TestActivity extends SherlockFragmentActivity {
    private ActionBar actionBar;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setupTabs(savedInstanceState);
    }

    private void setupTabs(Bundle savedInstanceState) {
        actionBar = getSupportActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

        addTab1();
        addTab2();
    }

    private void addTab1() {
        Tab tab1 = actionBar.newTab();
        tab1.setTag("1");
        String tabText = "1";
        tab1.setText(tabText);
        tab1.setTabListener(new TabListener<MyFragment>(TestActivity.this, "1", MyFragment.class));

        actionBar.addTab(tab1);
    }

    private void addTab2() {
        Tab tab1 = actionBar.newTab();
        tab1.setTag("2");
        String tabText = "2";
        tab1.setText(tabText);
        tab1.setTabListener(new TabListener<MyFragment>(TestActivity.this, "2", MyFragment.class));

        actionBar.addTab(tab1);
    }
}

TabListener.java

public class TabListener<T extends SherlockFragment> implements com.actionbarsherlock.app.ActionBar.TabListener {
    private final SherlockFragmentActivity mActivity;
    private final String mTag;
    private final Class<T> mClass;

    public TabListener(SherlockFragmentActivity activity, String tag, Class<T> clz) {
        mActivity = activity;
        mTag = tag;
        mClass = clz;
    }

    /* The following are each of the ActionBar.TabListener callbacks */

    public void onTabSelected(Tab tab, FragmentTransaction ft) {
        SherlockFragment preInitializedFragment = (SherlockFragment) mActivity.getSupportFragmentManager().findFragmentByTag(mTag);

        // Check if the fragment is already initialized
        if (preInitializedFragment == null) {
            // If not, instantiate and add it to the activity
            SherlockFragment mFragment = (SherlockFragment) SherlockFragment.instantiate(mActivity, mClass.getName());
            ft.add(android.R.id.content, mFragment, mTag);
        } else {
            ft.attach(preInitializedFragment);
        }
    }

    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        SherlockFragment preInitializedFragment = (SherlockFragment) mActivity.getSupportFragmentManager().findFragmentByTag(mTag);

        if (preInitializedFragment != null) {
            // Detach the fragment, because another one is being attached
            ft.detach(preInitializedFragment);
        }
    }

    public void onTabReselected(Tab tab, FragmentTransaction ft) {
        // User selected the already selected tab. Usually do nothing.
    }
}

MyFragment.java

public class MyFragment extends SherlockFragment {

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

        new AsyncTask<Void, Void, Void>() {

            @Override
            protected Void doInBackground(Void... params) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException ex) {
                }
                return null;
            }

            @Override
            protected void onPostExecute(Void result){
                getResources().getString(R.string.app_name);
            }

        }.execute();
    }
}

मैंने Thread.sleepडेटा डाउनलोड करने के लिए अनुकरण करने के लिए भाग जोड़ा है । का onPostExecuteउपयोग करने के लिए अनुकरण में कोड है Fragment

जब मैं परिदृश्य और पोर्ट्रेट के बीच बहुत तेज़ी से स्क्रीन को घुमाता हूं, तो मुझे onPostExecuteकोड में एक अपवाद प्राप्त होता है :

java.lang.IllegalStateException: खंड MyFragment {410f6060} गतिविधि से संबंधित नहीं

मुझे लगता है कि यह इसलिए है क्योंकि MyFragmentइस बीच एक नया बनाया गया है, और AsyncTaskसमाप्त होने से पहले गतिविधि से जुड़ा था । onPostExecuteएक अनासक्त पर कॉल में कोड MyFragment

लेकिन मैं इसे कैसे ठीक कर सकता हूं?


1
आपको टुकड़े के प्रवाह से दृश्य का उपयोग करना चाहिए। mView = inflater.inflate(R.layout.my_layout, container, false) और अब इस दृश्य का उपयोग तब करें जब आप संसाधन प्राप्त करना चाहते हैं mView.getResources().***:। इस बग को ठीक करने में मेरी मदद करता है।
फॉक्सिस

@foxis वह लीक जो आपके Context`mView` से जुड़ी है।
नहरमन

हो सकता है कि मैं अभी तक इसकी जाँच न करूँ। लीक से बचने के लिए कैसे mViewonDestroy में शून्य पाने के बारे में?
फॉक्सिस

जवाबों:


774

मुझे बहुत सरल उत्तर मिला है isAdded():

trueयदि खंड वर्तमान में अपनी गतिविधि में जोड़ा जाता है, तो वापस लौटें ।

@Override
protected void onPostExecute(Void result){
    if(isAdded()){
        getResources().getString(R.string.app_name);
    }
}

से बचने के लिए onPostExecuteजब बुलाया जा रहा से Fragmentसे जुड़ी नहीं है Activityरद्द करने के लिए है AsyncTaskजब रोक कर या रोक Fragment। तब isAdded()जरूरी नहीं होगा। हालाँकि, इस जाँच को यथावत रखना उचित है।


मेरे मामले में जब मैं एक और एप्लिकेशन इंट्रेंट लॉन्च कर रहा हूं ... तो मुझे वही त्रुटि मिल रही है ... कोई भी शक्कर?
कॉड

1
developer.android.com/reference/android/app/… ... वहाँ भी है isDetached(), कि एपीआई स्तर 13 पर जोड़ा गया था
लुकास जोटा

5
जब API <11 पर, आप developer.android.com/reference/android/support/v4/app/… का उपयोग कर रहे हैं, जहां यह काम करेगा।
नहरमन

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

28

समस्या यह है कि आप getResources ()। GetString () का उपयोग करके संसाधनों (इस मामले में, स्ट्रिंग) तक पहुंचने का प्रयास कर रहे हैं, जो कि गतिविधि से संसाधन प्राप्त करने का प्रयास करेंगे। फ्रैगमेंट वर्ग का यह स्रोत कोड देखें:

 /**
  * Return <code>getActivity().getResources()</code>.
  */
 final public Resources getResources() {
     if (mHost == null) {
         throw new IllegalStateException("Fragment " + this + " not attached to Activity");
     }
     return mHost.getContext().getResources();
 }

mHost वह वस्तु है जो आपकी गतिविधि रखती है।

क्योंकि गतिविधि संलग्न नहीं हो सकती है, आपके getResources () कॉल एक अपवाद फेंक देंगे।

स्वीकृत समाधान IMHO जाने का तरीका नहीं है क्योंकि आप समस्या को छिपा रहे हैं। सही तरीका यह है कि संसाधनों को कहीं और से प्राप्त किया जाए, जो हमेशा मौजूद रहने की गारंटी हो, जैसे आवेदन संदर्भ:

youApplicationObject.getResources().getString(...)

मैंने इस समाधान का उपयोग किया क्योंकि मुझे getString()उस समय निष्पादित करने की आवश्यकता थी जब मेरे टुकड़े को रोक दिया गया था। धन्यवाद
Geekarist

24

मैंने यहां दो अलग-अलग परिदृश्यों का सामना किया है:

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

@Override
protected void onPostExecute(void result) {
    // do whatever you do to save data
    if (this.getView() != null) {
        // update views
    }
}

2) जब मैं चाहता हूं कि अतुल्यकालिक कार्य केवल तब समाप्त हो जाए जब विचारों को अपडेट किया जा सकता है: जिस मामले को आप यहां प्रस्तावित कर रहे हैं, वह कार्य केवल विचारों को अपडेट करता है, किसी डेटा संग्रहण की आवश्यकता नहीं है, इसलिए यह कार्य के लिए कोई सुराग नहीं है यदि विचार हैं अब नहीं दिखाया जा रहा है। मैं यह करता हूँ:

@Override
protected void onStop() {
    // notice here that I keep a reference to the task being executed as a class member:
    if (this.myTask != null && this.myTask.getStatus() == Status.RUNNING) this.myTask.cancel(true);
    super.onStop();
}

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

काश यह किसी की मदद करता! :)


18

आपके कोड के साथ समस्या यह है कि आप AsyncTask का उपयोग किस तरह से कर रहे हैं, क्योंकि जब आप अपने स्लीप थ्रेड के दौरान स्क्रीन को घुमाते हैं:

Thread.sleep(2000) 

AsyncTask अभी भी काम कर रहा है, यह इसलिए है क्योंकि आपने टुकड़े टुकड़े (जब आप घुमाते हैं) से पहले AsyncTask उदाहरण को ठीक से onDestroy () में रद्द नहीं किया और जब यह AsyncTask उदाहरण (घूमने के बाद) PPostExecute () से चलता है, तो यह खोजने की कोशिश करता है पुराने टुकड़े टुकड़े के साथ getResources () के साथ संसाधन (एक अमान्य उदाहरण):

getResources().getString(R.string.app_name)

जो इसके बराबर है:

MyFragment.this.getResources().getString(R.string.app_name)

इसलिए अंतिम समाधान स्क्रीन को घुमाए जाने पर खंड के पुनर्निर्माण से पहले AsyncTask उदाहरण (यदि यह अभी भी काम कर रहा है तो रद्द करने के लिए) को प्रबंधित करता है, और यदि संक्रमण के दौरान रद्द किया जाता है, तो एक बिलियन ध्वज की सहायता से पुनर्निर्माण के बाद AsyncTask को पुनरारंभ करें:

public class MyFragment extends SherlockFragment {

    private MyAsyncTask myAsyncTask = null;
    private boolean myAsyncTaskIsRunning = true;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if(savedInstanceState!=null) {
            myAsyncTaskIsRunning = savedInstanceState.getBoolean("myAsyncTaskIsRunning");
        }
        if(myAsyncTaskIsRunning) {
            myAsyncTask = new MyAsyncTask();
            myAsyncTask.execute();
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBoolean("myAsyncTaskIsRunning",myAsyncTaskIsRunning);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if(myAsyncTask!=null) myAsyncTask.cancel(true);
        myAsyncTask = null;

    }

    public class MyAsyncTask extends AsyncTask<Void, Void, Void>() {

        public MyAsyncTask(){}

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            myAsyncTaskIsRunning = true;
        }
        @Override
        protected Void doInBackground(Void... params) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException ex) {}
            return null;
        }

        @Override
        protected void onPostExecute(Void result){
            getResources().getString(R.string.app_name);
            myAsyncTaskIsRunning = false;
            myAsyncTask = null;
        }

    }
}

इसके बजाय अगर मदद getResources().***का उपयोग कर रहे हैंFragments.this.getResource().***
Prbs

17

इसके लिए वे काफी ट्रिक सॉल्यूशन हैं और गतिविधि से अंश का रिसाव।

तो getResource या किसी भी चीज के मामले में, जो कि फ़्रैगमेंट से एक्सेस होने वाली गतिविधि के संदर्भ पर निर्भर करता है, यह हमेशा गतिविधि की स्थिति और टुकड़े की स्थिति की जाँच करता है जो निम्नानुसार है

 Activity activity = getActivity(); 
    if(activity != null && isAdded())

         getResources().getString(R.string.no_internet_error_msg);
//Or any other depends on activity context to be live like dailog


        }
    }

7
isAdded () पर्याप्त है क्योंकि: अंतिम सार्वजनिक बूलियन AdAdded () {रिटर्न mHost! = null && AdAdded; }
गुयेनडैट

मेरे मामले में यह चेक एनओजी नहीं है, फिर भी मैं इनको जोड़ने के बावजूद दुर्घटनाग्रस्त हो रहा हूं।
डेविड

@ डेविड, isAddedकाफी है। मैंने कभी ऐसी स्थिति नहीं देखी जब getString()दुर्घटनाग्रस्त हो गया हो isAdded == true। क्या आप सुनिश्चित हैं कि एक गतिविधि दिखाई गई थी और एक टुकड़ा जुड़ा हुआ था?
CoolMind

14
if (getActivity() == null) return;

कुछ मामलों में भी काम करता है। बस इससे कोड निष्पादन को तोड़ता है और सुनिश्चित करता है कि ऐप क्रैश न हो


10

मैं एक ही समस्या का सामना करना पड़ा मैं सिर्फ सिंग्रोन का उदाहरण जोड़कर संसाधन प्राप्त करने के लिए एरिक द्वारा कहा गया है

MainFragmentActivity.defaultInstance().getResources().getString(R.string.app_name);

आप भी उपयोग कर सकते हैं

getActivity().getResources().getString(R.string.app_name);

मुझे उम्मीद है कि इससे सहायता मिलेगी।


2

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

डिबगिंग के दौरान यह ऑनक्रिएट की तरह लग रहा था () प्रेफरेंसफ्रेगमेंट की विधि दो बार कॉल की जा रही थी जब डिस्प्ले कंटेंट घुमाया गया। यह पहले से ही काफी अजीब था। फिर मैंने इस ब्लॉक के बाहर isAdded () चेक जोड़ा जहां यह दुर्घटना का संकेत होगा और इसने समस्या को हल किया।

यहां श्रोता का कोड है जो नई प्रविष्टि दिखाने के लिए वरीयताओं के सारांश को अपडेट करता है। यह मेरी प्राथमिकता वर्ग के onCreate () विधि में स्थित है, जो वरीयता वर्ग को बढ़ाता है:

public static class Preferences extends PreferenceFragment {
    SharedPreferences.OnSharedPreferenceChangeListener listener;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        // ...
        listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
            @Override
            public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
                // check if the fragment has been added to the activity yet (necessary to avoid crashes)
                if (isAdded()) {
                    // for the preferences of type "list" set the summary to be the entry of the selected item
                    if (key.equals(getString(R.string.pref_fileviewer_textsize))) {
                        ListPreference listPref = (ListPreference) findPreference(key);
                        listPref.setSummary("Display file content with a text size of " + listPref.getEntry());
                    } else if (key.equals(getString(R.string.pref_fileviewer_segmentsize))) {
                        ListPreference listPref = (ListPreference) findPreference(key);
                        listPref.setSummary("Show " + listPref.getEntry() + " bytes of a file at once");
                    }
                }
            }
        };
        // ...
    }

मुझे आशा है कि यह दूसरों की मदद करेगा!


0

यदि आप Applicationवर्ग का विस्तार करते हैं और एक स्थिर 'वैश्विक' संदर्भ वस्तु को बनाए रखते हैं, तो आप इस का उपयोग कर सकते हैं कि गतिविधि के बजाय एक स्ट्रिंग संसाधन को लोड करने के लिए।

public class MyApplication extends Application {
    public static Context GLOBAL_APP_CONTEXT;

    @Override
    public void onCreate() {
        super.onCreate();
        GLOBAL_APP_CONTEXT = this;
    }
}

यदि आप इसका उपयोग करते हैं, तो आप Toastजीवन चक्र के बारे में चिंता किए बिना संसाधन लोडिंग से दूर हो सकते हैं ।


5
मुझे नीचा दिखाया जा रहा है, लेकिन किसी ने क्यों नहीं समझाया। स्टेटिक संदर्भ आमतौर पर खराब होते हैं लेकिन मैं इस विचार का था कि यदि आपके पास स्थैतिक अनुप्रयोग संदर्भ है तो यह मेमोरी लीक नहीं है।
एंथनी चुइनार्ड

आपका उत्तर अस्वीकृत है क्योंकि यह सिर्फ हैक नहीं उचित समाधान है। @ वर्मन द्वारा साझा किए गए समाधान की जांच करें
विवेक कुमार श्रीवास्तव

0

मेरे मामले में टुकड़े के तरीकों के बाद कहा गया है

getActivity().onBackPressed();

0

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

इस के लिए उचित समाधान onStop में asynctask को रद्द करने के लिए होना चाहिए (या जहां भी आपके टुकड़े में उपयुक्त हो)। इस तरह आप स्मृति रिसाव (आपके नष्ट हुए टुकड़े का संदर्भ रखते हुए एक एसिंक्टस्क) का परिचय नहीं देते हैं और आपके पास आपके टुकड़े में क्या चल रहा है, इसका बेहतर नियंत्रण है।

@Override
public void onStop() {
    super.onStop();
    mYourAsyncTask.cancel(true);
}

1
सबसे उत्कीर्ण उत्तर इसमें शामिल हैं। साथ ही, cancelइसे onPostExecuteलागू होने से नहीं रोका जा सकता है ।
नहरमन

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