2010-01-23 18 views
33

मैं एंड्रॉइड में AsyncTasks के साथ काम कर रहा हूं और मैं किसी समस्या से निपट रहा हूं।एंड्रॉइड AsyncTask संदर्भ व्यवहार

एक सरल उदाहरण लें, एक AsyncTask के साथ एक गतिविधि लें। पृष्ठभूमि पर कार्य कुछ भी शानदार नहीं करता है, यह सिर्फ 8 सेकंड के लिए सोता है।

ऑनपोस्टएक्सक्यूट() विधि में AsyncTask के अंत में मैं केवल एक दृश्य दृश्यता स्थिति को देख रहा हूं। केवल मेरे परिणामों को सत्यापित करने के लिए।

अब, यह तब तक बढ़िया काम करता है जब तक उपयोगकर्ता AsyncTask काम नहीं कर रहा है (8 सेकंड नींद खिड़की के भीतर) अपने फोन अभिविन्यास को बदलने का फैसला करता है।

मैं एंड्रॉइड गतिविधि जीवन चक्र को समझता हूं और मुझे पता है कि गतिविधि नष्ट हो जाती है और फिर से बनाई जाती है।

यह वह जगह है जहां समस्या आती है। AsyncTask एक बटन का जिक्र कर रहा है और स्पष्ट रूप से उस संदर्भ का संदर्भ रखता है जिसने पहले स्थान पर AsyncTask शुरू किया था।

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

इसके बजाय, कोई एनपीई फेंक दिया नहीं गया है, AsyncTask सोचता है कि बटन संदर्भ शून्य नहीं है, इसे दृश्यमान करने के लिए सेट करता है। परिणाम? स्क्रीन पर कुछ भी नहीं हो रहा है!

अद्यतन: मैंने गतिविधि के लिए WeakReference को रखकर और कॉन्फ़िगरेशन परिवर्तन होने पर स्विचिंग करके इसे हल किया है। यह बोझिल है।

कोड यह रहा:

public class Main extends Activity { 

    private Button mButton = null; 
    private Button mTestButton = null; 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 

     mButton = (Button) findViewById(R.id.btnStart); 
     mButton.setOnClickListener(new OnClickListener() { 
      @Override 
      public void onClick(View v) { 
       new taskDoSomething().execute(0l); 
      } 
     }); 
     mTestButton = (Button) findViewById(R.id.btnTest); 
    } 

    private class TaskDoSomething extends AsyncTask<Long, Integer, Integer> 
    { 
     @Override 
     protected Integer doInBackground(Long... params) { 
      Log.i("LOGGER", "Starting..."); 
      try { 
       Thread.sleep(8000); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      return 0; 
     } 

     @Override 
     protected void onPostExecute(Integer result) { 
      Log.i("LOGGER", "...Done"); 
      mTestButton.setVisibility(View.VISIBLE); 
     } 
    } 
} 

यह क्रियान्वित करने की कोशिश करो और AsyncTask अपने फोन ओरिएंटेशन बदलने काम कर रहा है, जबकि।

+0

लिखने के लिए थोड़ा भ्रामक "लिखने के लिए गतिविधि को कमजोर करना" और कोड में ऐसा करने से नहीं। इससे भी बदतर, आगे आप कहते हैं "और कॉन्फ़िगरेशन परिवर्तन होने पर स्विचिंग" जिसका अर्थ वास्तव में कुछ नहीं है ... क्या स्विच कर रहा है? – Ewoks

उत्तर

23

AsyncTask नहीं बनाया गया है एक बार एक गतिविधि पुन: उपयोग किया जा करने के लिए फेंक दिया गया है और फिर से शुरू किया गया है। जैसा कि आपने कहा है, आंतरिक हैंडलर ऑब्जेक्ट बासी हो जाता है। रोवेन गाय द्वारा शेल्व उदाहरण में, वह किसी भी वर्तमान में AsyncTask के चलते सरल को रद्द करता है और फिर पोस्ट-ओरिएंटेशन परिवर्तन के बाद नए को पुनरारंभ करता है।

नई थ्रेड को अपने थ्रेड को बंद करना संभव है, लेकिन इसमें बहुत सारी नलसाजी शामिल है।कोई आम तौर पर यह करने के लिए रास्ते पर सहमति व्यक्त की है, लेकिन आप मेरी विधि के बारे में यहाँ पढ़ सकते हैं: http://foo.jasonhudgins.com/2010/03/simple-progressbar-tutorial.html

2

यह ऐसी चीज है जो मुझे हमेशा मेरी गतिविधि को उन्मूलन परिवर्तन पर नष्ट/पुनर्निर्मित करने से रोकती है।

ऐसा करने के लिए अपने मैनिफ़ेस्ट फ़ाइल में अपने <Activity> टैग से जोड़ें:

android:configChanges="orientation|keyboardHidden" 

और अपनी गतिविधि कक्षा में onConfigurationChanged ओवरराइड:

@Override 
public void onConfigurationChanged(final Configuration newConfig) 
{ 
    // Ignore orientation change to keep activity from restarting 
    super.onConfigurationChanged(newConfig); 
} 
+5

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

+0

जहां तक ​​मुझे पता है, मुख्य चीज जो आप खो देंगे, वह लैंडस्केप और पोर्ट्रेट मोड के लिए अलग-अलग गतिविधि लेआउट करने की क्षमता होगी। ऐसी कई चीजें हो सकती हैं जिन्हें आप "खो देंगे" लेकिन मुझे नहीं पता कि वे क्या हैं। –

+1

Google आम तौर पर इस दृष्टिकोण के खिलाफ अनुशंसा करता है जबतक कि आप यह नहीं जानते कि आप क्या कर रहे हैं। – emmby

2

से बचने के लिए इस आप यहाँ दूँगा जवाब का उपयोग कर सकते हैं: https://stackoverflow.com/a/2124731/327011

लेकिन अगर आप (गतिविधि को नष्ट करने की जरूरत है पोर्ट्रेट और लैंडस्केप के लिए अलग-अलग लेआउट) आप AsyncTask को सार्वजनिक कक्षा बना सकते हैं (यहां पढ़ें कि यह निजी Android: AsyncTask recommendations: private class or public class? क्यों नहीं होना चाहिए) और फिर जब भी इसे नष्ट/बनाया जाता है तो वर्तमान गतिविधि के संदर्भ को सेट करने के लिए एक विधि सेट सक्रियता बनाएं।

आप यहाँ एक उदाहरण देख सकते हैं: Android AsyncTask in external class

3

आप केवल एक संदर्भ की जरूरत है और ui सामान के लिए इसका उपयोग नहीं करेंगे तो आप बस अपने AsyncTask.You को ApplicationContext पारित कर सकते हैं अक्सर सिस्टम संसाधन के लिए संदर्भ की जरूरत है, उदाहरण के लिए।

एसिंक टास्क से UI को अपडेट करने का प्रयास न करें और कॉन्फ़िगरेशन को संभालने से बचने का प्रयास करें क्योंकि यह गन्दा हो सकता है। यूआई को अपडेट करने के लिए आप ब्रॉडकास्ट रिसीवर पंजीकृत कर सकते हैं और ब्रॉडकास्ट भेज सकते हैं।

आपके ऊपर उपरोक्त वर्णित गतिविधि से अलग सार्वजनिक वर्ग के रूप में AsyncTask भी होना चाहिए, यह परीक्षण को बहुत आसान बनाता है। दुर्भाग्य से एंड्रॉइड प्रोग्रामिंग अक्सर खराब प्रथाओं को मजबूत करती है और आधिकारिक उदाहरण मदद नहीं कर रहे हैं।

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