ओके ठीक है। मैं इस समस्या पर नरक और वापस आया हूँ। यहाँ कैसे आगे बढ़ना है। कीड़े हैं। यह पोस्ट वर्णन करता है कि कार्यान्वयन में बग का विश्लेषण कैसे करें और मुद्दों के आसपास काम करें।
बस संक्षेप में, यहाँ बताया गया है कि कैसे काम करना है। हर 30 मिनट या इसके बाद चलने वाली सेवाओं को नियमित रूप से परिमार्जन और समाप्त किया जाएगा। ऐसी सेवाएँ जो इससे अधिक समय तक जीवित रहना चाहती हैं, उन्हें Service.startForeground को कॉल करना होगा, जो सूचना पट्टी पर एक सूचना देता है, ताकि उपयोगकर्ताओं को पता चले कि आपकी सेवा स्थायी रूप से चल रही है और संभवतः बैटरी जीवन को चूस रही है। केवल 3 सेवा प्रक्रियाएं किसी भी समय खुद को अग्रभूमि सेवाओं के रूप में नामांकित कर सकती हैं। यदि तीन से अधिक अग्रभूमि सेवाएं हैं, तो Android मैला ढोने और समाप्त करने के लिए एक उम्मीदवार के रूप में सबसे पुरानी सेवा को नामांकित करेगा।
दुर्भाग्य से, अग्रभूमि सेवाओं को प्राथमिकता देने के संबंध में एंड्रॉइड में बग हैं, जो कि सर्विस बाइंडिंग फ़्लैग के विभिन्न संयोजनों द्वारा ट्रिगर किए जाते हैं। भले ही आपने अपनी सेवा को एक अग्रभूमि सेवा के रूप में नामांकित किया हो, फिर भी, Android आपकी सेवा को वैसे भी समाप्त कर सकता है, यदि आपकी प्रक्रिया में सेवाओं के किसी भी कनेक्शन को कभी-कभी बाध्यकारी झंडों के संयोजन के साथ बनाया गया है। विवरण नीचे दिया गया है।
ध्यान दें कि बहुत कम सेवाओं को अग्रभूमि सेवाओं की आवश्यकता होती है। आम तौर पर, आपको केवल एक अग्रभूमि सेवा होने की आवश्यकता होती है यदि आपके पास किसी तरह का लगातार सक्रिय या लंबे समय तक चलने वाला इंटरनेट कनेक्शन है जिसे उपयोगकर्ताओं द्वारा चालू या बंद किया जा सकता है, या रद्द किया जा सकता है। उन सेवाओं के उदाहरण जिन्हें अग्रभूमि स्थिति की आवश्यकता होती है: UPNP सर्वर, बहुत बड़ी फ़ाइलों के लंबे समय तक चलने वाले डाउनलोड, वाई-फाई द्वारा फाइल सिस्टम को सिंक्रनाइज़ करना और संगीत चलाना।
यदि आप कभी-कभार मतदान कर रहे हैं, या सिस्टम ब्रॉडकास्ट रिसीवर, या सिस्टम ईवेंट पर प्रतीक्षा कर रहे हैं, तो आप टाइमर पर या प्रसारण रिसीवर के जवाब में अपनी सेवा को जागृत करना बेहतर होगा, और फिर अपनी सेवा को एक बार पूरा होने देंगे। यह सेवाओं के लिए डिज़ाइन किया गया व्यवहार है। अगर आपको बस जिंदा रहना है, तो आगे पढ़ें।
अच्छी तरह से ज्ञात आवश्यकताओं (जैसे कॉलिंग Service.startForeground) पर बक्से की जाँच करने के बाद, देखने के लिए अगला स्थान Context.bindService कॉल में आपके द्वारा उपयोग किए जाने वाले झंडे पर है। बाँधने के लिए उपयोग किए जाने वाले झंडे विभिन्न प्रकार के अप्रत्याशित तरीकों से लक्ष्य सेवा प्रक्रिया की प्राथमिकता को प्रभावित करते हैं। विशेष रूप से, कुछ बाध्यकारी झंडों के उपयोग से एंड्रॉइड आपकी अग्रभूमि सेवा को गलत तरीके से नियमित सेवा में बदल सकता है। प्रक्रिया को प्राथमिकता देने के लिए उपयोग किए जाने वाले कोड पर काफी जोर दिया गया है। विशेष रूप से, एपीआई 14+ में संशोधन हैं जो पुराने बाध्यकारी झंडे का उपयोग करते समय बग पैदा कर सकते हैं; और 4.2.1 में निश्चित कीड़े हैं।
इस सब में आपका मित्र sysdump उपयोगिता है, जिसका उपयोग यह पता लगाने के लिए किया जा सकता है कि गतिविधि प्रबंधक ने आपकी सेवा प्रक्रिया को क्या प्राथमिकता दी है, और उन मामलों को स्पॉट किया है जहाँ उसने गलत प्राथमिकता दी है। अपनी सेवा प्राप्त करें और चल रहे हैं, और फिर अपने कंप्यूटर पर कमांड प्रॉम्प्ट से निम्नलिखित आदेश जारी करें:
adb शेल डंप्स गतिविधि गतिविधि> tmp.txt
सामग्री की जांच करने के लिए नोटपैड (वर्डपैड / राइट नहीं) का उपयोग करें।
पहले सत्यापित करें कि आपने अपनी सेवा को अग्रभूमि स्थिति में चलाने में सफलता प्राप्त कर ली है। डंप्स फ़ाइल के पहले भाग में प्रत्येक प्रक्रिया के लिए एक्टिविटी मैनजर गुण का विवरण होता है। निम्न की तरह एक पंक्ति के लिए देखें जो डिप्स फ़ाइल के पहले खंड में आपके एप्लिकेशन से मेल खाती है:
APP UID 10068 ProcessRecord {41937d40 2205: ट्यूनिन.सेवा / u0a10068}
सत्यापित करें कि अग्रभूमि सेवा = निम्न अनुभाग में सत्य है। छिपी और खाली सेटिंग्स के बारे में चिंता न करें; वे प्रक्रिया में गतिविधियों की स्थिति का वर्णन करते हैं, और उनमें सेवाओं के साथ प्रक्रियाओं के लिए विशेष रूप से प्रासंगिक नहीं लगते हैं। यदि अग्रभूमि सेवा सच नहीं है, तो आपको इसे सच करने के लिए Service.startForeground को कॉल करना होगा।
अगली चीज़ जिसे आपको देखने की आवश्यकता है वह है "प्रक्रिया LRU सूची (oom_adj द्वारा क्रमबद्ध):" शीर्षक वाली फ़ाइल के अंत के पास का भाग। इस सूची में प्रविष्टियां आपको यह निर्धारित करने की अनुमति देती हैं कि क्या एंड्रॉइड ने वास्तव में आपके एप्लिकेशन को अग्रभूमि सेवा के रूप में वर्गीकृत किया है। यदि आपकी प्रक्रिया इस सूची में सबसे नीचे है, तो यह सारांश नष्ट करने के लिए एक प्रमुख उम्मीदवार है। यदि आपकी प्रक्रिया सूची में सबसे ऊपर है, तो यह वास्तव में अविनाशी है।
आइए इस तालिका में एक पंक्ति देखें:
Proc
यह एक अग्रभूमि सेवा का एक उदाहरण है जिसने सब कुछ सही किया है। यहाँ प्रमुख फ़ील्ड "adj =" फ़ील्ड है। यह इंगित करता है कि सब कुछ कहे जाने के बाद एक्टिविटी मैनजर सर्विस द्वारा आपकी प्रक्रिया को प्राथमिकता दी गई थी। आप इसे "adj = prcp" (दृश्यमान अग्रभूमि सेवा) चाहते हैं; या "adj = vis" (किसी गतिविधि के साथ दृश्यमान प्रक्रिया) या "अग्र" (अग्रभूमि गतिविधि वाली प्रक्रिया)। यदि यह "adj = svc" (सेवा प्रक्रिया), या "adj = svcb" (विरासत सेवा)?, या "adj = bak" (खाली पृष्ठभूमि प्रक्रिया) है, तो आपकी प्रक्रिया समाप्ति के लिए एक संभावित उम्मीदवार है, और समाप्त हो जाएगी। स्मृति को पुनः प्राप्त करने के लिए कोई दबाव न होने पर भी हर 30 मिनट पर कम बार नहीं। लाइन पर शेष झंडे ज्यादातर Google इंजीनियरों के लिए नैदानिक डिबग जानकारी हैं। समाप्ति के निर्णय adj क्षेत्रों के आधार पर किए जाते हैं। संक्षेप में, / एफएस एक अग्रभूमि सेवा को इंगित करता है; / एफए एक गतिविधि के साथ अग्रभूमि प्रक्रिया को इंगित करता है। / B एक पृष्ठभूमि सेवा को इंगित करता है। अंत में लेबल सामान्य नियम को इंगित करता है जिसके तहत प्रक्रिया को प्राथमिकता दी गई थी। आमतौर पर यह adj = फ़ील्ड से मेल खाना चाहिए; लेकिन adj = मान को कुछ मामलों में अन्य सेवाओं या गतिविधियों के साथ सक्रिय बाइंडिंग पर बाध्यकारी झंडे के कारण ऊपर या नीचे समायोजित किया जा सकता है।
यदि आपने एक झंडे को बाध्यकारी झंडे के साथ फँसाया है, तो डिप्स लाइन इस तरह दिखाई देगी:
Proc
ध्यान दें कि कैसे adj फ़ील्ड का मान गलत तरीके से "adj = bak" (खाली पृष्ठभूमि प्रक्रिया) पर सेट होता है, जो मोटे तौर पर "कृपया मुझे अब समाप्त कर दें ताकि मैं इस निरर्थक अस्तित्व को समाप्त कर सकूं" प्रक्रिया के उद्देश्यों के लिए। लाइन के अंत में (fg-service) ध्वज को भी नोट करें जो इंगित करता है कि "adj" सेटिंग को निर्धारित करने के लिए "forground सेवा नियमों का उपयोग किया गया था। इस तथ्य के बावजूद कि fg- सेवा नियमों का उपयोग किया गया था, इस प्रक्रिया को एक adj सेटिंग असाइन किया गया था। "बक", और यह लंबे समय तक नहीं रहेगा। धीरे-धीरे डाल दिया, यह एक बग है।
तो लक्ष्य यह सुनिश्चित करना है कि आपकी प्रक्रिया हमेशा "adj = prcp" (या बेहतर) हो। और उस लक्ष्य को प्राप्त करने के लिए विधि बाध्यकारी झंडे को ट्विस्ट करना है जब तक कि आप प्राथमिकता असाइनमेंट में बग से बचने के लिए प्रबंधन नहीं करते हैं।
यहाँ मैं के बारे में पता कीड़े हैं। (1) यदि कोई सेवा या गतिविधि कभी भी Context.BIND_ABOVE_CLIENT का उपयोग करते हुए सेवा के लिए बाध्य होती है, तो आप जोखिम को चलाते हैं कि adj = सेटिंग को "bak" में डाउनग्रेड किया जाएगा, भले ही वह बंधन अधिक सक्रिय न हो। यह विशेष रूप से सच है यदि आपके पास सेवाओं के बीच बाइंडिंग भी है। 4.2.1 स्रोतों में एक स्पष्ट बग। (2) निश्चित रूप से सर्विस-टू-सर्विस बाइंडिंग के लिए BIND_ABOVE_CLIENT का उपयोग न करें। गतिविधि-से-सेवा कनेक्शनों के लिए भी इसका उपयोग न करें। BIND_ABOVE_CLIENT व्यवहार को लागू करने के लिए उपयोग किए जाने वाले ध्वज को प्रति-कनेक्शन आधार के बजाय प्रति-प्रक्रिया के आधार पर सेट किया गया लगता है, इसलिए यह सक्रिय गतिविधि-सेवा नहीं होने पर भी सेवा-से-सेवा बाइंडिंग के साथ बग को ट्रिगर करता है ध्वज सेट के साथ बाध्यकारी। सेवा-से-सेवा बाइंडिंग के साथ प्रक्रिया में कई सेवाएँ होने पर प्राथमिकता स्थापित करने में भी समस्याएँ होने लगती हैं। सर्विस-टू-सर्विस बाइंडिंग पर Context.BIND_WAIVE_PRIORITY (API 14) का उपयोग करने से मदद मिलती है। किसी गतिविधि से किसी सेवा के लिए बाध्य होने पर Context.BIND_IMPORTANT एक अधिक या कम अच्छा विचार है। ऐसा करने से आपकी प्रक्रिया की प्राथमिकता एक पायदान ऊपर बढ़ जाती है जब गतिविधि अग्रभूमि में होती है, जब गतिविधि रुकी हुई या समाप्त होने पर कोई स्पष्ट नुकसान किए बिना।
लेकिन कुल मिलाकर, रणनीति यह है कि आपके bindService झंडे को तब तक समायोजित किया जाए जब तक कि sysdump इंगित नहीं करता कि आपकी प्रक्रिया को सही प्राथमिकता मिली है।
मेरे उद्देश्यों के लिए, Context.BIND_AUTO_CREATE का उपयोग करना गतिविधि से सेवा बाइंडिंग के लिए Context.BIND_IMPORTANT, और Context.BIND_AUTO_CREATE | सेवा-से-सेवा बाइंडिंग के लिए संदर्भ। BIND_WAIVE_PRIORITY सही काम करने लगती है। आपका माइलेज अलग हो सकता है।
मेरा ऐप काफी जटिल है: दो पृष्ठभूमि सेवाएं, जिनमें से प्रत्येक स्वतंत्र रूप से अग्रभूमि सेवा राज्यों को पकड़ सकती है, साथ ही एक तिहाई जो अग्रभूमि सेवा राज्य भी ले सकती है; दो सेवाएं सशर्त रूप से एक-दूसरे को बांधती हैं; तीसरा पहले, हमेशा के लिए बांधता है। इसके अलावा, एक्टिविटीज एक अलग प्रक्रिया में चलती हैं (एनीमेशन को स्मूथ बनाता है)। गतिविधियों और सेवाओं को एक ही प्रक्रिया में चलाने से कोई फर्क नहीं पड़ता है।
स्कैवेंजिंग प्रक्रियाओं के लिए नियमों का कार्यान्वयन, (और sddump फ़ाइलों की सामग्री उत्पन्न करने के लिए उपयोग किया जाने वाला स्रोत कोड) कोर android फ़ाइल में पाया जा सकता है
frameworks\base\services\java\com\android\server\am\ActivityManagerService.java.
उत्साहित करना।
पुनश्च: यहाँ Android 5.0 के लिए sysdump तार की व्याख्या है। मैंने उनके साथ काम नहीं किया है, इसलिए उनमें से जो आप करेंगे। मेरा मानना है कि आप 4 को 'ए' या 'एस' चाहते हैं, और 5 को "आईएफ" या "आईबी", और 1 को जितना संभव हो उतना कम होना चाहिए (शायद 3 से नीचे, क्योंकि केवल 3 तीन अग्रभूमि सेवा प्रक्रियाएं सक्रिय रखी गई हैं डिफ़ॉल्ट कॉन्फ़िगरेशन में)।
Example:
Proc # : prcp F/S/IF trm: 0 31719: neirotech.cerebrum.attention:blePrcs/u0a77 (fg-service)
Format:
Proc # {1}: {2} {3}/{4}/{5} trm: {6} {7}: {8}/{9} ({10}
1: Order in list: lower is less likely to get trimmed.
2: Not sure.
3:
B: Process.THREAD_GROUP_BG_NONINTERACTIVE
F: Process.THREAD_GROUP_DEFAULT
4:
A: Foreground Activity
S: Foreground Service
' ': Other.
5:
-1: procState = "N ";
ActivityManager.PROCESS_STATE_PERSISTENT: procState = "P ";
ActivityManager.PROCESS_STATE_PERSISTENT_UI:procState = "PU";
ActivityManager.PROCESS_STATE_TOP: procState = "T ";
ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND: procState = "IF";
ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND: procState = "IB";
ActivityManager.PROCESS_STATE_BACKUP:procState = "BU";
ActivityManager.PROCESS_STATE_HEAVY_WEIGHT: procState = "HW";
ActivityManager.PROCESS_STATE_SERVICE: procState = "S ";
ActivityManager.PROCESS_STATE_RECEIVER: procState = "R ";
ActivityManager.PROCESS_STATE_HOME: procState = "HO";
ActivityManager.PROCESS_STATE_LAST_ACTIVITY: procState = "LA";
ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: procState = "CA";
ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: procState = "Ca";
ActivityManager.PROCESS_STATE_CACHED_EMPTY: procState = "CE";
{6}: trimMemoryLevel
{8} Process ID.
{9} process name
{10} appUid