कैसे करें

मेमोरी की खपत कम करने को प्राथमिकता देना: Android 17 के लिए ज़रूरी चरण

पढ़ने में 10 मिनट लगेंगे
3 लेखक
Alice Yuan, Ajesh Pai, Fung Lam

ऐप्लिकेशन की परफ़ॉर्मेंस को अक्सर बेहतर यूज़र इंटरफ़ेस (यूआई) और ऐप्लिकेशन के तेज़ी से शुरू होने से जोड़कर देखा जाता है. हालांकि, मेमोरी एक ऐसा आधार है जिस पर ये मेट्रिक तैयार की जाती हैं. यह कोई छिपी हुई बात नहीं है कि अब डिवाइस की मेमोरी पहले से ज़्यादा ज़रूरी हो गई है. हमने Android 17 के साथ, Android की मेमोरी को ऑप्टिमाइज़ करने में काफ़ी तरक्की की है. साथ ही, हम आपको टूल और एपीआई की मदद भी दे रहे हैं, ताकि इस साल के आखिर में मेमोरी से जुड़ी ज़्यादा ज़रूरी शर्तों को पूरा किया जा सके.

डिवाइस के ठीक से काम करने के लिए, Android 17 से सिस्टम, डिवाइस की कुल रैम के आधार पर ऐप्लिकेशन की मेमोरी की सीमाएं लागू करना शुरू कर देगा. अगर कोई ऐप्लिकेशन इन सीमाओं से ज़्यादा मेमोरी का इस्तेमाल करता है, तो Android उस प्रोसेस को बंद कर देगा. हालांकि, इससे जुड़ी स्टैक ट्रेस की जानकारी नहीं मिलेगी.

इनके अलावा, मेमोरी का सही तरीके से इस्तेमाल न करने पर, उपयोगकर्ता अनुभव खराब हो जाता है. जब ऐप्लिकेशन, हीप मेमोरी की सीमाओं के करीब पहुंच जाता है, तो यह बार-बार गार्बेज कलेक्शन को ट्रिगर करता है. इससे यूज़र इंटरफ़ेस (यूआई) में रुकावटें आती हैं. इसके अलावा, जब किसी डिवाइस में उपलब्ध मेमोरी खत्म हो जाती है, तो सिस्टम पेजों को वापस पाने के लिए तेज़ी से काम करता है. इससे सीपीयू पर दबाव पड़ता है, यूज़र इंटरफ़ेस (यूआई) में इंतज़ार का समय बढ़ जाता है, और तेज़ी से बैटरी खर्च होती है. अगर मेमोरी की कमी बहुत ज़्यादा है, तो इससे Low Memory Killer (एलएमके) इवेंट हो सकते हैं. ये इवेंट, बैकग्राउंड प्रोसेस को अचानक बंद कर देते हैं. साथ ही, ऐप्लिकेशन को कोल्ड स्टार्ट होने में ज़्यादा समय लगता है और उपयोगकर्ता की स्थिति का डेटा मिट जाता है.

ज़्यादा परफ़ॉर्म करने वाले ऐप्लिकेशन बनाने और ऐप्लिकेशन के अचानक बंद होने की समस्या से बचने के लिए, हम आपको मेमोरी ऑप्टिमाइज़ेशन की इन रणनीतियों को अपनाने का सुझाव देते हैं:

  1. R8 की मदद से बाइटकोड ऑप्टिमाइज़ेशन को ज़्यादा से ज़्यादा करना
  2. इमेज लोड होने की प्रोसेस को ऑप्टिमाइज़ करना
  3. Android Studio की मदद से, मेमोरी लीक का पता लगाना और उन्हें ठीक करना
  4. ऐप्लिकेशन के बंद होने पर मेमोरी को ट्रिम करें
  5. ProfilingManager की मदद से, मेमोरी की बेहतर निगरानी करना

इस ब्लॉग पोस्ट का छोटा वर्शन, वीडियो फ़ॉर्मैट में भी उपलब्ध है. इसे देखें!

Android 17 पर ऐप्लिकेशन के लिए मेमोरी की सीमाएं समझना

Android 17 में ऐप्लिकेशन के लिए मेमोरी की सीमाएं तय की जा रही हैं. इससे "बुरे मकसद से काम करने वाले लोग या ग्रुप" को मल्टीटास्किंग के अनुभव को खराब करने और उपयोगकर्ता के पूरे डिवाइस की स्थिरता को नुकसान पहुंचाने से रोका जा सकेगा.

आर्किटेक्चर में इस बदलाव की वजहें यहां दी गई हैं:

  • कैस्केडिंग किल को रोकना: जब कोई ऐप्लिकेशन, खास अधिकार वाली स्थिति में होता है (जैसे कि वह फ़ोरग्राउंड सेवा चला रहा है) और इस दौरान उसकी मेमोरी का इस्तेमाल बढ़ जाता है या मेमोरी लीक होती है, तो उसे सिस्टम के Low Memory Killer (एलएमके) से सुरक्षित रखा जाता है. इस एक ऐप्लिकेशन के लगातार बढ़ने और रैम का इस्तेमाल करने की वजह से, एलएमके को कई छोटे-छोटे, ठीक से काम करने वाले कैश किए गए ऐप्लिकेशन और बैकग्राउंड में चल रहे टास्क बंद करने पड़ते हैं. ऐसा इसलिए, ताकि मेमोरी का ज़्यादा इस्तेमाल करने वाले ऐप्लिकेशन के लिए जगह बनाई जा सके.
     
  • मल्टीटास्किंग और उपयोगकर्ता की स्थिति को बनाए रखना: जब सिस्टम को किसी एक प्रोसेस के लिए कैश मेमोरी में सेव किए गए ऐप्लिकेशन हटाने पड़ते हैं, तो मल्टीटास्किंग का अनुभव खराब हो जाता है. पहले से कैश मेमोरी में सेव किए गए ऐप्लिकेशन पर लौटने वाले उपयोगकर्ताओं को, तुरंत चालू होने के बजाय धीरे-धीरे चालू होने की समस्या का सामना करना पड़ता है. इस वजह से, सीपीयू पर ज़्यादा असर पड़ता है और बैटरी तेज़ी से खत्म होती है. इससे हाल ही में इस्तेमाल किए गए ऐप्लिकेशन में उपयोगकर्ता का कॉन्टेक्स्ट भी मिट सकता है. जैसे, स्क्रोल करने की जगह, नेविगेशन स्टैक, और गेम में की गई प्रोग्रेस.

यह पता लगाने के लिए कि फ़ील्ड में इन पाबंदियों की वजह से, आपके ऐप्लिकेशन सेशन पर असर पड़ा है या नहीं, ApplicationExitInfo में getDescription() को कॉल करें. अगर सिस्टम ने सीमा लागू की है, तो बाहर निकलने की वजह के तौर पर REASON_OTHER रिपोर्ट किया जाता है. साथ ही, ब्यौरे वाली स्ट्रिंग में "MemoryLimiter:AnonSwap" शामिल होता है. मेमोरी की सीमा पूरी होने पर, हीप डंप अपने-आप कैप्चर करने के लिए, TRIGGER_TYPE_ANOMALY का इस्तेमाल करके, ट्रिगर पर आधारित प्रोफ़ाइलिंग का फ़ायदा भी लिया जा सकता है. इसके अलावा, Android, Google Play Console में डेवलपर को ज़्यादा इन-फ़ील्ड मेमोरी मेट्रिक दिखाने के लिए लगातार काम कर रहा है.

हमने मेमोरी की सीमाओं से जुड़े दस्तावेज़ को भी बड़ा किया है, ताकि इसमें स्थानीय डीबग करने के कमांड शामिल किए जा सकें. इससे आपको अपने लोकल एनवायरमेंट में मेमोरी की सीमाओं को सिम्युलेट करने और मेमोरी की किसी भी सीमा को लागू करने पर, अपने ऐप्लिकेशन के व्यवहार की पुष्टि करने में मदद मिलेगी. 

R8 की मदद से बाइटकोड ऑप्टिमाइज़ेशन को ज़्यादा से ज़्यादा करना

अपने ऐप्लिकेशन के मेमोरी फ़ुटप्रिंट को कम करने का सबसे असरदार तरीका, R8 ऑप्टिमाइज़र को चालू करना है. R8, क्लास, मेथड, और फ़ील्ड के नामों को छोटा करके और इस्तेमाल न होने वाले कोड और संसाधनों को हटाकर, आपके ऐप्लिकेशन की मेमोरी फ़ुटप्रिंट को काफ़ी हद तक कम कर देता है. ऐसा इसलिए होता है, क्योंकि यह एक्ज़ीक्यूशन के दौरान ज़रूरी कोड की मात्रा को कम कर देता है. 

R8, रेज़िडेंट कोड को कम करता है. इससे मेमोरी फ़ुटप्रिंट कम हो जाता है और एलएमके के बंद होने का जोखिम कम हो जाता है. इससे स्लो कोल्ड स्टार्ट के मुकाबले, वॉर्म स्टार्ट ज़्यादा बार होता है. इसके अलावा, स्ट्रीमलाइन किए गए बाइटकोड से मुख्य थ्रेड के सीपीयू पर पड़ने वाला असर कम हो जाता है. इससे सीधे तौर पर ANR रेट कम हो जाते हैं, ताकि उपयोगकर्ता को बेहतर अनुभव मिल सके. उदाहरण के लिए, डिजिटल बैंक Monzo ने R8 को पूरी तरह से ऑप्टिमाइज़ किया. इसके बाद, उसे एएनआर रेट में 35% की कमी, कोल्ड स्टार्ट रेट में 30% की बढ़ोतरी, और ऐप्लिकेशन के कुल साइज़ में 9% की कमी देखने को मिली.

pic1-IO26_113_TSV-monzo-casestudy.jpg
डिजिटल बैंक Monzo ने R8 के ऑप्टिमाइज़ेशन की पूरी सुविधा चालू की. इससे परफ़ॉर्मेंस मेट्रिक में 35% तक की बढ़ोतरी हुई.

अपनी build.gradle फ़ाइल में R8 को सही तरीके से कॉन्फ़िगर करने के लिए:

  • isShrinkResources = true और isMinifyEnabled = true सेट करें.
  • लेगसी proguard-android.txt के बजाय proguard-android-optimize.txt का इस्तेमाल करें. इससे ऑप्टिमाइज़ेशन नहीं हो पाता है. साथ ही, Android Gradle प्लगिन 9 में अब यह काम नहीं करता.
  • अपने gradle.properties से android.enableR8.fullMode = false को हटाएं.

अगर आपके कोड बेस में रिफ़्लेक्शन का इस्तेमाल किया जा रहा है, तो Keep rules जोड़ें. इससे R8 को कोड के उन हिस्सों को ऑप्टिमाइज़ करने से रोका जा सकेगा. ज़्यादा से ज़्यादा ऑप्टिमाइज़ेशन पाने के लिए, पक्का करें कि कीप के नियमों का दायरा सीमित हो. 

ज़्यादा से ज़्यादा ऑप्टिमाइज़ेशन पाने के लिए, पक्का करें कि आपने कीप रूल फ़ाइल में इन सबसे सही तरीकों को अपनाया हो.

  • -dontoptimize-dontshrink, और -dontobfuscate जैसे ग्लोबल विकल्पों को हटाएं. इनकी वजह से, R8 पूरे कोडबेस को ऑप्टिमाइज़ नहीं कर पाता
  • ऐसे कीप नियमों को हटाएं जो Android कॉम्पोनेंट, जैसे कि ऐक्टिविटी, सेवाएं, व्यू या ब्रॉडकास्ट रिसीवर को ऑप्टिमाइज़ होने से रोकते हैं.
  • सिर्फ़ कुछ क्लास या तरीकों को टारगेट करने के लिए, पैकेज के हिसाब से लागू होने वाले कीप नियमों में बदलाव करें. 

सबसे सही तरीके जानने के लिए, डेटा सुरक्षित रखने के नियमों से जुड़ा दस्तावेज़ देखें.

Library Developer R8 के सबसे सही तरीके

अगर आप लाइब्रेरी डेवलपर हैं, तो अपने उपभोक्ताओं के लिए ज़रूरी नियमों को consumer-rules file में रखें. साथ ही, लाइब्रेरी के इंटरनल सुरक्षा नियमों को proguard-rules.pro फ़ाइल में रखें. लाइब्रेरी को ऑप्टिमाइज़ करने के बारे में ज़्यादा जानने के लिए, लाइब्रेरी को तैयार करने वालों के लिए ऑप्टिमाइज़ेशन लेख पढ़ें.

R8 कॉन्फ़िगरेशन ऐनलिसिस

अपने R8 ऑप्टिमाइज़ेशन की जांच करने के लिए, कॉन्फ़िगरेशन विश्लेषक का इस्तेमाल करें. कॉन्फ़िगरेशन विश्लेषक, ऑप्टिमाइज़ेशन की मौजूदा स्थिति को दिखाता है. इसमें  डेटा को छिपाने, ऑप्टिमाइज़ेशन, और फ़ाइल के साइज़ को कम करने से जुड़े स्कोर शामिल होते हैं. कॉन्फ़िगरेशन विश्लेषक की मदद से, यह भी समझा जा सकता है कि हर कीप नियम की वजह से कितनी क्लास, तरीके या फ़ील्ड को ऑप्टिमाइज़ नहीं किया जा सकता. ज़्यादा ऑप्टिमाइज़ेशन के लिए, पैकेज के हिसाब से लागू होने वाले इन नियमों को बेहतर बनाएं. 

कॉन्फ़िगरेशन विश्लेषक का इस्तेमाल करके, ऐसे कीप रूल भी पहचाने जा सकते हैं जो दूसरे कीप रूल को शामिल कर रहे हैं, ज़रूरत से ज़्यादा कीप रूल, और इस्तेमाल नहीं किए जा रहे कीप रूल.

pic2-r8-config-analyzer.png
कॉन्फ़िगरेशन विश्लेषक, अस्पष्टता, ऑप्टिमाइज़ेशन, और कम करने के स्कोर के साथ ऑप्टिमाइज़ेशन की मौजूदा स्थिति दिखाता है.

R8 एजेंट स्किल 

Android Studio एजेंट या अन्य एआई टूल के साथ, R8 एजेंट स्किल का इस्तेमाल करके, गलत कॉन्फ़िगरेशन को ठीक किया जा सकता है. साथ ही, अपने नियमों को बेहतर बनाया जा सकता है. इससे ऐप्लिकेशन की परफ़ॉर्मेंस बेहतर होती है. (एआई की मदद से मिलने वाली अहम जानकारी के लिए, तकनीकी पुष्टि ज़रूरी होगी)

इमेज लोड होने की प्रोसेस को ऑप्टिमाइज़ करना

आम तौर पर, बिटमैप आपके ऐप्लिकेशन की मेमोरी में मौजूद सबसे बड़े ऑब्जेक्ट होते हैं. ये इमेज लोड होने की प्रोसेस के आखिरी चरण को दिखाते हैं. इसमें, JPEGs या PNGs जैसी कंप्रेस की गई फ़ाइलों को डिसप्ले के लिए, रॉ पिक्सल डेटा में डिकोड किया जाता है. इसका मतलब है कि कंप्रेस की गई 100 केबी की छोटी इमेज, कई मेगाबाइट की रैम इस्तेमाल कर सकती है. ऐसा इसलिए, क्योंकि मेमोरी का इस्तेमाल, इमेज के पिक्सल डाइमेंशन और कलर डेप्थ से तय होता है. बिटमैप ऑपरेशन, फ़्रेम बनाने के लिए अक्सर ज़रूरी होते हैं. इसलिए, ऑप्टिमाइज़ न की गई इमेज से मेमोरी का इस्तेमाल बहुत ज़्यादा होता है और यूज़र इंटरफ़ेस (यूआई) अटक-अटककर चलता है.

Google का सुझाव है कि Kotlin-first प्रोजेक्ट के लिए, इमेज लोड करने वाली लाइब्रेरी Coil का इस्तेमाल करें. खास तौर पर, Jetpack Compose का इस्तेमाल करके डेवलपमेंट करते समय. साथ ही, Java पर आधारित ऐप्लिकेशन के लिए, Glide का इस्तेमाल करें.

इन पांच सबसे सही तरीकों को अपनाएं

  1. इमेज को डाउनसैंपल करें: अगर बिटमैप को मैन्युअल तरीके से लोड किया जा रहा है, तो छोटी थंबनेल व्यू में बड़ी इमेज लोड करने से बचें. छोटा वर्शन लोड करने के लिए, inSampleSize का इस्तेमाल करें. Glide और Coil, डिफ़ॉल्ट रूप से इमेज को डाउनसैंपल करते हैं. DownsampleStrategy और ImageLoader का इस्तेमाल करके, डाउनसैंपल करने की इस रणनीति को कॉन्फ़िगर किया जा सकता है.
  2. काटना-छांटना: लेटरबॉक्सिंग के लिए, इमेज फ़ाइल में सीधे तौर पर पैडिंग एम्बेड करने से बचें. उदाहरण के लिए, इमेज के डाइमेंशन को बढ़ाने के लिए पारदर्शी बॉर्डर बनाना. इन बॉर्डर को शामिल करने के बजाय, InsetDrawable का इस्तेमाल करें. इसके अलावा, बिटमैप वाली View या Composable में सीधे तौर पर पैडिंग लागू करें.
  3. कॉन्फ़िगरेशन: सही पिक्सल फ़ॉर्मैट चुनकर, मेमोरी और क्वालिटी को बैलेंस करें. अगर पारदर्शिता की ज़रूरत नहीं है, तो RGB_565 का इस्तेमाल करें. यह डिफ़ॉल्ट ARGB_8888 फ़ॉर्मैट की तुलना में आधी मेमोरी का इस्तेमाल करता है. Glide में, DecodeFormat का इस्तेमाल करके इसे कॉन्फ़िगर किया जा सकता है. वहीं, Coil में bitmapConfig प्रॉपर्टी का इस्तेमाल किया जा सकता है.
  4. वेक्टर ड्रॉएबल को प्राथमिकता दें: बुनियादी ज्यामितीय ऐसेट के लिए, रास्टर इमेज को डिकोड करने के हल्के विकल्प के तौर पर ShapeDrawable का इस्तेमाल करें. एक्सएमएल के ज़रिए इन ऐसेट को एक बार तय करने पर, यह पक्का किया जा सकता है कि वे सभी डिसप्ले डेंसिटी में आसानी से स्केल हो जाएं. साथ ही, इससे रिसॉर्स की वजह से मेमोरी का इस्तेमाल भी कम हो जाता है.
  5. फिर से इस्तेमाल करना: अगर आपका ऐप्लिकेशन, बिटमैप को मैन्युअल तरीके से मैनेज करता है, तो मेमोरी के इस्तेमाल को कम करने के लिए, जब किसी बिटमैप की ज़रूरत न हो, तो ऐप्लिकेशन को bitmap.recycle() को कॉल करना चाहिए और Bitmap रेफ़रंस को तुरंत हटा देना चाहिए. अगर Glide या Coil जैसी इमेज लोड करने वाली लाइब्रेरी का इस्तेमाल किया जाता है, तो बिटमैप को लाइब्रेरी के मैनेज किए गए पूल में वापस भेजें. आने वाले समय में मेमोरी की ज़रूरतों के लिए मौजूदा बफ़र उपलब्ध कराकर, पूल नए असाइनमेंट के ओवरहेड से बचता है.

ज़्यादा जानने के लिए, इमेज की परफ़ॉर्मेंस को ऑप्टिमाइज़ करने के बारे में हमारा दस्तावेज़ देखें.

Android Studio के टूल

Android Studio Narwhal 4 का इस्तेमाल करके, ज़रूरत से ज़्यादा बिटमैप भी हटाए जा सकते हैं. यहां पांच आसान चरणों में, उन्हें ढूंढने का तरीका बताया गया है:

  1. Android Studio में Profiler टैब खोलें
  2. Heap Dump (या "Analyze Memory Usage") पर क्लिक करें. इसके बाद, रिकॉर्डिंग शुरू करने के लिए रिकॉर्ड करें पर क्लिक करें. इससे आपके ऐप्लिकेशन की मौजूदा मेमोरी का स्नैपशॉट लिया जाएगा.
  3. विश्लेषण के नतीजों में, पीले रंग का चेतावनी वाला त्रिकोण ⚠️ देखें. Android Studio इसका इस्तेमाल, एक से ज़्यादा बार सेव किए जा रहे डुप्लीकेट बिटमैप को फ़्लैग करने के लिए करता है. इसके अलावा, प्रोफ़ाइलर हेडर पर जाएं. इसके बाद, "इसके हिसाब से फ़िल्टर करें:" चुनें और "डुप्लीकेट बिटमैप" सेटिंग चुनें.
  4. फ़्लैग की गई किसी भी एंट्री पर क्लिक करके, बिटमैप की झलक वाला पैनल खोलें. इससे आपको यह पता चलेगा कि बार-बार उल्लंघन करने वाली इमेज कौनसी है.
  5. उस विज़ुअल पुष्टि का इस्तेमाल करके, अपने कोड में मौजूद लॉजिक को ट्रैक करें. साथ ही, बेहतर कैश मेमोरी की रणनीति लागू करें.
pic3-IO26_113_TSV -dup-bitmaps-cropped.jpg
Android Studio Profiler का इस्तेमाल करते समय, हीप डंप में पीले रंग का चेतावनी वाला त्रिकोण ⚠️ देखें.

Android Studio की मदद से, मेमोरी लीक का पता लगाना और उन्हें ठीक करना

Android में मेमोरी लीक तब होती है, जब आपका कोड किसी ऑब्जेक्ट के रेफ़रंस को उसकी लाइफ़साइकल खत्म होने के बाद भी बनाए रखता है. इससे गार्बेज कलेक्टर (जीसी) को उस मेमोरी को वापस पाने से रोका जाता है. इससे परफ़ॉर्मेंस धीमी हो जाती है या OutOfMemoryError (OOM) हो जाती है.

Android Studio Panda 3 में, LeakCanary प्रोफ़ाइलर टास्क की सुविधा है. इससे डेवलपर, रीयल-टाइम में मेमोरी लीक का विश्लेषण कर सकते हैं. साथ ही, सीधे तौर पर आईडीई में मैप ट्रेस कर सकते हैं.

Android Studio में LeakCanary का प्रोफ़ाइलर टास्क, मेमोरी लीक के विश्लेषण को आपके डिवाइस से डेवलपमेंट मशीन पर ले जाता है. इससे, उपयोगकर्ता के डिवाइस पर मेमोरी लीक का विश्लेषण करने की तुलना में, लीक के विश्लेषण के दौरान परफ़ॉर्मेंस में काफ़ी सुधार होता है.

pic4-android-studio-leaks.png
 LeakCanary की मदद से मेमोरी लीक का विश्लेषण करने की सुविधा. इसमें डीबग करने के लिए, 'डेफ़िनिशन पर जाएं' सुविधा का इस्तेमाल किया जा सकता है

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

मेमोरी लीक के सामान्य उदाहरण 

मेमोरी लीक तब होती है, जब कोई ऑब्जेक्ट अपनी तय की गई लाइफ़टाइम से ज़्यादा समय तक मेमोरी में बना रहता है. आम तौर पर, ऐसा इन वजहों से होता है:

  • ऐसे फ़्रैगमेंट, ऐक्टिविटी या व्यू के रेफ़रंस बनाए रखना जिनका अब इस्तेमाल नहीं किया जा रहा है.
  • कॉन्टेक्स्ट रेफ़रंस को गलत तरीके से मैनेज करना.
  • ऑब्ज़र्वर, लिसनर, और रिसीवर को सही तरीके से अनरजिस्टर न करना.
  • ऐसे ऑब्जेक्ट के लिए स्टैटिक रेफ़रंस बनाना जो कम लाइफ़साइकल वाले कॉम्पोनेंट से जुड़े होते हैं.

यहां कुछ उदाहरण दिए गए हैं:

परिदृश्यCompose पर आधारित उदाहरण व्यू के आधार पर उदाहरण
कॉन्टेक्स्ट लीक होना

उदाहरण:
ViewModel को LocalContext.current पास करना

ठीक किया गया:
कॉन्टेक्स्ट पर निर्भर लॉजिक को यूज़र इंटरफ़ेस (यूआई) लेयर में रखें. यूज़र इंटरफ़ेस (यूआई) वाली लेयर के अलावा अन्य लेयर के लिए, डिपेंडेंसी इंजेक्शन का इस्तेमाल करने के लिए कोड को फिर से लिखें या Kotlin फ़्लो का इस्तेमाल करके यूज़र इंटरफ़ेस (यूआई) की स्थिति को मॉनिटर करें.

उदाहरण: 
कंपैनियन ऑब्जेक्ट या स्टैटिक वैरिएबल में Activity को सेव करना.

ठीक करें:
यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट के स्टैटिक रेफ़रंस को न रोकें. डिपेंडेंसी इंजेक्शन का इस्तेमाल करने के लिए, कोड को रीफ़ैक्टर करें या Kotlin फ़्लो का इस्तेमाल करके, यूज़र इंटरफ़ेस (यूआई) की स्थिति को मॉनिटर करें.

लीकिंग लिसनर

उदाहरण:
लिसनर को शुरू करने के लिए DisposableEffect का इस्तेमाल करना, लेकिन onDispose को खाली छोड़ना.

ठीक किया गया:
onDispose ब्लॉक में, अनरजिस्टर करने और क्लीनअप लॉजिक को लागू किया गया.

उदाहरण:
SensorManager के अपडेट के लिए रजिस्टर करना और उसे अनरजिस्टर करना भूल जाना.

ठीक करें:
मैन्युअल तरीके से कॉल करें unregisterListener() में onStop() या onDestroy() लाइफ़साइकल.

लीक हो रहे व्यू

उदाहरण:
रिलीज़ की रणनीति के बिना, AndroidView में लेगसी View का रेफ़रंस सेव करना.


ठीक करें:
लेगसी View को हटाने के लिए, AndroidView कंपोज़ेबल के release ब्लॉक का इस्तेमाल करें.

उदाहरण:
Fragment के डिस्ट्रॉय होने के बाद भी, व्यू बाइंडिंग ऑब्जेक्ट का रेफ़रंस बनाए रखना.

 

ठीक किया गया:
बाइंडिंग वैरिएबल को onDestroyView() लाइफ़साइकल के तरीके में null पर सेट किया गया.

ऐप्लिकेशन के बंद होने पर मेमोरी को ट्रिम करें

अगर ज़रूरी टास्क के लिए मेमोरी खाली करनी हो, तो Android आपके ऐप्लिकेशन से मेमोरी वापस ले सकता है या आपके ऐप्लिकेशन को पूरी तरह से बंद कर सकता है. इस बारे में, मेमोरी मैनेजमेंट की खास जानकारी में बताया गया है. Android आम तौर पर, आपके ऐप्लिकेशन से मेमोरी तब वापस लेता है, जब वह उपयोगकर्ता को नहीं दिखता. जैसे, मेमोरी में मौजूद आपके ऐप्लिकेशन के कुछ कोड और डेटा पेजों को खारिज करके या आपके हीप के लिए मेमोरी को कंप्रेस करके. जब उपयोगकर्ता आपके ऐप्लिकेशन को फिर से शुरू करता है और आपका ऐप्लिकेशन, वापस ली गई मेमोरी को ऐक्सेस करने की कोशिश करता है, तो ओएस उस मेमोरी को वापस स्वैप कर देगा. स्वैप करने की यह प्रोसेस धीमी हो सकती है. साथ ही, इससे आपके ऐप्लिकेशन में अनचाहे जंक या रुकावटें आ सकती हैं.

अगर ओएस को यह तय करने दिया जाता है कि आपके ऐप्लिकेशन से कितनी मेमोरी वापस लेनी है, तो हो सकता है कि ओएस ने ऐसी मेमोरी वापस ले ली हो जिसकी आपको ऐप्लिकेशन फिर से शुरू करने के तुरंत बाद ज़रूरत पड़ने वाली हो. इसके बजाय, आपका ऐप्लिकेशन मेमोरी के उन हिस्सों को हटा सकता है जिन्हें बाद में, ज़रूरत पड़ने पर और कम लागत में फिर से जनरेट किया जा सकता है. इसके लिए, ComponentCallbacks2 इंटरफ़ेस लागू किया जा सकता है. onTrimMemory को अपनी Activity, Fragment, Service या कस्टम Application क्लास में लागू किया जा सकता है. Application क्लास में इसका इस्तेमाल करने से, ग्लोबल कैश मेमोरी को मैनेज करने में काफ़ी मदद मिलती है.

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

मेमोरी लाइफ़साइकल मैनेजमेंट के मामले में, आपके लागू किए गए कोड में TRIM_MEMORY_UI_HIDDEN और TRIM_MEMORY_BACKGROUND पर खास तौर पर फ़ोकस किया जाना चाहिए. Android 14 के बाद से, सिस्टम ने अन्य लेगसी कॉन्स्टेंट के लिए सूचनाएं भेजना बंद कर दिया है. इन्हें Android 15 में आधिकारिक तौर पर बंद कर दिया गया था.

TRIM_MEMORY_UI_HIDDEN: इस सिग्नल से पता चलता है कि आपके ऐप्लिकेशन का यूज़र इंटरफ़ेस (यूआई), उपयोगकर्ता की स्क्रीन से हट गया है. इससे इंटरफ़ेस से जुड़ी मेमोरी को रिलीज़ करने का मौका मिलता है. जैसे, बिटमैप, वीडियो चलाने के बफ़र या जटिल ऐनिमेशन रिसॉर्स.

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

import android.content.ComponentCallbacks2
// Other import statements.

class MainActivity : AppCompatActivity(), ComponentCallbacks2 {

    /**
     * Release memory when the UI becomes hidden or when system resources become low.
     * @param level the memory-related event that is raised.
     */
    override fun onTrimMemory(level: Int) {

        if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
            // Release memory related to UI elements, such as bitmap caches.
        }

        if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
            // Release memory related to background processing, such as by
            // closing a database connection.
        }
    }
}

ध्यान दें: onTrimMemory इंटिग्रेशन, एसडीके टूल के साथ काम करने की सुविधा पर निर्भर कर सकता है. उदाहरण के लिए, कुछ गेम इस सुविधा को चालू करने के लिए, अपने गेम इंजन पर निर्भर होते हैं. कृपया गेम की मेमोरी को ऑप्टिमाइज़ करने से जुड़े दस्तावेज़ देखें.

ProfilingManager की मदद से, मेमोरी की बेहतर निगरानी करना

अगर आपको फ़ील्ड में मेमोरी से जुड़ी ऐसी समस्याओं का पता लगाना है और उनका निदान करना है जिन्हें स्थानीय तौर पर दोहराया नहीं जा सकता, तो आपको ProfilingManager API का इस्तेमाल करना चाहिए. Android 15 में लॉन्च किया गया यह बेहतर ऑब्ज़र्वेबिलिटी एपीआई, आपको प्रोग्राम के हिसाब से असली उपयोगकर्ता की Perfetto प्रोफ़ाइलें इकट्ठा करने की अनुमति देता है. 

जिन टीमों के पास परफ़ॉर्मेंस आर्टफ़ैक्ट को मैनेज और होस्ट करने के लिए कोई इन्फ़्रास्ट्रक्चर नहीं है उनके लिए Crashlytics, इस वर्कफ़्लो को आसान बनाने के लिए एक खास समाधान पर काम कर रहा है. वे डेवलपर को सुझाव/राय देने या शिकायत करने के लिए न्योता दे रहे हैं.

Android 17 में, इवेंट पर आधारित नए ट्रिगर जोड़े गए हैं. इनमें सबसे अहम TRIGGER_TYPE_OOM और TRIGGER_TYPE_ANOMALY हैं:

  • OOM ट्रिगर, OutOfMemoryError क्रैश होने के दौरान Java हीप डंप को अपने-आप इकट्ठा करता है. इससे सटीक तौर पर यह पता चलता है कि मेमोरी कैसे असाइन की गई है. इकट्ठा की गई ओओएम प्रोफ़ाइल, अगली बार ऐप्लिकेशन शुरू होने पर उपलब्ध कराई जाती है. साथ ही, registerForAllProfilingResults कॉलबैक को रजिस्टर किया जाता है.
  • अनियमितता का पता लगाने वाली सुविधा, परफ़ॉर्मेंस से जुड़ी गंभीर समस्याओं का पता लगाती है. जैसे, बहुत ज़्यादा बाइंडर स्पैम या मेमोरी थ्रेशोल्ड का उल्लंघन. मेमोरी की गड़बड़ी की वजह से, सिस्टम के ऐप्लिकेशन को बंद करने से ठीक पहले हीप डंप डिलीवर होता है.
    val profilingManager = 
applicationContext.getSystemService(ProfilingManager::class.java)
    val triggers = ArrayList<ProfilingTrigger>()  


    triggers.add(ProfilingTrigger.Builder(
                 ProfilingTrigger.TRIGGER_TYPE_ANOMALY))
    val mainExecutor: Executor = Executors.newSingleThreadExecutor()
    val resultCallback = Consumer<ProfilingResult> { profilingResult ->
        if (profilingResult.errorCode != ProfilingResult.ERROR_NONE) {
            // upload profile result to server for further analysis          
            setupProfileUploadWorker(profilingResult.resultFilePath)
        } 

    profilingManager.registerForAllProfilingResults(mainExecutor, resultCallback)
    profilingManager.addProfilingTriggers(triggers)

हीप डंप इकट्ठा करने के बाद, प्रोफ़ाइल को सर्वर से डाउनलोड किया जा सकता है. इसके अलावा, इसे स्थानीय तौर पर adb pull के ज़रिए डाउनलोड किया जा सकता है. साथ ही, फ़ाइल को Perfetto यूज़र इंटरफ़ेस (यूआई) में खींचकर छोड़ा जा सकता है. मेमोरी डीबग करने के वर्कफ़्लो को बेहतर बनाने के लिए, हीप डंप एक्सप्लोरर का इस्तेमाल करें. यह Perfetto यूज़र इंटरफ़ेस (यूआई) में हीप डंप के लिए नया डिफ़ॉल्ट व्यू है. यह टूल, Java हीप डंप की जांच करने के लिए एक आसान इंटरफ़ेस उपलब्ध कराता है. इससे ऑब्जेक्ट के लिए मेमोरी के बंटवारे के क्रम को देखा जा सकता है. साथ ही, मेमोरी के इस्तेमाल का हिसाब लगाया जा सकता है. इसके अलावा, यह भी पता लगाया जा सकता है कि गार्बेज कलेक्शन रूट से सबसे छोटा पाथ कौनसा है. Heap Dump Explorer का इस्तेमाल करके, मेमोरी लीक, बहुत ज़्यादा बिटमैप असाइनमेंट जैसे बहुत ज़्यादा मेमोरी इस्तेमाल करने वाले ऑब्जेक्ट का तुरंत पता लगाया जा सकता है. साथ ही, एक ही जगह पर हीप ऑब्जेक्ट असाइनमेंट का विश्लेषण किया जा सकता है.

pic5-perfettoheapdump-analyzer.png
हीप डंप एक्सप्लोरर में एम्बेड किए गए फ़्लेमग्राफ़ का इस्तेमाल करके, सबसे ज़्यादा हीप मेमोरी वाले ऑब्जेक्ट की जांच करें और उन पर नेविगेट करें.

नतीजा

दबाव में संसाधनों को असरदार तरीके से मैनेज करते हुए, उपयोगकर्ताओं को बेहतर अनुभव देने के लिए, R8 की मदद से बाइटकोड को ऑप्टिमाइज़ करना, इमेज लोड करने के सबसे सही तरीके अपनाना, और मेमोरी लीक की समस्या हल करना ज़रूरी है. इन ज़रूरी कदमों को उठाने से, ऐप्लिकेशन की परफ़ॉर्मेंस और स्थिरता को बनाए रखने में मदद मिलती है. साथ ही, इससे उपयोगकर्ता के कॉन्टेक्स्ट को सुरक्षित रखते हुए, ऐप्लिकेशन के अचानक बंद होने की समस्या को रोका जा सकता है. परफ़ॉर्मेंस से जुड़ी अपनी विशेषज्ञता को और बेहतर बनाने के लिए, मेमोरी से जुड़ी हमारी नई गाइड देखें.

लेखक:
पढ़ना जारी रखें