चेतावनी: स्थिर क्षेत्रों में Android संदर्भ कक्षाएं न लगाएं; यह एक मेमोरी लीक है (और इंस्टेंट रन भी तोड़ता है)


84

Android स्टूडियो:

एंड्रॉइड संदर्भ कक्षाओं को स्थिर क्षेत्रों में न रखें; यह एक मेमोरी लीक है (और इंस्टेंट रन भी तोड़ता है)

तो 2 प्रश्न:

# 1 आप startServiceसंदर्भ के लिए एक स्थिर चर के बिना एक स्थिर विधि से कैसे कॉल करते हैं ?
# 2 आप एक स्थैतिक विधि (उसी) से एक लोकलब्रॉडकास्ट कैसे भेजते हैं?

उदाहरण:

public static void log(int iLogLevel, String sRequest, String sData) {
    if(iLogLevel > 0) {

        Intent intent = new Intent(mContext, LogService.class);
        intent.putExtra("UPDATE_MAIN_ACTIVITY_VIEW", "UPDATE_MAIN_ACTIVITY_VIEW");
        mContext.startService(intent);
    }
}

या

        Intent intent = new Intent(MAIN_ACTIVITY_RECEIVER_INTENT);
        intent.putExtra(MAIN_ACTIVITY_REQUEST_FOR_UPDATE, sRequest));
        intent.putExtra(MAIN_ACTIVITY_DATA_FOR_VIEW, sData);
        intent.putExtra(MAIN_ACTIVITY_LOG_LEVEL, iLogLevel);
        LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);

बिना उपयोग के ऐसा करने का सही तरीका क्या होगा mContext?

नोट: मुझे लगता है कि मेरा मुख्य प्रश्न उस वर्ग के संदर्भ में कैसे हो सकता है जिसमें से कॉलिंग विधि रहती है।


क्या आप विधि में पैरामीटर के रूप में संदर्भ को पारित नहीं कर सकते?
जुआन क्रूज़ सोलर

मैं इस रूटीन को उन जगहों पर कहूंगा जो संदर्भ के साथ भी नहीं होंगे।
जॉन स्मिथ

# 1 इसे # 2 पैरामीटर के रूप में पास करता है।
njzk2

फिर आपको कॉल करने की विधि के संदर्भ को भी पास करना होगा। समस्या यह है कि स्थैतिक क्षेत्रों में कचरा एकत्र नहीं किया जाता है, इसलिए आप इसके सभी दृश्यों के साथ एक गतिविधि को लीक कर सकते हैं
जुआन क्रूज़ सोलर

1
@JohnSmith इसे आरंभ करने वाली गतिविधि (निर्माण मापदंडों या विधि मापदंडों के माध्यम से) से उस बिंदु तक ले जाएं जहां तक ​​आपको इसकी आवश्यकता है।
AndroidMechanic - वायरल पटेल

जवाबों:


56

बस इसे अपनी विधि के पैरामीटर के रूप में पास करें। एक Contextशुरू करने के उद्देश्य के लिए पूरी तरह से एक स्थिर उदाहरण बनाने में कोई मतलब नहीं है Intent

इस प्रकार आपकी विधि दिखनी चाहिए:

public static void log(int iLogLevel, String sRequest, String sData, Context ctx) {
    if(iLogLevel > 0) {

        Intent intent = new Intent(ctx, LogService.class);
        intent1.putExtra("UPDATE_MAIN_ACTIVITY_VIEW", "UPDATE_MAIN_ACTIVITY_VIEW");
        ctx.startService(intent);
    }
}

प्रश्न पर टिप्पणियों से अपडेट करें: आरंभिक गतिविधि (निर्माण मापदंडों या विधि मापदंडों के माध्यम से) के संदर्भ में उस बिंदु तक पहुंचें, जहां तक ​​इसकी आवश्यकता है।


क्या आप कंस्ट्रक्टर का उदाहरण दे सकते हैं?
जॉन स्मिथ

यदि आपका वर्ग के नाम है MyClassबस इसे करने की विधि की तरह एक सार्वजनिक निर्माता जोड़ने public MyClass(Context ctx) { // put this ctx somewhere to use later }(यह आपके निर्माता है) अब के नया उदाहरण बनाने के MyClassइस निर्माता जैसे का उपयोग करMyClass mc = new MyClass(ctx);
AndroidMechanic - वायरल पटेल

मुझे नहीं लगता कि यह पास-ऑन-डिमांड के रूप में सरल है। हालांकि, स्पष्ट लाभ हैं जैसे बासी संदर्भ के बारे में चिंता न करना या यहाँ की तरह, एक स्थिर। मान लीजिए कि आपको एक प्रतिक्रिया कॉलबैक में संदर्भ की आवश्यकता है [क्या आप प्रीफ़्स के लिए लिखना चाहते हैं] जिसे अतुल्यकालिक रूप से आमंत्रित किया जाएगा। इसलिए कई बार, आपको इसे सदस्य क्षेत्र में रखने के लिए मजबूर किया जाता है। और अब आपको यह सोचना है कि इसे स्थिर कैसे नहीं बनाया जाए। stackoverflow.com/a/40235834/2695276 काम करने लगता है।
रजत शर्मा

1
क्या स्थिर क्षेत्र के रूप में ApplicationContext का उपयोग करना ठीक है? गतिविधियों के विपरीत, एप्लिकेशन ऑब्जेक्ट नष्ट नहीं होता है, है ना?
NeoWang

50

यदि आप किसी भी सदस्य क्षेत्र में इसे संग्रहीत करने का निर्णय लेते हैं, तो बस यह सुनिश्चित करें कि आप संदर्भ को भूल जाते हैं। आप किसी भी संदर्भ में संदर्भ /getApplicationContext () या कॉल getApplicationContext () को पास करें।

इडियट प्रूफ उदाहरण (भले ही कोई व्यक्ति किसी गतिविधि में पास हो जाए, यह ऐप के संदर्भ को ले लेगा और सिंगलटन को इंस्टेंट करने के लिए उपयोग करेगा):

public static synchronized RestClient getInstance(Context context) {
    if (mInstance == null) {
        mInstance = new RestClient(context.getApplicationContext());
    }
    return mInstance;
}

डॉक्स के अनुसार getApplicationContext (): "वर्तमान प्रक्रिया के एकल, वैश्विक अनुप्रयोग ऑब्जेक्ट का संदर्भ लौटाएं।"

इसका अर्थ है कि "getApplicationContext ()" द्वारा लौटाया गया संदर्भ पूरी प्रक्रिया से गुजरेगा और इस प्रकार इससे कोई फर्क नहीं पड़ता कि आप कहीं भी इसका स्थैतिक संदर्भ संग्रहीत करते हैं क्योंकि यह आपके ऐप के रनटाइम के दौरान हमेशा रहेगा (और किसी भी ऑब्जेक्ट को आउटिवेट करता है। / सिंगलटैलन इसके द्वारा त्वरित)।

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

एक गतिविधि के संदर्भ में उसके संदर्भ में ही गतिविधि के रूप में एक ही जीवन चक्र रहना चाहिए, अन्यथा यह एक स्मृति रिसाव के कारण संदर्भ बंधक को धारण करेगा (जो लिंट चेतावनी के पीछे का कारण है)।

संपादित करें: ऊपर दिए गए डॉक्स से उदाहरण को कोसने वाले व्यक्ति के लिए, कोड में एक टिप्पणी अनुभाग भी है जिसके बारे में मैंने अभी लिखा है:

    // getApplicationContext() is key, it keeps you from leaking the
    // Activity or BroadcastReceiver if someone passes one in.

8
ऊपर दिए गए उदाहरण को कोसने वाले आदमी को कोसने वाले व्यक्ति को: इस धागे का बिंदु Google को सिंगलटन बनाने के अपने अनुशंसित पैटर्न के साथ संघर्ष में चेतावनी है।
राफेल C

7
पढ़ें: "एंड्रॉइड संदर्भ कक्षाओं को स्थिर क्षेत्रों में न रखें; यह एक मेमोरी लीक है (और इंस्टेंट रन को भी तोड़ता है)" क्या आप जानते हैं कि कौन से प्रोग्राम हैं? गतिविधि उनमें से एक है, और आपको गतिविधि को एक स्थिर क्षेत्र के रूप में संग्रहीत नहीं करना चाहिए, जैसा कि आपने खुद को बताया (या यह मेमोरी को लीक करेगा)। हालांकि, आप एक स्थिर क्षेत्र के रूप में संदर्भ (जब तक यह एप्लिकेशन संदर्भ है) को स्टोर कर सकते हैं, क्योंकि यह सब कुछ रेखांकित करता है। (और इस प्रकार चेतावनी को अनदेखा करें)। मुझे यकीन है कि हम इस सरल तथ्य पर सहमत हो सकते हैं, है ना?
मार्कस ग्रांउ

iOS vet के रूप में, Android के मेरे पहले सप्ताह में ... इस तरह के स्पष्टीकरण से मुझे इस संदर्भ को समझने में मदद मिलती है .. इसलिए, कि लिंट चेतावनी (ओह, मैं किसी भी चेतावनी को कैसे नापसंद करता हूं) चारों ओर लटकाएंगे, लेकिन आपका जवाब वास्तविक समस्या को हल करता है ।
एरिक

@Marcus यदि आपके बच्चे के वर्ग को इस बात की जानकारी नहीं है कि यह किस संदर्भ में है, तो उसे स्थैतिक सदस्य के रूप में संग्रहीत करना केवल एक बुरा अभ्यास है। इसके अलावा, एप्लिकेशन संदर्भ आपके ऐप के एप्लिकेशन ऑब्जेक्ट के भाग के रूप में रहता है, एप्लिकेशन ऑब्जेक्ट हमेशा के लिए मेमोरी में नहीं रहेगा, यह मारा जाएगा। आम धारणा के विपरीत, ऐप को स्क्रैच से रीस्टार्ट नहीं किया जाएगा। एंड्रॉइड एक नया एप्लिकेशन ऑब्जेक्ट बनाएगा और उस गतिविधि को शुरू करेगा जहां उपयोगकर्ता यह भ्रम देने से पहले था कि एप्लिकेशन को पहले स्थान पर कभी नहीं मारा गया था।
राफेल C

@RaphaelC क्या आपके पास ऐसे दस्तावेज हैं? ऐसा लगता है कि यह पूरी तरह से गलत है क्योंकि एंड्रॉइड प्रत्येक प्रक्रिया के प्रति रन केवल एक एप्लिकेशन संदर्भ का आश्वासन देता है।
हेडनकै

6

यह सिर्फ एक चेतावनी है। चिंता मत करो। यदि आप एक एप्लिकेशन संदर्भ का उपयोग करना चाहते हैं, तो आप इसे "सिंगलटन" श्रेणी में सहेज सकते हैं, जिसका उपयोग आपकी परियोजना में सभी सिंगलटन वर्ग को बचाने के लिए किया जाता है।


2

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

साथ ही इंस्टेंट रन यहां ठीक काम ...


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

android: launchMode = "singleTask" पर्याप्त होना चाहिए, इसलिए मैं उस पर स्विच कर रहा हूं, मैंने singleTop का उपयोग किया है, लेकिन इसका कोई पर्याप्त पता नहीं है क्योंकि मैं हमेशा अपनी मुख्य गतिविधियों के केवल एकल उदाहरण चाहता हूं, अर्थात मेरे ऐप कैसे डिज़ाइन किए गए हैं।
रेनेटिक

2
"सिंगलटैस्क" केवल प्रति कार्य एक उदाहरण की गारंटी देता है। यदि आपके ऐप में कई एंट्री पॉइंट हैं जैसे डीप लिंकिंग, या इसे एक अधिसूचना से लॉन्च करना, तो आप कई कार्यों के साथ समाप्त हो सकते हैं।
ब्लेडकॉडर

1

आमतौर पर, संदर्भ क्षेत्रों को स्थिर के रूप में परिभाषित करने से बचें। चेतावनी ही बताती है कि क्यों: यह एक स्मृति रिसाव है। हालांकि तत्काल रन को तोड़ना ग्रह पर सबसे बड़ी समस्या नहीं हो सकती है

अब, दो परिदृश्य हैं जहाँ आपको यह चेतावनी मिलेगी। एक उदाहरण के लिए (सबसे स्पष्ट एक):

public static Context ctx;

और फिर थोड़ा और मुश्किल है, जहां संदर्भ एक वर्ग में लिपटे हुए है:

public class Example{
    public Context ctx;
    //Constructor omitted for brievety 
}

और उस वर्ग को कहीं स्थिर के रूप में परिभाषित किया गया है:

public static Example example;

और आपको चेतावनी मिल जाएगी।

इसका समाधान स्वयं काफी सरल है: संदर्भ क्षेत्रों को स्थिर उदाहरणों में न रखें , चाहे वह रैपिंग क्लास हो या सीधे इसे स्थिर घोषित करना।

और चेतावनी का समाधान सरल है: क्षेत्र को सांख्यिकीय रूप से न रखें। आपके मामले में, विधि को उदाहरण के रूप में पास करें। उन कक्षाओं के लिए जहां एकाधिक संदर्भ कॉल किए जाते हैं, कक्षा में संदर्भ (या उस मामले के लिए एक गतिविधि) को पास करने के लिए एक कंस्ट्रक्टर का उपयोग करें।

ध्यान दें कि यह एक चेतावनी है, त्रुटि नहीं। क्या आपको किसी भी कारण से स्थैतिक संदर्भ की आवश्यकता है, आप इसे कर सकते हैं। यद्यपि आप ऐसा करने पर स्मृति रिसाव बनाते हैं।


मेमोरी लीकेज बनाए बिना हम इसे कैसे कर सकते हैं?
isJulian00

1
आप नहीं कर सकते। यदि आपको बिल्कुल संदर्भों को पास करने की आवश्यकता है, तो आप एक घटना बस में देख सकते हैं
ज़ो

ठीक इस समस्या को अगर आप को खुश कर सकता है एक बार देख ले यह शायद यह ऐसा करने का एक और तरीका है पर, btw विधि क्योंकि मैं इसे सी ++ से कोड बोल रहा हूँ स्थिर हो गया है मैं हो रही थी था stackoverflow.com/questions/54683863/...
isJulian00

0

यदि आप सुनिश्चित करते हैं कि यह एक अनुप्रयोग संदर्भ है। यह सबसे महत्वपूर्ण बात है। इसे जोड़ो

@SuppressLint("StaticFieldLeak")

1
मैं वैसे भी ऐसा करने की सलाह नहीं दूंगा। यदि आपको संदर्भ की आवश्यकता है, तो आप आवश्यकता का उपयोग कर सकते हैं कॉन्टेक्स्ट () विधि, यदि आप AndroidX लिबास का उपयोग करते हैं। या फिर आप सीधे उस तरीके से संदर्भ को पास कर सकते हैं जिसकी आवश्यकता है। या आप बस ऐप का क्लास रेफरेंस भी प्राप्त कर सकते हैं, लेकिन मैं सुझाव दूंगा कि आप ऐसे सप्रेसलाइन सुझाव का इस्तेमाल न करें।
ऑलेक्ज़ेंडर नोस

0

WeakReferenceसिंगलटन क्लासेस में कॉन्सेप्ट को स्टोर करने के लिए उपयोग करें और चेतावनी समाप्त हो जाएगी

private WeakReference<Context> context;

//Private contructor
private WidgetManager(Context context) {
    this.context = new WeakReference<>(context);
}

//Singleton
public static WidgetManager getInstance(Context context) {
    if (null == widgetManager) {
        widgetManager = new WidgetManager(context);
    }
    return widgetManager;
}

अब आप Context को एक्सेस कर सकते हैं

  if (context.get() instanceof MainActivity) {
            ((MainActivity) context.get()).startActivityForResult(pickIntent, CODE_REQUEST_PICK_APPWIDGET);
        }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.