चेतावनी: यह AsyncTask वर्ग स्थिर होना चाहिए या लीक हो सकता है


270

मुझे अपने कोड में एक चेतावनी मिल रही है जिसमें कहा गया है:

यह AsyncTask वर्ग स्थिर होना चाहिए या लीक हो सकता है (अनाम android.os.AsyncTask)

पूरी चेतावनी है:

यह AsyncTask क्लास स्टैटिक होनी चाहिए या लीक हो सकती है (अनाम android.os.AsyncTask) एक स्टैटिक फील्ड सेफ्टी लीक होगी। गैर-स्थिर आंतरिक कक्षाओं में उनके बाहरी वर्ग का एक अंतर्निहित संदर्भ होता है। यदि वह बाहरी वर्ग उदाहरण के लिए एक टुकड़ा या गतिविधि है, तो इस संदर्भ का अर्थ है कि लंबे समय तक चलने वाला हैंडलर / लोडर / कार्य उस गतिविधि का एक संदर्भ रखेगा जो इसे कचरा एकत्र करने से रोकता है। इसी तरह, इन लंबे समय तक चलने वाले उदाहरणों से गतिविधियों और टुकड़ों के प्रत्यक्ष क्षेत्र के संदर्भ लीक का कारण बन सकते हैं। ViewModel वर्गों को कभी भी दृश्य या गैर-अनुप्रयोग संदर्भों की ओर संकेत नहीं करना चाहिए।

यह मेरा कोड है:

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

        @Override
        protected Void doInBackground(Void... params) {
            runOnUiThread(new Runnable() {

                @Override
                public void run() {
                    mAdapter.notifyDataSetChanged();
                }
            });

            return null;
        }
    }.execute();

मैं इसे कैसे ठीक करूं?


2
इस androiddesignpatterns.com/2013/01// को पढ़कर आपको यह संकेत देना चाहिए कि यह स्थिर क्यों होना चाहिए
रघुनंदन

अब तक, मैं हमेशा AsyncTask को नए थ्रेड (...) के साथ बदलने में सक्षम रहा हूं ... (यदि आवश्यक हो तो runOnUiThread (...) के साथ संयोजन में (तो), इसलिए मुझे अब इस चेतावनी से निपटने की आवश्यकता नहीं है।
हांग

1
इस समस्या के लिए कोटलिन में क्या समाधान है?
तपनहप

कृपया पुन: विचार करें कि कौन सा उत्तर स्वीकार किया जाना चाहिए। नीचे उत्तर देखें।
.मेगा

मेरे मामले में, मुझे एक सिंगलटन से यह चेतावनी मिलती है जिसमें गतिविधि का कोई सीधा संदर्भ नहीं है (यह myActivity.getApplication()रूमबेल कक्षाओं और अन्य कक्षाओं को आरम्भ करने के लिए, सिंगलटन के लिए निजी निर्माणकर्ता का उत्पादन प्राप्त करता है )। मेरे ViewModels को DB पर कुछ संचालन करने के लिए निजी संदर्भ के रूप में सिंगलटन उदाहरण मिलता है। तो, ViewModels सिंगलटन पैकेज और साथ ही android.app.Applicationउनमें से एक को भी आयात करता है android.app.Activity। चूंकि "सिंगलटन" को काम करने के लिए उन ViewModels को आयात करने की आवश्यकता नहीं है, इसलिए भी, क्या मेमोरी लीक हो सकती है?
सेबासबीएम

जवाबों:


64

गैर-स्थिर आंतरिक वर्ग में युक्त वर्ग का संदर्भ होता है। जब आप AsyncTaskएक आंतरिक वर्ग के रूप में घोषित करते हैं , तो यह युक्त Activityवर्ग की तुलना में अधिक समय तक जीवित रह सकता है । इसका कारण है कि इसमें निहित वर्ग का संदर्भ है। यह गतिविधि को कचरा एकत्र करने से रोकेगा, इसलिए स्मृति रिसाव।

अपनी समस्या को हल करने के लिए, या तो अनाम, स्थानीय और आंतरिक वर्ग के बजाय स्थिर नेस्टेड क्लास का उपयोग करें या शीर्ष-स्तरीय वर्ग का उपयोग करें।


1
समाधान चेतावनी में ही निहित है। या तो स्थिर नेस्टेड क्लास या एक शीर्ष स्तर वर्ग का उपयोग करें।
आनंद

3
@KeyurNimavat मुझे लगता है कि आप अपनी गतिविधि के लिए एक कमजोर संदर्भ में पारित कर सकते हैं
peterchaula

42
तो AsyncTask का उपयोग करने का क्या मतलब है? अगर थ्रेड की रन विधि में अंत में नया थ्रेड और हैंडलर.पोस्ट या व्यू.पोस्ट (यूआई को अपडेट करना) चलाना आसान है। यदि AsyncTask स्थिर या उच्च-स्तरीय वर्ग है, तो इसके लिए आवश्यक चर / विधियों तक पहुंचना मुश्किल है
user924

8
इसका उपयोग करने का सही तरीका कोई कोड प्रदान नहीं किया गया है। मैंने कभी भी वहाँ स्थैतिक लगाने की कोशिश की, लेकिन अधिक चेतावनी और त्रुटि दिखाई
जाएगी

19
@ आनंद इस उत्तर को हटा दें ताकि stackoverflow.com/a/46166223/145119 पर अधिक उपयोगी उत्तर शीर्ष पर हो सके।
मितलदू

555

एक स्थिर आंतरिक AsyncTask वर्ग का उपयोग कैसे करें

लीक को रोकने के लिए, आप आंतरिक वर्ग को स्थिर बना सकते हैं। हालांकि, इसके साथ समस्या यह है कि अब आपके पास गतिविधि के UI विचारों या सदस्य चर तक पहुंच नहीं है। आप एक संदर्भ में पास कर सकते हैं, Contextलेकिन फिर आप मेमोरी रिसाव के समान जोखिम को चलाते हैं। (यदि AsyncTask वर्ग के पास इसका एक मजबूत संदर्भ है, तो इसे बंद करने के बाद Android गतिविधि को एकत्र नहीं कर सकता।) समाधान गतिविधि (या जो भी Contextआपको आवश्यक है) के लिए एक कमजोर संदर्भ बनाने के लिए है ।

public class MyActivity extends AppCompatActivity {

    int mSomeMemberVariable = 123;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // start the AsyncTask, passing the Activity context
        // in to a custom constructor 
        new MyTask(this).execute();
    }

    private static class MyTask extends AsyncTask<Void, Void, String> {

        private WeakReference<MyActivity> activityReference;

        // only retain a weak reference to the activity 
        MyTask(MyActivity context) {
            activityReference = new WeakReference<>(context);
        }

        @Override
        protected String doInBackground(Void... params) {

            // do some long running task...

            return "task finished";
        }

        @Override
        protected void onPostExecute(String result) {

            // get a reference to the activity if it is still there
            MyActivity activity = activityReference.get();
            if (activity == null || activity.isFinishing()) return;

            // modify the activity's UI
            TextView textView = activity.findViewById(R.id.textview);
            textView.setText(result);

            // access Activity member variables
            activity.mSomeMemberVariable = 321;
        }
    }
}

टिप्पणियाँ

  • जहां तक ​​मुझे पता है, इस प्रकार का मेमोरी लीक खतरा हमेशा सच रहा है, लेकिन मैंने केवल एंड्रॉइड स्टूडियो 3.0 में चेतावनी देखना शुरू किया। बहुत सारे मुख्य AsyncTaskट्यूटोरियल अभी भी इससे नहीं निपटते हैं ( यहां देखें , यहां , यहां और यहां देखें )।
  • यदि आप AsyncTaskएक शीर्ष स्तर के वर्ग थे, तो आप भी इसी तरह की प्रक्रिया का पालन करेंगे । एक स्थिर आंतरिक वर्ग मूल रूप से जावा में शीर्ष स्तर के वर्ग के समान है।
  • यदि आपको स्वयं गतिविधि की आवश्यकता नहीं है, लेकिन फिर भी संदर्भ (उदाहरण के लिए Toast) प्रदर्शित करना चाहते हैं , तो आप ऐप संदर्भ के संदर्भ में पास कर सकते हैं। इस मामले में AsyncTaskनिर्माणकर्ता इस तरह दिखेगा:

    private WeakReference<Application> appReference;
    
    MyTask(Application context) {
        appReference = new WeakReference<>(context);
    }
  • इस चेतावनी को अनदेखा करने और गैर-स्थिर वर्ग का उपयोग करने के लिए कुछ तर्क दिए गए हैं। आखिरकार, AsyncTask को बहुत कम समय तक रहने का इरादा है (सबसे लंबे समय में एक युगल सेकंड), और यह गतिविधि के संदर्भ को तब जारी करेगा जब यह किसी भी तरह खत्म हो जाएगा। यह और यह देखें ।
  • उत्कृष्ट लेख: कैसे एक संदर्भ लीक करने के लिए: हैंडलर और इनर क्लासेस

Kotlin

कोटलिन में केवल आंतरिक वर्ग के लिए कीवर्ड शामिल नहीं हैinner । यह डिफ़ॉल्ट रूप से इसे स्थिर बनाता है।

class MyActivity : AppCompatActivity() {

    internal var mSomeMemberVariable = 123

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // start the AsyncTask, passing the Activity context
        // in to a custom constructor
        MyTask(this).execute()
    }

    private class MyTask
    internal constructor(context: MyActivity) : AsyncTask<Void, Void, String>() {

        private val activityReference: WeakReference<MyActivity> = WeakReference(context)

        override fun doInBackground(vararg params: Void): String {

            // do some long running task...

            return "task finished"
        }

        override fun onPostExecute(result: String) {

            // get a reference to the activity if it is still there
            val activity = activityReference.get()
            if (activity == null || activity.isFinishing) return

            // modify the activity's UI
            val textView = activity.findViewById(R.id.textview)
            textView.setText(result)

            // access Activity member variables
            activity.mSomeMemberVariable = 321
        }
    }
}

1
@ManojFrekzz, नहीं, वास्तव में आप पास की गई गतिविधि के कमजोर संदर्भ का उपयोग करके UI को अपडेट कर सकते हैं , onPostExecuteऊपर दिए गए कोड में फिर से मेरी विधि देखें । आप देख सकते हैं कि मैंने TextViewवहां UI अपडेट किया है । activity.findViewByIdआपको जो भी यूआई तत्व अपडेट करने की आवश्यकता है उसका संदर्भ प्राप्त करने के लिए बस उपयोग करें।
9

7
+1। यह सबसे अच्छा और साफ समाधान है जो मैंने कभी देखा है! केवल, यदि आप onPostExecute विधि में UI को संशोधित करना चाहते हैं, तो आपको यह भी देखना चाहिए कि क्या गतिविधि नष्ट हो रही है: गतिविधि .isishing ()
zapotec

1
ध्यान दें! इस उत्तर का उपयोग करते समय, मैं नल पॉइंटर अपवादों में भागता रहा, क्योंकि डॉकब्लैक ऑपरेशन मेमोरी इंटेंसिव था, कचरा संग्रहित था, कमजोर संग्रह एकत्र किया, और एसिंक्टस्क को मार दिया। यदि आप जानते हैं कि आपके बैकग्राउंड ऑपरेशन अधिक मेमोरी इंटेंसिव हैं, तो आप एक कमजोर के बजाय सॉफ्टराइज़र का उपयोग कर सकते हैं।
PGMacDesign

2
@Sunny, गतिविधि के बजाय Fragment के संदर्भ में पास करें। आप activity.isFinishing()चेक निकाल लेंगे और संभवतः इसे fragment.isRemoving()चेक से बदल देंगे । मैंने हाल ही में टुकड़ों के साथ ज्यादा काम नहीं किया है, हालांकि।
सुरगाच

1
@ बशन, (1) यदि बाहरी वर्ग कोई गतिविधि नहीं है, तो अपने AsyncTaskनिर्माता में आप अपने बाहरी वर्ग के संदर्भ में पास होते हैं। और doInBackground()आप बाहरी वर्ग के साथ एक संदर्भ प्राप्त कर सकते हैं MyOuterClass ref = classReference.get()। के लिए जाँच करें null। (2) onPostExecute()आप पृष्ठभूमि कार्य के परिणामों के साथ केवल यूआई को अपडेट कर रहे हैं। यह किसी भी समय आप यूआई को अपडेट करने की तरह ही है। इसके लिए जांच activity.isFinishing()केवल यह सुनिश्चित करने के लिए है कि गतिविधि पहले से ही समाप्त नहीं हुई है, जिस स्थिति में यह यूआई को अपडेट करने के लिए व्यर्थ होगा।
सुरगाछ

23

यह AsyncTaskवर्ग स्थिर होना चाहिए या लीक हो सकता है क्योंकि

  • जब Activityनष्ट हो जाता है, AsyncTask(दोनों staticया non-static) अभी भी चल रहा है
  • यदि आंतरिक वर्ग non-static( AsyncTask) वर्ग है, तो इसमें बाहरी वर्ग ( Activity) का संदर्भ होगा ।
  • यदि किसी वस्तु का कोई संदर्भ बिंदु नहीं है, तो Garbage Collectedउसे जारी करेगा। यदि कोई ऑब्जेक्ट अप्रयुक्त है और इसे रिलीज़ Garbage Collected नहीं कर सकता है => रिसाव मेमोरी

=> यदि AsyncTaskहै non-static, Activityतो यह नष्ट हो जाने वाली घटना को जारी नहीं करेगा = लीक

रिसाव के बिना स्थिर वर्ग के रूप में AsyncTask करने के बाद अद्यतन यूआई के लिए समाधान

1) WeakReference@Suragch जवाब
2 की तरह उपयोग करें ) भेजें और Activity(से) के लिए संदर्भ निकालेंAsyncTask

public class NoLeakAsyncTaskActivity extends AppCompatActivity {
    private ExampleAsyncTask asyncTask;

    @Override 
    protected void onCreate(Bundle savedInstanceState) {
        ...

        // START AsyncTask
        asyncTask = new ExampleAsyncTask();
        asyncTask.setListener(new ExampleAsyncTask.ExampleAsyncTaskListener() {
            @Override
            public void onExampleAsyncTaskFinished(Integer value) {
                // update UI in Activity here
            }
        });
        asyncTask.execute();
    }

    @Override
    protected void onDestroy() {
        asyncTask.setListener(null); // PREVENT LEAK AFTER ACTIVITY DESTROYED
        super.onDestroy();
    }

    static class ExampleAsyncTask extends AsyncTask<Void, Void, Integer> {
        private ExampleAsyncTaskListener listener;

        @Override
        protected Integer doInBackground(Void... voids) {
            ...
            return null;
        }

        @Override
        protected void onPostExecute(Integer value) {
            super.onPostExecute(value);
            if (listener != null) {
                listener.onExampleAsyncTaskFinished(value);
            }
        }

        public void setListener(ExampleAsyncTaskListener listener) {
            this.listener = listener;
        }

        public interface ExampleAsyncTaskListener {
            void onExampleAsyncTaskFinished(Integer value);
        }
    }
}


5
@Suragch आपके लिंक में कहा गया है कि, जबकि onDestroy को कॉल करने की गारंटी नहीं है, एकमात्र स्थिति जहां यह नहीं है जब सिस्टम प्रक्रिया को मारता है, तो सभी संसाधनों को वैसे भी मुक्त किया जाता है। इसलिए, यहां सेव न करें, लेकिन आप यहां रिसोर्स रिलीज कर सकते हैं।
एंजेलो फुच्स

2
गैर-स्थैतिक AsyncTask उपयोग के मामले में, हम इस तरह के समान NULL को AsyncTask उदाहरण चर सेट क्यों नहीं कर सकते। हालांकि यह बताइए कि GC को फ्री एक्टिविटी बताई जा रही है, हालांकि AsyncTask चल रही है?
हनीफ

@ हनीफ सेटिंग AsyncTask उदाहरण चर NUL को मदद नहीं करेगा, क्योंकि कार्य अभी भी श्रोता के माध्यम से रेफरी है।
सुशांत
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.