2012-06-06 16 views
323

से जुड़ा नहीं है मैंने एक छोटा परीक्षण ऐप बनाया है जो मेरी समस्या का प्रतिनिधित्व करता है। मैं (शेरलॉक) टुकड़ों के साथ टैब को लागू करने के लिए 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 में कोड एक unattached MyFragment पर कॉल करता है।

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

+0

क्रैश नहीं हो रहा है, आपको खंड inflater से दृश्य का उपयोग करना चाहिए। 'mView = inflater.inflate (R.layout.my_layout, कंटेनर, झूठा) 'और अब जब आप संसाधन प्राप्त करना चाहते हैं तो इस दृश्य का उपयोग करें:' mView.getResources()। ***'। यह मुझे इस बग को ठीक करने में मदद करता है। – foxis

+0

@ फॉक्सिस जो आपके 'एमवी व्यू' से जुड़ा हुआ 'संदर्भ' लीक करता है। – nhaarman

+0

हो सकता है कि मैं इसे अभी तक जांचूं। रिसाव से बचने के लिए कैसे ऑनस्ट्रॉय में शून्य 'mView' प्राप्त करने के बारे में? – foxis

उत्तर

685

मैं बहुत ही सरल जवाब मिल गया है: isAdded():

वापसी true अगर टुकड़ा वर्तमान में अपनी गतिविधि में जोड़ा जाता है।

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

जब FragmentActivity से जुड़ी नहीं है बुलाया जा रहा से onPostExecute से बचने के लिए जब रोककर या Fragment रोक AsyncTask रद्द करने के लिए है। फिर isAdded() अब और आवश्यक नहीं होगा। हालांकि, इस चेक को जगह में रखने की सलाह दी जाती है।

+0

मेरे मामले में जब मैं एक और आवेदन मंशा लॉन्च कर रहा हूं ... तो मुझे एक ही त्रुटि मिल रही है ... कोई शर्करा? – CoDe

+0

एपीआई स्तर 10 या उससे कम –

+1

@ लुकास में काम नहीं करेगा? – nhaarman

22

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

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

@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(); 
} 

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

इच्छा है कि यह किसी की मदद करे!

Thread.sleep(2000) 

AsyncTask अभी भी काम कर रहा है, यह है, क्योंकि आप नहीं था: :)

18

अपने कोड के साथ समस्या यह जिस तरह से आप, AsyncTask उपयोग कर रहे हैं जब आप अपने सोने के धागे के दौरान स्क्रीन बारी बारी वजह से है टुकड़े पुनर्निर्माण (जब आप घूमते हैं) से पहले AsstcTask उदाहरण को ठीक से Destroy() में रद्द नहीं करते हैं और जब यह वही AsyncTask उदाहरण (घुमाव के बाद) पर चलता है PostExecute(), यह पुराने खंड उदाहरण के साथ 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; 
     } 

    } 
} 
+0

इसके बजाय 'Fragments.this.getResource() का उपयोग करते हुए 'getResources()। ***' ***' मदद – Prabs

10

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

द्वारा
MainFragmentActivity.defaultInstance().getResources().getString(R.string.app_name); 
संदर्भित किया जाता का सामना करना पड़ा

आप भी

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

उपयोग कर सकते हैं मुझे आशा है कि यह मदद मिलेगी।

0

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

public class MyApplication extends Application { 
    public static Context GLOBAL_APP_CONTEXT; 

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

आप इस का उपयोग करते हैं, तो आप lifecycles के बारे में चिंता किए बिना Toast और संसाधन लोड हो रहा है के साथ भाग प्राप्त कर सकते हैं।

+5

मुझे डाउनवॉट किया जा रहा है लेकिन किसी ने भी क्यों समझाया है। स्टेटिक संदर्भ आम तौर पर खराब होते हैं लेकिन मुझे लगता है कि यदि आपके पास स्थिर अनुप्रयोग संदर्भ है तो यह स्मृति स्मृति नहीं है। –

+0

आपका उत्तर डाउनवॉटेड है क्योंकि यह सिर्फ एक हैक उचित समाधान नहीं है। @nhaarman –

2

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

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

यहां श्रोता का कोड है जो नई प्रविष्टि दिखाने के लिए प्राथमिकता सारांश अपडेट करता है।

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"); 
        } 
       } 
      } 
     }; 
     // ... 
    } 

मुझे आशा है कि यह दूसरों की मदद करेगा: यह() जो PreferenceFragment वर्ग फैली मेरी प्राथमिकताएं वर्ग की विधि onCreate में स्थित है!

14

समस्या यह है कि आप 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() कॉल एक अपवाद फेंक देगा।

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

youApplicationObject.getResources().getString(...) 
+0

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

13

उनकी इस बात के लिए काफी चाल समाधान और गतिविधि से टुकड़ा का रिसाव कर रहे हैं।

तो जो गतिविधि संदर्भ टुकड़ा से एक्सेस करते समय पर निर्भर करता है 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 


     } 
    } 
+5

isAdded() पर्याप्त है क्योंकि: अंतिम सार्वजनिक बूलियन जोड़ा गया है() { वापसी mHost! = Null && mdded; } – NguyenDat

0

मेरे मामले टुकड़ा तरीकों में इस प्रकार

के बाद बुलाया गया है स्थिति विखंडित
getActivity().onBackPressed(); 
0

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

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

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

सबसे ऊपर दिए गए उत्तर में यह शामिल है। साथ ही, 'रद्द' 'onPostExecute' को लागू होने से नहीं रोका जा सकता है। – nhaarman

+0

कॉलिंग रद्द करने पर गारंटी देता हैपोस्टएक्सक्यूट को कभी भी कॉल नहीं किया जाएगा, दोनों कॉल एक ही थ्रेड पर निष्पादित होते हैं, इसलिए आपको गारंटी है कि इसे रद्द करने के बाद इसे नहीं बुलाया जाएगा – Raz

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

काम करता है। बस कोड निष्पादन को तोड़ता है और सुनिश्चित करता है कि ऐप

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