सेवाओं को लिंक करने और प्रोसेस करने की स्थितियां

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

प्रोसेस की स्थितियां और ओओएम स्कोर

Android फ़्रेमवर्क, प्रोसेस की स्थितियां इस्तेमाल करता है. इससे हर चालू प्रोसेस की अहमियत को ट्रैक किया जाता है. इसके बाद, इन स्थितियों का इस्तेमाल OomAdjuster करता है, ताकि -1000 से 1000 के बीच की OOM स्कोर अडजस्टमेंट (oom_score_adj) वैल्यू असाइन की जा सके.

oom_score_adj की वैल्यू कम होने का मतलब है कि प्रोसेस ज़्यादा ज़रूरी है और Low Memory Killer (एलएमके) के इसे बंद करने की संभावना कम है.

प्रोसेस की सामान्य स्थितियां

यहां दी गई टेबल में, प्रोसेस की कुछ सामान्य स्थितियां और उनकी सामान्य oom_score_adj वैल्यू दिखाई गई हैं. पूरी और अप-टू-डेट सूची देखने के लिए, Android के सोर्स कोड में android.app.ActivityManager और com.android.server.am.psc.Constants देखें.

प्रोसेस का स्टेटस (संक्षिप्त नाम) ब्यौरा औसत oom_score_adj
PER (परसिस्टेंट) सिस्टम की ऐसी प्रोसेस जो हमेशा चालू रहनी चाहिए. जैसे, टेलीफ़ोनी. -800
TOP वह प्रोसेस जिससे उपयोगकर्ता फ़िलहाल इंटरैक्ट कर रहा है. 0
VIS (दिख रहा है) इस प्रोसेस में कोई ऐक्टिविटी दिख रही हो. जैसे, पारदर्शी डायलॉग के पीछे. 100
PERC (Perceptible) बैकग्राउंड में ऐसी प्रोसेस चल रही हो जिसके बारे में उपयोगकर्ता को पता हो. जैसे, संगीत चलाना. 200
FGS फ़ोरग्राउंड सेवा को होस्ट करने वाली प्रोसेस. 0 से 200 (अलग-अलग हो सकता है)
BTOP (बाउंड टॉप) यह प्रोसेस, टीओपी ऐप्लिकेशन से जुड़ी होती है. 100
BFGS बाउंड फ़ोरग्राउंड सेवा (आम तौर पर, सिस्टम से बाउंड होती है). 0
PREV (Previous) मौजूदा प्रोसेस से पहले, उपयोगकर्ता जिस प्रोसेस में था. 700
CACHED बैकग्राउंड में चल रहे ऐसे ऐप्लिकेशन जिन्हें सुरक्षित तरीके से बंद किया जा सकता है. 900 से 999

सेवाओं को बाइंड करने का असर

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

इस डायग्राम में, प्रोसेस A (TOP) को system_server के ज़रिए bindService() को कॉल करते हुए दिखाया गया है. साथ ही, प्रोसेस B को BTOP पर अपग्रेड करते हुए दिखाया गया है

BIND फ़्लैग की मदद से इनहेरिटेंस को कंट्रोल करना

Context.BIND_AUTO_CREATE का इस्तेमाल करने पर, इनहेरिटेंस डिफ़ॉल्ट रूप से लागू होता है. हालांकि, डेवलपर यह कंट्रोल कर सकते हैं कि बाइंडिंग से टारगेट प्रोसेस की अहमियत पर कैसे असर पड़ता है. इसके लिए, bindService() में मौजूद अलग-अलग फ़्लैग का इस्तेमाल किया जा सकता है.

OOM स्कोर के लिए मुख्य BIND फ़्लैग

सिस्टम-वाइड मेमोरी प्रेशर को मैनेज करते समय, ये फ़्लैग सबसे ज़्यादा काम के होते हैं:

  • BIND_AUTO_CREATE: यह सबसे ज़्यादा इस्तेमाल किया जाने वाला फ़्लैग है. इससे यह पक्का होता है कि सेवा की प्रोसेस शुरू हो गई है और जब तक बाइंडिंग मौजूद है, तब तक चालू रहेगी. डिफ़ॉल्ट रूप से, यह सर्वर प्रोसेस की प्राथमिकता को भी क्लाइंट से मैच करने के लिए बढ़ाता है.
  • BIND_NOT_FOREGROUND: इससे टारगेट सेवा की प्रोसेस को फ़ोरग्राउंड शेड्यूलिंग प्राथमिकता (सीपीयू प्राथमिकता) पर नहीं ले जाया जा सकता. हालांकि, इससे मेमोरी की प्राथमिकता (oom_score_adj) को अब भी बढ़ाया जा सकता है. यह बैकग्राउंड में होने वाले ऐसे काम के लिए फ़ायदेमंद है जिसमें सीपीयू साइकल के लिए यूज़र इंटरफ़ेस (यूआई) से मुकाबला नहीं करना चाहिए. हालांकि, इसे बंद होने से बचाया जाना चाहिए.
  • BIND_WAIVE_PRIORITY: यह एक बहुत ही अहम फ़्लैग है. यह सिस्टम को निर्देश देता है कि टारगेट प्रोसेस की शेड्यूलिंग या मेमोरी मैनेजमेंट की प्राथमिकता पर कोई असर न पड़े. इस सेवा को इस तरह मैनेज किया जाएगा जैसे कि यह LRU सूची में मौजूद कोई सामान्य बैकग्राउंड प्रोसेस हो. इससे यह सेवा, बाइंड होने के दौरान भी OOM किलिंग के लिए उपलब्ध हो जाएगी.
  • BIND_ABOVE_CLIENT: इससे पता चलता है कि सेवा, क्लाइंट ऐप्लिकेशन से ज़्यादा ज़रूरी है. जब सिस्टम को मेमोरी वापस चाहिए होती है, तो वह बाइंड की गई सेवा को बंद करने से पहले, क्लाइंट ऐप्लिकेशन को बंद कर देगा. यह BIND_AUTO_CREATE से "ज़्यादा सुरक्षित" है, क्योंकि यह क्लाइंट की कीमत पर सेवा के लिए सुरक्षा की एक अतिरिक्त लेयर उपलब्ध कराता है.
  • BIND_NOT_PERCEPTIBLE: इससे टारगेट की गई सेवा की अहमियत को PERCEPTIBLE लेवल से नीचे कर दिया जाता है. इससे सिस्टम को अपनी मेमोरी वापस पाने में मदद मिलती है, ताकि वह उपयोगकर्ता के लिए ज़्यादा ज़रूरी प्रोसेस के लिए जगह बना सके.

हाथों-हाथ: बाइंडिंग इफ़ेक्ट देखना

हम MemoryLab ऐप्लिकेशन का इस्तेमाल करके यह दिखाएँगे कि TOP ऐप्लिकेशन से बाइंडिंग करने पर, किसी दूसरी प्रोसेस की स्थिति पर क्या असर पड़ता है.

1. MemoryLab लॉन्च करें

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

adb shell am start -n com.android.memorylab/.MainActivity

2. प्रोसेस की पहचान करना

बाइंड करने से पहले, प्रोसेस की स्थितियां देखें. MemoryLab अपना मुख्य यूज़र इंटरफ़ेस (यूआई) एक प्रोसेस में चलाता है. साथ ही, इसमें एक RemoteService होता है, जो :remote प्रोसेस में चलता है.

adb shell dumpsys activity processes com.android.memorylab

आउटपुट स्निपेट का उदाहरण:

  Process OOM control (154 total, non-act at 7, non-svc at 7):
    Proc #0: fg       T/A/TOP  LCMNFUATI  t: 0 13470:com.android.memorylab/u0a417 (top-activity)
        oom: max=1001 curRaw=0 setRaw=0 cur=0 set=0
        state: cur=TOP  set=TOP   lastRss=0.00 lastCachedRss=0.00

आपको मुख्य प्रोसेस com.android.memorylab, TOP स्थिति में दिखेगी. :remote प्रोसेस अभी शुरू नहीं हुई है.

3. ट्रिगर बाइंडिंग

सेवा को बाइंड करने के लिए, ऐप्लिकेशन को ब्रॉडकास्ट भेजें:

adb shell am broadcast -a com.android.memorylab.LEAK_BINDER

4. डिवाइस की स्थिति में बदलाव होने पर सूचना पाना

प्रोसेस की स्थितियों की फिर से जांच करें:

adb shell dumpsys activity processes | grep -A 10 "com.android.memorylab"

आउटपुट स्निपेट का उदाहरण:

  Proc #  1: vis      F/ /BTOP ---NFUATI  t: 0 13560:com.android.memorylab:remote/u0a417 (service)
    com.android.memorylab/.RemoteService<=Proc{13470:com.android.memorylab/u0a417}
    oom: max=1001 curRaw=100 setRaw=100 cur=100 set=100
    state: cur=BTOP set=BTOP  lastRss=0.00 lastCachedRss=0.00

:remote प्रोसेस अब चालू है और BTOP (बाउंड टॉप) स्थिति में है. साथ ही, oom_score_adj की वैल्यू 100 है. यह सामान्य बैकग्राउंड में चलने वाली सेवा (जो 500 या इससे ज़्यादा पर होगी) की तुलना में ज़्यादा सुरक्षित है. नोटेशन <=Proc{...} से पता चलता है कि प्राथमिकता बढ़ाने के लिए कौनसी प्रोसेस ज़िम्मेदार है.

5. बैकग्राउंड में चलाएं

डिवाइस पर मौजूद HOME बटन दबाएं. राज्यों के नाम की फिर से जांच करें:

adb shell dumpsys activity processes | grep -A 10 "com.android.memorylab"

आउटपुट स्निपेट का उदाहरण:

    Proc #  2: prev     b/ /LAST --------I  t: 0 13560:com.android.memorylab:remote/u0a417 (service)
        com.android.memorylab/.RemoteService<=Proc{13470:com.android.memorylab/u0a417}
        oom: max=1001 curRaw=700 setRaw=700 cur=700 set=700
        state: cur=LAST set=LAST  lastRss=0.00 lastCachedRss=0.00
    Proc #  1: prev     b/ /LAST --------I  t: 0 13470:com.android.memorylab/u0a417 (previous)
        oom: max=1001 curRaw=700 setRaw=700 cur=700 set=700
        state: cur=LAST set=LAST  lastRss=209MB lastCachedRss=0.00

अब दोनों प्रोसेस को कम प्राथमिकता वाली स्थिति (PREV / oom_score_adj 700) में बदल दिया गया है, क्योंकि क्लाइंट प्रोसेस अब TOP नहीं है. (ध्यान दें: LAST स्टेट डंप में LAST_ACTIVITY इंटरनल स्टेट को दिखाता है, जो हाई-लेवल की खास जानकारी में PREV से मैप होता है).

procstats की मदद से विश्लेषण किया जा रहा है

procstats टूल से, इन स्थितियों के बारे में पुरानी जानकारी मिलती है.

# View stats for MemoryLab over the last hour
adb shell dumpsys procstats --hours 1 com.android.memorylab

आउटपुट स्निपेट का उदाहरण:

  *   com.android.memorylab / u0a417 / v37:
      *   Prc com.android.memorylab / u0a417 / v37:
             TOTAL: 0.89% (0.00-0.00-0.00/0.00-0.00-0.00/210MB-210MB-210MB over 1)
               Top: 0.89% (0.00-0.00-0.00/0.00-0.00-0.00/210MB-210MB-210MB over 1)
      *   Prc com.android.memorylab:remote / u0a417 / v37:
             TOTAL: 0.19%
           Bnd Top: 0.19%

यहां, Bnd Top से पता चलता है कि रिमोट प्रोसेस, TOP स्थिति में किसी ऐप्लिकेशन से कितने प्रतिशत समय तक जुड़ी रही.

Perfetto की मदद से बाइंडिंग कैप्चर करना और उनका विश्लेषण करना

dumpsys से आपको स्नैपशॉट मिलता है, जबकि Perfetto से आपको यह पता चलता है कि बाइंडिंग कब हुई और ओओएम स्कोर में रीयल-टाइम में कैसे बदलाव होता है.

1. ट्रेस रिकॉर्ड करना

ऐसे कॉन्फ़िगरेशन का इस्तेमाल करें जिसमें linux.process_stats और am एट्रैस कैटगरी शामिल हो:

adb shell perfetto -c - --txt -o /data/misc/perfetto-traces/service_bindings.perfetto-trace <<EOF
buffers: { size_kb: 65536 }
data_sources: {
    config {
        name: "linux.process_stats"
        process_stats_config { proc_stats_poll_ms: 100 }
    }
}
data_sources: {
    config {
        name: "linux.ftrace"
        ftrace_config { ftrace_events: "am/am_proc_bound" }
    }
}
duration_ms: 15000
EOF

2. क्वेरी के ओओएम स्कोर में बदलाव

PerfettoSQL का इस्तेमाल करके, यह देखा जा सकता है कि यूज़र इंटरफ़ेस (यूआई) प्रोसेस के मुकाबले, रिमोट प्रोसेस का ओओएम स्कोर कैसे बदला:

SELECT ts, p.name, value AS oom_score_adj
FROM counter c
JOIN process_counter_track t ON c.track_id = t.id
JOIN process p USING (upid)
WHERE p.name LIKE 'com.android.memorylab%'
  AND t.name = 'oom_score_adj'
ORDER BY ts;

3. बाइंडिंग इवेंट की पहचान करना

यह देखने के लिए कि बाइंडिंग डिपेंडेंसी कब सेट की गई थी और इसे किस प्रोसेस ने शुरू किया था, इस क्वेरी का इस्तेमाल करें:

SELECT
    s.ts,
    p.name AS process_name,
    t.name AS thread_name,
    s.name AS slice_name
FROM slice s
JOIN thread_track tt ON s.track_id = tt.id
JOIN thread t USING (utid)
JOIN process p USING (upid)
WHERE s.name LIKE 'bindService:{com.android.memorylab%';

सिस्टम से ऐप्लिकेशन बाइंडिंग

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

यहां कुछ ऐसे उदाहरण दिए गए हैं जिन्हें किसी सामान्य डिवाइस पर देखा जा सकता है:

VoiceInteractor

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

जब असिस्टेंट को ट्रिगर किया जाता है (जैसे कि Google Pixel फ़ोन पर "Ok Google" हॉटवर्ड), तो डिजिटल असिस्टेंट को तुरंत जवाब देना होता है. यह पक्का करने के लिए, system_server आवाज़ से इंटरैक्ट करने की सेवा से हमेशा जुड़ा रहता है. यह सेवा उपयोगकर्ता चुनता है.

Google ऐप्लिकेशन की इंटरैक्टर प्रोसेस से सिस्टम_सर्वर को बाइंड करने वाला डायग्राम

अगर प्रोसेस की स्थितियों की जांच की जाती है (जैसे, dumpsys activity processes का इस्तेमाल करके), तो आपको BFGS (बाउंड फ़ोरग्राउंड सेवा) स्थिति में com.google.android.googlequicksearchbox:interactor जैसी प्रोसेस दिख सकती है. इसे system_server (यूआईडी 1000) से बाइंड करके चालू रखा जाता है.

NotificationListenerService

सिस्टम से ऐप्लिकेशन को बाइंड करने के कुछ मामलों में, हमारा मकसद कम समय में ऐप्लिकेशन को शुरू करना नहीं होता. इसके बजाय, हमारा मकसद ऐप्लिकेशन को बार-बार बंद होने से रोकना होता है. NotificationListenerService, इसका एक बेहतरीन उदाहरण है. यह एक ऐसी सेवा है जिसे नई सूचनाएं पोस्ट किए जाने या हटाए जाने पर, सिस्टम से कॉल मिलते हैं. स्मार्टफ़ोन का इस्तेमाल करने वाले किसी व्यक्ति को दिन भर में सैकड़ों सूचनाएं मिल सकती हैं. अगर सिस्टम किसी सूचना लिसनर से अनबाउंड हो जाता है, तो उस ऐप्लिकेशन की प्रोसेस, कैश मेमोरी में सेव की गई स्थिति में चली जाएगी. साथ ही, LMK उसे बंद कर सकता है.

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

लॉन्चर की "-1 स्क्रीन" (न्यूज़ फ़ीड)

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

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

अन्य सामान्य उदाहरण

  • लॉन्चर (HOME_APP_ADJ): लॉन्चर ऐप्लिकेशन (Home) को प्राथमिकता वाली सूची में खास स्लॉट मिलता है. यह हमेशा किसी सेवा से नहीं जुड़ा होता है. इसे HOME_APP_ADJ (आम तौर पर 600) असाइन किया जाता है. सिस्टम, लॉन्चर को चालू रखने की कोशिश करता है, क्योंकि उपयोगकर्ता अक्सर इसका इस्तेमाल करता है. असल में, सिस्टम लॉन्चर को बंद करने के बजाय, पहले इस्तेमाल किए गए ऐप्लिकेशन (PREV_APP_ADJ = 700) को बंद करना पसंद करेगा. ऐसा इसलिए, क्योंकि लॉन्चर को बंद करने से, किसी भी ऐप्लिकेशन से बाहर निकलते समय उपयोगकर्ता अनुभव धीमा हो जाएगा. ऐसा इसलिए होगा, क्योंकि उपयोगकर्ता को लॉन्चर के कोल्ड स्टार्ट होने का इंतज़ार करना होगा.
  • इनपुट पद्धति संपादक (आईएमई): टाइपिंग करते समय, सिस्टम आपके चुने गए कीबोर्ड ऐप्लिकेशन (जैसे, Gboard) से जुड़ जाता है. इससे कीबोर्ड की प्रोसेस को बेहतर स्थिति में रखा जाता है. भले ही, कीबोर्ड को कुछ समय के लिए छिपा दिया गया हो. इससे यह पक्का होता है कि किसी दूसरे टेक्स्ट फ़ील्ड पर टैप करने पर, कीबोर्ड तुरंत फिर से दिखने लगे.
  • एनएफ़सी पेमेंट: पेमेंट करने के लिए फ़ोन को टैप करने पर, सिस्टम एनएफ़सी पेमेंट सेवा (जैसे, Google Wallet) से जुड़ जाता है. इन लेन-देन के लिए, कारोबारी या कंपनी के टर्मिनल से रीयल-टाइम में पुष्टि करना ज़रूरी होता है. अगर पेमेंट ऐप्लिकेशन को कोल्ड स्टार्ट करना पड़ा, तो लेन-देन का समय खत्म हो सकता है और वह पूरा नहीं हो सकता.

ट्रेडऑफ़ और परफ़ॉर्मेंस में अचानक गिरावट

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

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

सेवा को बाइंड करने से जुड़ी सामान्य समस्याएं

सर्विस बाइंडिंग, सीधे तौर पर oom_score_adj को बढ़ाती हैं. इसलिए, बाइंडिंग को हासिल करने या स्ट्रक्चर करने के तरीके में लाइफ़साइकल से जुड़ी छोटी-छोटी गलतियां, कई घंटों तक मेमोरी के बड़े हिस्से को खास स्थितियों (BTOP, BFGS या PERC) में पिन कर सकती हैं. बाउंड सेवाओं को डिज़ाइन या ऑडिट करते समय, इन सामान्य एंटी-पैटर्न से सावधान रहें.

लंबे समय तक चलने वाले क्लाइंट में unbindService() को भूल जाना

Application सिंगलटन, बैकग्राउंड मैनेजर या Activity से किसी सेवा को बाइंड करने पर, onStart() में bindService() को कॉल करने वाला, onStop() में मैचिंग unbindService() के बिना ServiceConnection को लीक करता है.

जब तक यह बाइंडिंग चालू रहती है, तब तक टारगेट प्रोसेस को क्लाइंट की बढ़ी हुई प्राथमिकता मिलती है. अगर क्लाइंट, सिस्टम का कोई स्थायी कॉम्पोनेंट या फ़ोरग्राउंड ऐप्लिकेशन है, तो बाउंड सर्विस प्रोसेस, BFGS या BTOP में हमेशा के लिए पिन हो जाती है. इससे procstats में यह प्रोसेस, 100% के आस-पास दिखती है. इस वजह से, LMK अपनी मेमोरी वापस नहीं ले पाता, भले ही सर्विस पूरी तरह से कुछ समय से इस्तेमाल में नहीं है.

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

मेमोरी का ज़्यादा इस्तेमाल करने वाले यूज़र इंटरफ़ेस (यूआई) के साथ बाउंड सर्विस को एक साथ रखना

डिफ़ॉल्ट रूप से, किसी APK के सभी कॉम्पोनेंट एक ही प्रोसेस में चलते हैं. अगर आपका ऐप्लिकेशन, मुख्य Activity की तरह ही प्रोसेस में हल्की बाउंड सेवा (जैसे कि NotificationListenerService, विजेट उपलब्ध कराने वाली कंपनी या प्लगिन सेवा, जिसे सिस्टम या लॉन्चर बाइंड करता है) दिखाता है, तो पूरी प्रोसेस में सेवा का बेहतर स्टेटस (BFGS या PERC, आम तौर पर 200 या इससे कम का oom_score_adj) दिखता है.

जब उपयोगकर्ता आपके ऐप्लिकेशन का यूज़र इंटरफ़ेस (यूआई) खोलता है, तो प्रोसेस में लार्ज व्यू हायरार्की, डिकोड किए गए बिटमैप, और ग्राफ़िक बफ़र असाइन किए जाते हैं. जब उपयोगकर्ता किसी दूसरे ऐप्लिकेशन पर जाता है, तो प्रोसेस CACHED (900 या इससे ज़्यादा oom_score_adj) पर नहीं जाती. इसकी वजह यह है कि चालू सेवा बाइंडिंग, प्रोसेस को ऊपर रखती है. इससे दो समस्याएं होती हैं:

  • बैकग्राउंड में मेमोरी कंपैक्शन नहीं होता: सिस्टम की CachedAppOptimizer सिर्फ़ उन प्रोसेस को कंपैक्ट करती है जो CACHED स्थिति में आ जाती हैं.
  • कैश की गई LRU मेमोरी को ट्रिम नहीं किया जाता या LMK को वापस नहीं लिया जाता: जब कोई सेवा बाइंडिंग, प्रोसेस को बेहतर स्थिति में रखती है, तब सिस्टम, कैश की गई LRU सूची (TRIM_MEMORY_BACKGROUND और इससे ऊपर) से जुड़ी बैकग्राउंड ट्रिम कॉलबैक डिलीवर नहीं करता है. अगर आपका ऐप्लिकेशन TRIM_MEMORY_UI_HIDDEN या Activity.onStop() पर यूज़र इंटरफ़ेस (यूआई) संसाधनों को साफ़ तौर पर रिलीज़ नहीं करता है, तो पीक यूज़र इंटरफ़ेस (यूआई) के लिए मेमोरी का आवंटन, ओओएम स्कोर के साथ रैम में पिन किया जाता है. इससे एलएमके उन्हें आसानी से वापस नहीं ले पाता.

समस्या हल करना: जब आपका यूज़र इंटरफ़ेस (यूआई) दिखना बंद हो जाए, तब यूज़र इंटरफ़ेस (यूआई) की कैश मेमोरी, डिकोड किए गए बिटमैप, और व्यू रेफ़रंस को साफ़ तौर पर हटा दें. इसके लिए, Activity.onStop(), ComponentCallbacks2.onTrimMemory(TRIM_MEMORY_UI_HIDDEN), Application.ActivityLifecycleCallbacks या ProcessLifecycleOwner का इस्तेमाल करें. इसके अलावा, android:process मेनिफ़ेस्ट एट्रिब्यूट का इस्तेमाल करके, हमेशा बाइंड रहने वाली सेवा को किसी अलग और हल्की प्रोसेस में ले जाएं. इससे सिस्टम, आपकी मुख्य यूज़र इंटरफ़ेस (यूआई) प्रोसेस को CACHED स्थिति में ले जा सकता है, ताकि वह अपनी मेमोरी को छोटा कर सके या उसे वापस पा सके.

प्राथमिकता से जुड़ी शर्तों को पूरा न करने वाले फ़्लैग को हटाना

सिर्फ़ BIND_AUTO_CREATE के साथ bindService() को कॉल करने पर, कॉल करने वाले व्यक्ति की पूरी शेड्यूलिंग और मेमोरी की प्राथमिकता, टारगेट सेवा को ट्रांसफ़र हो जाती है. जब कोई फ़ोरग्राउंड ऐप्लिकेशन, सिर्फ़ BIND_AUTO_CREATE का इस्तेमाल करके बैकग्राउंड में काम करने वाली किसी Analytics, लॉगिंग या प्रीफ़ेचिंग सेवा से जुड़ता है, तो वह अनजाने में उस बैकग्राउंड वर्कर को BTOP पर प्रमोट कर देता है.

समस्या हल करने का तरीका: जब ऐसी सहायक या सबसे अच्छी सेवाओं से बाइंड किया जा रहा हो जिन्हें फ़ोरग्राउंड यूज़र इंटरफ़ेस (यूआई) के बराबर सुरक्षा की ज़रूरत नहीं होती, तब BIND_AUTO_CREATE को BIND_WAIVE_PRIORITY, BIND_NOT_FOREGROUND या BIND_NOT_PERCEPTIBLE के साथ मिलाएं. इससे सिस्टम, कैश मेमोरी में सेव की गई LRU सूची में टारगेट प्रोसेस को मैनेज कर पाएगा.


← इलाका | ↑ ऊपर जाएं | सिस्टम-वाइड →