मुझे पता है, बहुत सारे उत्तर पहले ही प्रकाशित हो चुके हैं, हालांकि सच्चाई यह है - startForegroundService को एक ऐप स्तर पर तय नहीं किया जा सकता है और आपको इसका उपयोग बंद कर देना चाहिए। Context # startForegroundService () के बाद 5 सेकंड के भीतर सेवा # startForeground () API का उपयोग करने के लिए Google की सिफारिश को कुछ ऐसा नहीं कहा जा सकता है जो एक ऐप हमेशा कर सकता है।
एंड्रॉइड एक साथ बहुत सारी प्रक्रियाएं चलाता है और इसमें कोई गारंटी नहीं है कि लूपर आपकी लक्ष्य सेवा को कॉल करेगा जिसे 5 सेकंड के भीतर startForeground () कॉल करना है। यदि आपकी लक्ष्य सेवा 5 सेकंड के भीतर कॉल प्राप्त नहीं करती है, तो आप भाग्य से बाहर हैं और आपके उपयोगकर्ता ANR स्थिति का अनुभव करेंगे। आपके स्टैक ट्रेस में आपको कुछ इस तरह दिखाई देगा:
Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{1946947 u0 ...MessageService}
main" prio=5 tid=1 Native
| group="main" sCount=1 dsCount=0 flags=1 obj=0x763e01d8 self=0x7d77814c00
| sysTid=11171 nice=-10 cgrp=default sched=0/0 handle=0x7dfe411560
| state=S schedstat=( 1337466614 103021380 2047 ) utm=106 stm=27 core=0 HZ=100
| stack=0x7fd522f000-0x7fd5231000 stackSize=8MB
| held mutexes=
#00 pc 00000000000712e0 /system/lib64/libc.so (__epoll_pwait+8)
#01 pc 00000000000141c0 /system/lib64/libutils.so (android::Looper::pollInner(int)+144)
#02 pc 000000000001408c /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+60)
#03 pc 000000000012c0d4 /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+44)
at android.os.MessageQueue.nativePollOnce (MessageQueue.java)
at android.os.MessageQueue.next (MessageQueue.java:326)
at android.os.Looper.loop (Looper.java:181)
at android.app.ActivityThread.main (ActivityThread.java:6981)
at java.lang.reflect.Method.invoke (Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1445)
जैसा कि मैं समझता हूं, लूपर ने यहां कतार का विश्लेषण किया है, एक "अपमानजनक" पाया और बस इसे मार दिया। सिस्टम अभी खुश और स्वस्थ है, जबकि डेवलपर्स और उपयोगकर्ता नहीं हैं, लेकिन चूंकि Google सिस्टम के लिए अपनी जिम्मेदारियों को सीमित करता है, इसलिए उन्हें बाद के दो के बारे में क्यों ध्यान रखना चाहिए? जाहिर है वे नहीं है। क्या वे इसे बेहतर बना सकते थे? बेशक, उदाहरण के लिए, वे "एप्लिकेशन व्यस्त है" संवाद कर सकते हैं, उपयोगकर्ता को ऐप का इंतजार करने या मारने के बारे में निर्णय लेने के लिए कह सकते हैं, लेकिन परेशान क्यों होते हैं, यह उनकी जिम्मेदारी नहीं है। मुख्य बात यह है कि सिस्टम अब स्वस्थ है।
मेरी टिप्पणियों से, यह अपेक्षाकृत कम ही होता है, मेरे मामले में 1K उपयोगकर्ताओं के लिए एक महीने में लगभग 1 दुर्घटना। इसे फिर से प्रस्तुत करना असंभव है, और भले ही इसे पुन: पेश किया जाए, कुछ भी नहीं है जो आप इसे स्थायी रूप से ठीक करने के लिए कर सकते हैं।
इस धागे में "स्टार्ट" के बजाय "बाइंड" का उपयोग करने का एक अच्छा सुझाव था और फिर जब सेवा तैयार हो जाती है, तो onServiceConnected प्रक्रिया करें, लेकिन फिर, इसका मतलब है कि startForegroundService कॉल का उपयोग बिल्कुल नहीं करें।
मुझे लगता है, Google की ओर से सही और ईमानदार कार्रवाई सभी को बताना होगा कि startForegourndServcie में एक कमी है और इसका उपयोग नहीं किया जाना चाहिए।
सवाल अभी भी बना हुआ है: इसके बजाय क्या उपयोग करना है? हमारे लिए सौभाग्य से, अब जॉबस्क्राइडर और जॉब सर्विस हैं, जो अग्रभूमि सेवाओं के लिए एक बेहतर विकल्प हैं। यह एक बेहतर विकल्प है, इस वजह से:
जब कोई नौकरी चल रही होती है, तो सिस्टम आपके ऐप की ओर से एक वैकलॉक रखता है। इस कारण से, आपको यह गारंटी देने के लिए कोई कार्रवाई करने की आवश्यकता नहीं है कि डिवाइस नौकरी की अवधि के लिए जागता है।
इसका मतलब है कि आपको अब वैकलॉक्स को संभालने की परवाह करने की आवश्यकता नहीं है और इसीलिए यह अग्रभूमि सेवाओं से अलग नहीं है। कार्यान्वयन के दृष्टिकोण से JobScheduler आपकी सेवा नहीं है, यह एक प्रणाली है, संभवत: यह कतार को सही तरीके से संभालेगा, और Google कभी भी अपने बच्चे को समाप्त नहीं करेगा :)
सैमसंग ने अपने सैमसंग एक्सेसरी प्रोटोकॉल (SAP) में स्टार्टफॉरेग्राउंड सर्विस से जॉबस्क्रिलर और जॉब सर्विस में स्विच किया है। यह बहुत उपयोगी है जब स्मार्टवॉच जैसे उपकरणों को फोन जैसे मेजबानों से बात करने की आवश्यकता होती है, जहां ऐप के मुख्य धागे के माध्यम से उपयोगकर्ता के साथ काम करने की आवश्यकता होती है। चूंकि नौकरियों को अनुसूचक द्वारा मुख्य धागे में पोस्ट किया जाता है, इसलिए यह संभव हो जाता है। हालांकि आपको याद रखना चाहिए कि नौकरी मुख्य धागे पर चल रही है और सभी भारी सामान को अन्य थ्रेड्स और एसिंक्स कार्यों में लोड करना है।
यह सेवा आपके एप्लिकेशन के मुख्य थ्रेड पर चल रहे हैंडलर पर आने वाली प्रत्येक नौकरी को निष्पादित करती है। इसका मतलब है कि आपको अपने निष्पादन तर्क को अपने चयन के दूसरे थ्रेड / हैंडलर / AsyncTask पर लोड करना होगा
JobScheduler / JobService पर स्विच करने का एकमात्र नुकसान यह है कि आपको पुराने कोड को रिफलेक्टर करने की आवश्यकता होगी, और यह मज़ेदार नहीं है। मैंने पिछले दो दिन बिताए हैं कि नए सैमसंग एसएपी कार्यान्वयन का उपयोग करें। मैं अपनी क्रैश रिपोर्ट देखूंगा और आपको बता दूंगा कि क्या क्रैश फिर से दिखाई देंगे। सैद्धांतिक रूप से ऐसा नहीं होना चाहिए, लेकिन हमेशा ऐसे विवरण होते हैं जिनके बारे में हमें जानकारी नहीं हो सकती है।
अपडेट करें
Play Store द्वारा कोई और क्रैश रिपोर्ट नहीं की गई है। इसका मतलब यह है कि जॉबस्क्राइडर / जॉब सर्विस को इस तरह की समस्या नहीं है और इस मॉडल पर स्विच करना एक बार और हमेशा के लिए startForegroundService इश्यू से छुटकारा पाने का सही तरीका है। मुझे उम्मीद है, Google / Android इसे पढ़ता है और अंततः सभी के लिए एक आधिकारिक मार्गदर्शन प्रदान करेगा / टिप्पणी करेगा / सलाह देगा।
अद्यतन २
SAP का उपयोग करने वाले और यह पूछने के लिए कि SAP V2 JobService स्पष्टीकरण का उपयोग कैसे करता है।
अपने कस्टम कोड में आपको SAP (यह कोटलिन) को इनिशियलाइज़ करना होगा:
SAAgentV2.requestAgent(App.app?.applicationContext,
MessageJobs::class.java!!.getName(), mAgentCallback)
अब आपको सैमसंग के कोड को डिकंपाइल करने की जरूरत है, यह देखने के लिए कि अंदर क्या चल रहा है। SAAgentV2 में requestAgent कार्यान्वयन और निम्न पंक्ति पर एक नज़र डालें:
SAAgentV2.d var3 = new SAAgentV2.d(var0, var1, var2);
where d defined as below
private SAAdapter d;
अब SAAdapter वर्ग पर जाएं और onServiceConnectionRequested फ़ंक्शन खोजें जो निम्न कॉल का उपयोग करके किसी कार्य को शेड्यूल करता है:
SAJobService.scheduleSCJob(SAAdapter.this.d, var11, var14, var3, var12);
SAJobService, Android'd JobService का सिर्फ एक कार्यान्वयन है और यह वह है जो एक नौकरी निर्धारण करता है:
private static void a(Context var0, String var1, String var2, long var3, String var5, SAPeerAgent var6) {
ComponentName var7 = new ComponentName(var0, SAJobService.class);
Builder var10;
(var10 = new Builder(a++, var7)).setOverrideDeadline(3000L);
PersistableBundle var8;
(var8 = new PersistableBundle()).putString("action", var1);
var8.putString("agentImplclass", var2);
var8.putLong("transactionId", var3);
var8.putString("agentId", var5);
if (var6 == null) {
var8.putStringArray("peerAgent", (String[])null);
} else {
List var9;
String[] var11 = new String[(var9 = var6.d()).size()];
var11 = (String[])var9.toArray(var11);
var8.putStringArray("peerAgent", var11);
}
var10.setExtras(var8);
((JobScheduler)var0.getSystemService("jobscheduler")).schedule(var10.build());
}
जैसा कि आप देख रहे हैं, इस प्रणाली की सेवा पाने के लिए और नौकरी शेड्यूल करने के लिए यहां अंतिम पंक्ति एंड्रॉइड जॉबस्किड्यूलर का उपयोग करती है।
अनुरोध कॉल में हमने mAgentCallback पास किया है, जो एक कॉलबैक फ़ंक्शन है जो एक महत्वपूर्ण घटना होने पर नियंत्रण प्राप्त करेगा। इस प्रकार मेरे ऐप में कॉलबैक परिभाषित किया गया है:
private val mAgentCallback = object : SAAgentV2.RequestAgentCallback {
override fun onAgentAvailable(agent: SAAgentV2) {
mMessageService = agent as? MessageJobs
App.d(Accounts.TAG, "Agent " + agent)
}
override fun onError(errorCode: Int, message: String) {
App.d(Accounts.TAG, "Agent initialization error: $errorCode. ErrorMsg: $message")
}
}
MessageJobs यहां एक वर्ग है जिसे मैंने सैमसंग स्मार्टवॉच से आने वाले सभी अनुरोधों को संसाधित करने के लिए लागू किया है। यह पूर्ण कोड नहीं है, केवल एक कंकाल:
class MessageJobs (context:Context) : SAAgentV2(SERVICETAG, context, MessageSocket::class.java) {
public fun release () {
}
override fun onServiceConnectionResponse(p0: SAPeerAgent?, p1: SASocket?, p2: Int) {
super.onServiceConnectionResponse(p0, p1, p2)
App.d(TAG, "conn resp " + p1?.javaClass?.name + p2)
}
override fun onAuthenticationResponse(p0: SAPeerAgent?, p1: SAAuthenticationToken?, p2: Int) {
super.onAuthenticationResponse(p0, p1, p2)
App.d(TAG, "Auth " + p1.toString())
}
override protected fun onServiceConnectionRequested(agent: SAPeerAgent) {
}
}
override fun onFindPeerAgentsResponse(peerAgents: Array<SAPeerAgent>?, result: Int) {
}
override fun onError(peerAgent: SAPeerAgent?, errorMessage: String?, errorCode: Int) {
super.onError(peerAgent, errorMessage, errorCode)
}
override fun onPeerAgentsUpdated(peerAgents: Array<SAPeerAgent>?, result: Int) {
}
}
जैसा कि आप देखते हैं, MessageJobs के लिए MessageSocket वर्ग की आवश्यकता होती है और साथ ही आपको इसे लागू करने की आवश्यकता होती है और यह आपके डिवाइस से आने वाले सभी संदेशों को संसाधित करता है।
नीचे की रेखा, यह इतना आसान नहीं है और इसके लिए कुछ खोदने की आवश्यकता होती है आंतरिक और कोडिंग, लेकिन यह काम करता है, और सबसे महत्वपूर्ण बात - यह दुर्घटना नहीं करता है।