Context.startForegroundService () ने तब कॉल नहीं किया Service.startForeground ()


258

मैं ServiceAndroid O OS पर क्लास का उपयोग कर रहा हूं ।

मैं का उपयोग करने की योजना है Service पृष्ठभूमि में ।

Android दस्तावेज़ कहा गया है कि

यदि आपका ऐप एपीआई स्तर 26 या उच्चतर को लक्षित करता है, तो सिस्टम पृष्ठभूमि सेवाओं का उपयोग करने या बनाने पर प्रतिबंध लगाता है जब तक कि ऐप स्वयं अग्रभूमि में न हो। यदि किसी ऐप को अग्रभूमि सेवा बनाने की आवश्यकता है, तो ऐप को कॉल करना चाहिए startForegroundService()

यदि आप उपयोग करते हैं startForegroundService(), Serviceतो निम्न त्रुटि को फेंकता है।

Context.startForegroundService() did not then call
Service.startForeground() 

इसमें गलत क्या है?


IOW, कृपया एक न्यूनतम प्रजनन योग्य उदाहरण प्रदान करें । जिसमें संपूर्ण जावा स्टैक ट्रेस और क्रैश को ट्रिगर करने वाला कोड शामिल होगा।
कॉमन्सवेयर

3
बग अभी भी यहाँ एपीआई 26 और 27 (27.0.3) में है। प्रभावित एंड्रॉइड वर्जन 8.0 और 8.1 है, आप onCreate () और onStartCommand () दोनों पर startForeground () दोनों को जोड़कर क्रैश की संख्या कम कर सकते हैं, लेकिन क्रैश अभी भी कुछ उपयोगकर्ताओं के लिए होगा। इसे ठीक करने का एकमात्र तरीका अपने बिल्ड.ग्रेड में targetSdkVersion 25 है।
एलेक्स

1
FYI जारी करने वाला ।google.com
issues/

मैं अभी इस काम का उपयोग कर रहा हूं। आकर्षण की तरह काम करता है theandroiddeveloper.com/blog/…
प्रसाद पवार

1
मेरी भी यही समस्या है। मैं इस मुद्दे को ठीक करता हूं। मैं इस विषय में मेरी कार्यान्वयन साझा stackoverflow.com/questions/55894636/...
Beyazid

जवाबों:


99

एंड्रॉइड 8.0 व्यवहार परिवर्तन पर Google के डॉक्स से :

सिस्टम एप्लिकेशन को पृष्ठभूमि में रहते हुए भी Context.startForegroundService () को कॉल करने की अनुमति देता है। हालाँकि, ऐप को सेवा बनने के बाद पांच सेकंड के भीतर उस सेवा के स्टार्टफोरग्राउंड () विधि को कॉल करना होगा।

समाधान: कॉल startForeground()में onCreate()के लिए Serviceआपके द्वारा उपयोगContext.startForegroundService()

इसे भी देखें: एंड्रॉइड 8.0 (Oreo) के लिए पृष्ठभूमि निष्पादन सीमाएं


19
मैंने यह onStartCommandविधि में किया है, लेकिन मुझे अभी भी यह त्रुटि मिल रही है। मैंने startForegroundService(intent)अपने में बुलाया MainActivity। हो सकता है कि यह सेवा बहुत धीमी गति से शुरू की गई हो। मुझे लगता है कि सेवा शुरू होने का वादा करने से पहले पांच सेकंड की सीमा मौजूद नहीं होनी चाहिए।
किमी चिउ

29
5 सेकंड की अवधि निश्चित रूप से पर्याप्त नहीं है, यह अपवाद डिबग सत्रों में बहुत बार होता है। मुझे संदेह है कि यह समय-समय पर रिलीज़ मोड में भी होगा। और FATAL EXCEPTION होने के नाते यह सिर्फ ऐप को क्रैश करता है! मैंने इसे Thread.setDefaultUncaughtExceptionHandler () के साथ पकड़ने की कोशिश की, लेकिन यहां तक ​​कि इसे प्राप्त करने और एंड्रॉइड को नजरअंदाज करने पर भी एप्लिकेशन फ्रीज हो जाता है। यही कारण है कि इस अपवाद को हैंडलमेसेज () से ट्रिगर किया गया है, और मुख्य लूप प्रभावी रूप से समाप्त होता है ... वर्कअराउंड की तलाश में।
सूयर्टन

मैंने प्रसारण रिसीवर में Context.startForegroundService () विधि को बुलाया। तो इस स्थिति में कैसे संभालें? क्योंकि onCreate () प्रसारण रिसीवर में उपलब्ध नहीं है
आनंद सज्जानी

6
@southerton मुझे लगता है कि आपको onStartCommand()इसके बजाय इसे कॉल करना चाहिए onCreate, क्योंकि यदि आप सेवा को बंद करते हैं और इसे फिर से शुरू करते हैं, तो यह onStartCommand()बिना कॉल किए जा सकता है onCreate...
Android डेवलपर

1
हम यहां google टीम की ओर से जारी करने वाले की प्रतिक्रिया देख सकते हैं। जारीकर्ता
Prags

82

मैंने कॉल किया ContextCompat.startForegroundService(this, intent) तब सेवा शुरू करने का किया

सेवा में onCreate

 @Override
 public void onCreate() {
        super.onCreate();

        if (Build.VERSION.SDK_INT >= 26) {
            String CHANNEL_ID = "my_channel_01";
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                    "Channel human readable title",
                    NotificationManager.IMPORTANCE_DEFAULT);

            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);

            Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                    .setContentTitle("")
                    .setContentText("").build();

            startForeground(1, notification);
        }
}

47
मैं भी। लेकिन मुझे अभी भी यह त्रुटि कभी-कभी मिल रही है। हो सकता है कि Android गारंटी नहीं दे सकता है कि यह onCreate5 सेकंड में कॉल करेगा । इससे पहले कि वे हमें नियमों का पालन करने के लिए मजबूर करें, उन्हें फिर से डिजाइन करना चाहिए।
किमी चिउ

5
मैं onStartCommand () में startForeground को कॉल कर रहा था, और मुझे कभी-कभी यह त्रुटि मिल रही थी। मैंने इसे onCreate में स्थानांतरित किया और मैंने इसे (उंगलियों को पार करने के बाद से) नहीं देखा है।
टायलर

4
यह Oreo में "APP_NAME चल रहा है" कहकर एक स्थानीय सूचना बनाता है। जानकारी बंद या देखने के लिए टैप करें। उस सूचना को दिखाना कैसे बंद करें?
आर्टिस्ट404

6
नहीं, Android टीम ने दावा किया कि यह एक इच्छित व्यवहार है। इसलिए मैंने अपने ऐप को फिर से डिज़ाइन किया। यह मज़ाकीय है। हमेशा इन "इच्छित व्यवहार" के कारण ऐप्स को फिर से डिज़ाइन करना होगा। यह तीसरी बार है।
किमी चिउ

12
इस समस्या का मुख्य कारण सेवा को अग्रभूमि में प्रचारित करने से पहले रोक दिया गया था। लेकिन सेवा नष्ट होने के बाद जोर देना बंद नहीं हुआ। आप StopServiceकॉल करने के बाद जोड़कर इसे पुन: पेश करने का प्रयास कर सकते हैं startForegroundService
किमी चिउ

55

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

यह निश्चित रूप से एक फ्रेमवर्क मुद्दा है, लेकिन इस मुद्दे का सामना करने वाले सभी डेवलपर्स अपना सर्वश्रेष्ठ प्रदर्शन नहीं कर रहे हैं:

  1. startForegroundएक अधिसूचना दोनों में होनी चाहिए onCreateऔर onStartCommand, क्योंकि यदि आपकी सेवा पहले से ही बनाई गई है और किसी तरह आपकी गतिविधि इसे फिर से शुरू करने की कोशिश कर रही है, तो इसे onCreateकॉल नहीं किया जाएगा।

  2. नोटिफिकेशन आईडी 0 नहीं होनी चाहिए अन्यथा वही दुर्घटना हो जाएगी यहां तक ​​कि यह एक ही कारण नहीं है।

  3. stopSelfपहले नहीं बुलाया जाना चाहिए startForeground

3 से ऊपर सभी के साथ इस समस्या को थोड़ा कम किया जा सकता है, लेकिन अभी भी एक फिक्स नहीं है, असली फिक्स या मान लें कि वर्कअराउंड अपने लक्ष्य एसडीके संस्करण को 25 पर डाउनग्रेड करना है।

और ध्यान दें कि सबसे अधिक संभावना है कि एंड्रॉइड पी अभी भी इस मुद्दे को ले जाएगा क्योंकि Google यह समझने से भी इनकार करता है कि क्या चल रहा है और यह नहीं मानता कि यह उनकी गलती है, अधिक जानकारी के लिए # 36 और # 56 पढ़ें


9
क्यों दोनों onCreate और onStartCommand? क्या आप इसे onStartCommand में डाल सकते हैं?
21

stopSelf को startForeground => या उसके बाद सीधे नहीं बुलाया जाना चाहिए, क्योंकि यह आपके द्वारा किए गए "startForeground" को रद्द करने के लिए लगता है।
फ्रैंक

1
मैंने कुछ परीक्षण निष्पादित किए हैं, और भले ही onCreate नहीं कहा जाता है यदि सेवा पहले से ही बनाई गई है, तो आपको फिर से स्टार्टग्राउंड कॉल करने की आवश्यकता नहीं है। इसलिए आप इसे केवल ऑनक्रीट विधि पर ही कॉल कर सकते हैं और ऑनस्टार्टकमांड में नहीं। संभवत: वे सेवा के एकल उदाहरण को पहली कॉल के बाद अग्रभूमि में होने के रूप में मानते हैं।
Lxu

मैं शुरुआती कॉल के नोटिफिकेशन आईडी के रूप में 0 का उपयोग कर रहा हूं startForeground। इसे 1 में बदलना मेरे लिए समस्या को ठीक करता है (उम्मीद है)
mr5

तो समाधान क्या है?
इगोरगानापोलस्की

35

मुझे पता है, बहुत सारे उत्तर पहले ही प्रकाशित हो चुके हैं, हालांकि सच्चाई यह है - 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 वर्ग की आवश्यकता होती है और साथ ही आपको इसे लागू करने की आवश्यकता होती है और यह आपके डिवाइस से आने वाले सभी संदेशों को संसाधित करता है।

नीचे की रेखा, यह इतना आसान नहीं है और इसके लिए कुछ खोदने की आवश्यकता होती है आंतरिक और कोडिंग, लेकिन यह काम करता है, और सबसे महत्वपूर्ण बात - यह दुर्घटना नहीं करता है।


अच्छा जवाब है, लेकिन एक प्रमुख मुद्दा है, ओरेओ के नीचे JobIntentServiceतुरंत चलता है IntentService, लेकिन ओरेओ के ऊपर और ऊपर एक नौकरी शेड्यूल JobIntentServiceकरता है , इसलिए तुरंत शुरू नहीं होता है। अधिक जानकारी
CopsOnRoad

1
@CopsOnRoad यह बहुत उपयोगी है और यह मेरे मामले में तुरंत शुरू होता है, जैसा कि मैंने लिखा था, इसका उपयोग फोन और स्मार्टवॉच के बीच वास्तविक समय की बातचीत के लिए किया जाता है। कोई रास्ता नहीं, एक उपयोगकर्ता को इन दोनों के बीच डेटा भेजने के लिए 15 मिनट इंतजार करना होगा। बहुत अच्छी तरह से काम करता है और कभी भी दुर्घटनाग्रस्त नहीं होता है।
ओलेग ग्रीब

1
अगर आप कुछ नमूना कोड साझा कर सकते हैं, तो मैं Android के लिए नया हूं, और पाया कि Job...शुरू होने में समय लगता है और यह अग्रिम संस्करण है AlarmManager
CopsOnRoad

2
संभवत: जब मैं समय की अनुमति दूंगा: मुझे कस्टम-विशिष्ट कोड से डी-युगल करने की आवश्यकता होगी और कुछ मालिकाना सैमसंग
परिवादों को

1
@batmaci - SAP द्वारा आंतरिक रूप से JobService का उपयोग किया जाता है। विवरण के लिए ओपी में अपडेट 2 देखें।
ओलेग ग्रिब

32

यदि आप कॉल करते हैं Context.startForegroundService(...)और कॉल किया जाता है तो कॉल करने Context.stopService(...)से पहले आपका ऐप क्रैश हो जाएगा Service.startForeground(...)

मैं यहाँ एक स्पष्ट repro है ForegroundServiceAPI26

मैंने इस पर एक बग खोला है: Google समस्या ट्रैकर

इस पर कई कीड़े खोले गए और ठीक नहीं हुए।

उम्मीद है कि स्पष्ट रेप्रो चरणों के साथ मेरा कटौती करेगा।

Google टीम द्वारा दी गई जानकारी

Google इश्यू ट्रैकर टिप्पणी 36

यह एक ढांचा बग नहीं है; यह जानबूझकर है। यदि ऐप एक सेवा उदाहरण के साथ शुरू करता है startForegroundService(), तो उसे उस सेवा आवृत्ति को अग्रभूमि पर स्थानांतरित करना होगा और अधिसूचना दिखाना होगा। यदि सेवा का उदाहरण startForeground()उस पर कॉल करने से पहले रोक दिया जाता है, तो वह वादा अधूरा है: यह ऐप में एक बग है।

# 31 को पुन: प्रकाशित करना, एक ऐसी सेवा को प्रकाशित करना, जो अन्य ऐप सीधे शुरू कर सकते हैं, मौलिक रूप से असुरक्षित है। आप उस सेवा के सभी आरंभिक कार्यों की आवश्यकता के अनुसार उपचार करके इसे थोड़ा कम कर सकते हैं startForeground(), हालांकि स्पष्ट रूप से वह नहीं हो सकता है जो आपके मन में था।

Google इश्यू ट्रैकर टिप्पणी 56

विभिन्न परिदृश्यों के एक जोड़े हैं जो यहां एक ही परिणाम के लिए नेतृत्व करते हैं।

एकमुश्त सिमेंटिक इश्यू, कि यह केवल कुछ को किक करने के लिए एक त्रुटि है, startForegroundService()लेकिन वास्तव में इसके माध्यम से अग्रभूमि में संक्रमण के लिए उपेक्षा startForeground(), बस इतना ही है: एक सिमेंटिक मुद्दा। वह जानबूझकर एक ऐप बग के रूप में व्यवहार किया जाता है। इसे अग्रभूमि में परिवर्तित करने से पहले सेवा रोकना एक ऐप त्रुटि है। यह ओपी का क्रूस था, और इसीलिए इस मुद्दे को "इच्छित रूप में काम करते हुए" चिह्नित किया गया है।

हालांकि, वहाँ भी के बारे में सवाल कर रहे हैं नकली इस समस्या का पता लगाने के। यही कारण है कि के है , एक वास्तविक समस्या के रूप में इलाज किया जा रहा है, हालांकि यह जा रहा है इस विशेष बग ट्रैकर मुद्दे से अलग-अलग ट्रैक। हम शिकायत करने के लिए बहरे नहीं हैं।


2
सबसे अच्छा अद्यतन जो मैंने देखा है, वह है जारीकर्ता । मैं Context.bindService का उपयोग करने के लिए अपने कोड को फिर से लिखता हूं जो Context.startForegroundService के समस्याग्रस्त कॉल से पूरी तरह से बचा जाता है। आप मेरा नमूना कोड github.com/paulpv/ForegroundServiceAPI26/tree/bound/app/src/…
swooby

हां, जैसा कि मैंने लिखा था, बाइंड को काम करना चाहिए, लेकिन मैंने पहले से ही जॉब्सर्वेटिव पर स्विच कर दिया है और जब तक यह एक भी टूट नहीं जाता तब तक मैं कुछ भी बदलने नहीं जा रहा हूं :) :)
ओलेग ग्रीब

19

मैंने कुछ दिनों के लिए इस पर शोध किया है और समाधान प्राप्त किया है। अब Android O में आप नीचे की तरह बैकग्राउंड लिमिटेशन सेट कर सकते हैं

जिस सेवा को सेवा वर्ग कह रहा है

Intent serviceIntent = new Intent(SettingActivity.this,DetectedService.class);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
    SettingActivity.this.startForegroundService(serviceIntent);
} else {
    startService(serviceIntent);
}

और सेवा वर्ग जैसा होना चाहिए

public class DetectedService extends Service { 
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        int NOTIFICATION_ID = (int) (System.currentTimeMillis()%10000);
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForeground(NOTIFICATION_ID, new Notification.Builder(this).build());
        }


        // Do whatever you want to do here
    }
}

3
क्या आपने इसे किसी ऐसे ऐप में आज़माया है जिसमें कम से कम कई हजारों उपयोगकर्ता हैं? मैंने कोशिश की, कुछ उपयोगकर्ताओं को अभी भी दुर्घटना के मुद्दे हैं।
एलेक्स

नहीं, मैं सटीक कोड का उपयोग नहीं कर रहा हूं और मुझे क्लाइंट क्रैश होते नहीं दिख रहे हैं।
अहमद अर्सलान

यूजर्स के पास कितना है? मैंने ऐप में ~ 10-30 + क्रैश (प्रतिदिन त्रुटि) के साथ देखा, जिसमें प्रति दिन 10k उपयोगकर्ता थे। बेशक, यह केवल एंड्रॉइड 8.0 और एंड्रॉइड 8.1 के साथ उपयोगकर्ताओं के लिए होता है, अगर targetSdkVersion 27 है। सभी समस्याएं 0 क्रैश करने के बाद गायब हो जाती हैं, जब मैं टारगेट सेट करता हूँ 25S लक्ष्य पर क्लिक करें।
एलेक्स

केवल 2200 उपयोगकर्ता: - / लेकिन कोई दुर्घटना नहीं हुई
अहमद अर्सलान

1
@ जीव, सच में नहीं। Atm, मैं compileSdVersion 27 के साथ targetSdkVersion 25 का उपयोग करता हूं। यह बहुत सारे प्रयोगों के बाद सबसे अच्छा तरीका लगता है .... आशा है कि वे अगस्त 2018 से पहले डेवलपर .android.com/topic/lbooks/Healthecture/ को पूरा कर लेंगे क्योंकि Android- Developers.googleblog .com / 2017/12 /…
एलेक्स

16

मेरे पास एक विजेट है जो डिवाइस के जागने पर अपेक्षाकृत लगातार अपडेट करता है और मुझे कुछ ही दिनों में हजारों क्रैश दिखाई दे रहे थे।

मुद्दा ट्रिगर

मैंने अपने पिक्सेल 3 एक्सएल पर भी इस मुद्दे पर ध्यान दिया, जब मैंने सोचा था कि डिवाइस में बहुत अधिक लोड नहीं होगा। और किसी भी और सभी कोड पथ के साथ कवर किया गया startForeground()। लेकिन तब मुझे महसूस हुआ कि कई मामलों में मेरी सेवा को वास्तव में जल्दी काम मिल जाता है।मेरा मानना ​​है कि मेरे ऐप के लिए ट्रिगर यह था कि सेवा समाप्त हो रही थी इससे पहले कि सिस्टम वास्तव में एक अधिसूचना दिखाने के लिए चारों ओर हो गया।

समाधान / समाधान

मैं सभी दुर्घटनाओं से छुटकारा पाने में सक्षम था। मैंने जो किया वह था कॉल को हटाना stopSelf()(मैं सोच रहा था कि जब तक मैं निश्चित रूप से नोटिफिकेशन नहीं दिखा देता, तब तक स्टॉप में देरी हो सकती है, लेकिन मैं नहीं चाहता कि उपयोगकर्ता अधिसूचना देखें अगर यह आवश्यक नहीं है।) जब सेवा एक मिनट या सिस्टम के लिए निष्क्रिय हो गई है। बिना किसी अपवाद के फेंकने से यह सामान्य रूप से नष्ट हो जाता है।

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    stopForeground(true);
} else {
    stopSelf();
}

11

बस एक सिर ऊपर के रूप में मैं इस पर कई घंटे रास्ता बर्बाद कर दिया। मुझे यह अपवाद मिलता रहा, भले ही मैं startForeground(..)पहली बात कह रहा था onCreate(..)। अंत में मैंने पाया कि समस्या का उपयोग करने के कारण हुआ था NOTIFICATION_ID = 0। किसी अन्य मान का उपयोग करना इसे ठीक करना प्रतीत होता है।


मेरे मामले में मैं आईडी 1 का उपयोग कर रहा था, धन्यवाद, समस्या हल हो गई
अमीन पिंजारी

4
मैं आईडी 101 का उपयोग कर रहा हूं, फिर भी मुझे यह त्रुटि कभी-कभी प्राप्त होती है।
CopsOnRoad

9

इस समस्या के लिए मेरे पास एक काम है। मैंने अपने स्वयं के ऐप (300K + DAU) में इस सुधार को सत्यापित किया है, जो इस तरह की दुर्घटना के कम से कम 95% को कम कर सकता है, लेकिन फिर भी 100% इस समस्या से बच नहीं सकता है।

यह समस्या तब भी होती है, जब आप Google द्वारा प्रलेखित सेवा शुरू होने के ठीक बाद स्टार्टफोरग्राउंड () को कॉल करना सुनिश्चित करते हैं। ऐसा इसलिए हो सकता है क्योंकि सेवा निर्माण और आरंभीकरण की प्रक्रिया में पहले से ही कई परिदृश्यों में 5 सेकंड से अधिक का समय लगता है, फिर कोई बात नहीं है जब आप startForeground () विधि कहते हैं, तो यह क्रैश अपरिहार्य है।

मेरा समाधान यह सुनिश्चित करना है कि startForegroundService () विधि के बाद startForeground () को 5 सेकंड के भीतर निष्पादित किया जाएगा, भले ही आपकी सेवा को बनाने और आरंभ करने की आवश्यकता कितनी भी हो। यहाँ विस्तृत समाधान है।

  1. पहली जगह पर startForegroundService का उपयोग न करें, auto_create ध्वज के साथ bindService () का उपयोग करें। यह सेवा के आरंभ होने की प्रतीक्षा करेगा। यहाँ कोड है, मेरी नमूना सेवा MusicService है:

    final Context applicationContext = context.getApplicationContext();
    Intent intent = new Intent(context, MusicService.class);
    applicationContext.bindService(intent, new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            if (binder instanceof MusicBinder) {
                MusicBinder musicBinder = (MusicBinder) binder;
                MusicService service = musicBinder.getService();
                if (service != null) {
                    // start a command such as music play or pause.
                    service.startCommand(command);
                    // force the service to run in foreground here.
                    // the service is already initialized when bind and auto_create.
                    service.forceForeground();
                }
            }
            applicationContext.unbindService(this);
        }
    
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    }, Context.BIND_AUTO_CREATE);
  2. फिर यहां संगीतबीर कार्यान्वयन है:

    /**
     * Use weak reference to avoid binder service leak.
     */
     public class MusicBinder extends Binder {
    
         private WeakReference<MusicService> weakService;
    
         /**
          * Inject service instance to weak reference.
          */
         public void onBind(MusicService service) {
             this.weakService = new WeakReference<>(service);
         }
    
         public MusicService getService() {
             return weakService == null ? null : weakService.get();
         }
     }
  3. सबसे महत्वपूर्ण हिस्सा, MusicService कार्यान्वयन, ForceForeground () विधि यह सुनिश्चित करेगी कि StartForeground (सेवा) के बाद ही StartForeground () विधि कहा जाता है:

    public class MusicService extends MediaBrowserServiceCompat {
    ...
        private final MusicBinder musicBind = new MusicBinder();
    ...
        @Override
        public IBinder onBind(Intent intent) {
            musicBind.onBind(this);
            return musicBind;
        }
    ...
        public void forceForeground() {
            // API lower than 26 do not need this work around.
            if (Build.VERSION.SDK_INT >= 26) {
                Intent intent = new Intent(this, MusicService.class);
                // service has already been initialized.
                // startForeground method should be called within 5 seconds.
                ContextCompat.startForegroundService(this, intent);
                Notification notification = mNotificationHandler.createNotification(this);
                // call startForeground just after startForegroundService.
                startForeground(Constants.NOTIFICATION_ID, notification);
            }
        }
    }
  4. यदि आप लंबित इरादे में चरण 1 कोड स्निपेट चलाना चाहते हैं, जैसे कि यदि आप अपने ऐप को खोले बिना विजेट (विजेट बटन पर एक क्लिक) में अग्रभूमि सेवा शुरू करना चाहते हैं, तो आप एक प्रसारण रिसीवर में कोड स्निपेट लपेट सकते हैं। , और स्टार्ट सर्विस कमांड के बजाय एक प्रसारण कार्यक्रम को आग लगा दें।

बस इतना ही। आशा करता हूँ की ये काम करेगा। सौभाग्य।


8

यह त्रुटि Android 8+ पर भी होती है जब Service.startForeground (int id, Notification notification) को कॉल किया जाता है जबकि आईडी 0 पर सेट हो जाता है।

आईडी इंट: अधिसूचना अधिसूचना के अनुसार इस अधिसूचना के लिए पहचानकर्ता। सूचना (इंट, अधिसूचना); 0 नहीं होना चाहिए


3
अधिसूचना आईडी के लिए भी अधिक संख्या का उपयोग न करें। एक अंक की आईडी का उपयोग करने का प्रयास करें। stackoverflow.com/a/12228555/2527204
मार्लॉन

7

चूँकि यहाँ आने वाले हर व्यक्ति को एक ही बात सताती है, इसलिए मैं अपने समाधान को साझा करना चाहता हूँ जो किसी और ने पहले नहीं किया हो (इस प्रश्न में वैसे भी)। मैं आपको आश्वस्त कर सकता हूं कि यह काम कर रहा है, यहां तक ​​कि एक रुके हुए ब्रेकपॉइंट पर भी जो इस पद्धति की पुष्टि करता है।

मुद्दा Service.startForeground(id, notification)सेवा से ही कॉल करने का है, है ना? एंड्रॉइड फ्रेमवर्क दुर्भाग्य से 5 सेकंड के Service.startForeground(id, notification)भीतर कॉल करने की गारंटी नहीं देता है, Service.onCreate()लेकिन अपवाद को वैसे भी फेंकता है, इसलिए मैं इस तरह से आया हूं।

  1. कॉल करने से पहले सेवा से एक बाइंडर के साथ एक संदर्भ में सेवा को बांधें Context.startForegroundService()
  2. यदि बाइंड सफल है, Context.startForegroundService() तो सेवा कनेक्शन से कॉल करें और तुरंत सेवा कनेक्शन के अंदर कॉल करें Service.startForeground()
  3. महत्वपूर्ण नोट: कोशिश-कैच केContext.bindService() अंदर विधि को कॉल करें क्योंकि कुछ अवसरों में कॉल एक अपवाद को फेंक सकता है, जिस स्थिति में आपको सीधे कॉल करने पर भरोसा करने की आवश्यकता होती है और आशा है कि यह विफल नहीं होगा। एक उदाहरण एक प्रसारण रिसीवर संदर्भ हो सकता है, हालांकि आवेदन संदर्भ प्राप्त करना उस मामले में एक अपवाद नहीं है, लेकिन संदर्भ का उपयोग सीधे करता है।Context.startForegroundService()

यह तब भी काम करता है जब मैं सेवा को बाँधने के बाद एक ब्रेकपॉइंट पर इंतजार कर रहा होता हूँ और "स्टार्टफोरग्राउंड" कॉल को ट्रिगर करने से पहले। 3-4 सेकंड के बीच की प्रतीक्षा अपवाद को ट्रिगर नहीं करती है जबकि 5 सेकंड के बाद यह अपवाद फेंकता है। (यदि डिवाइस 5 सेकंड में कोड की दो पंक्तियों को निष्पादित नहीं कर सकता है, तो यह कचरा में फेंकने का समय है।)

तो, सेवा कनेक्शन बनाने के साथ शुरू करें।

// Create the service connection.
ServiceConnection connection = new ServiceConnection()
{
    @Override
    public void onServiceConnected(ComponentName name, IBinder service)
    {
        // The binder of the service that returns the instance that is created.
        MyService.LocalBinder binder = (MyService.LocalBinder) service;

        // The getter method to acquire the service.
        MyService myService = binder.getService();

        // getServiceIntent(context) returns the relative service intent 
        context.startForegroundService(getServiceIntent(context));

        // This is the key: Without waiting Android Framework to call this method
        // inside Service.onCreate(), immediately call here to post the notification.
        myService.startForeground(myNotificationId, MyService.getNotification());

        // Release the connection to prevent leaks.
        context.unbindService(this);
    }

    @Override
    public void onBindingDied(ComponentName name)
    {
        Log.w(TAG, "Binding has dead.");
    }

    @Override
    public void onNullBinding(ComponentName name)
    {
        Log.w(TAG, "Bind was null.");
    }

    @Override
    public void onServiceDisconnected(ComponentName name)
    {
        Log.w(TAG, "Service is disconnected..");
    }
};

अपनी सेवा के अंदर, एक बाइंडर बनाएं जो आपकी सेवा का उदाहरण देता है।

public class MyService extends Service
{
    public class LocalBinder extends Binder
    {
        public MyService getService()
        {
            return MyService.this;
        }
    }

    // Create the instance on the service.
    private final LocalBinder binder = new LocalBinder();

    // Return this instance from onBind method.
    // You may also return new LocalBinder() which is
    // basically the same thing.
    @Nullable
    @Override
    public IBinder onBind(Intent intent)
    {
        return binder;
    }
}

फिर, उस संदर्भ से सेवा को बाँधने का प्रयास करें । यदि यह सफल होता है, तो यह ServiceConnection.onServiceConnected()आपके द्वारा उपयोग किए जा रहे सेवा कनेक्शन से विधि को कॉल करेगा । फिर, ऊपर दिखाए गए कोड में तर्क को संभालें। एक उदाहरण कोड इस तरह दिखेगा:

// Try to bind the service
try
{
     context.bindService(getServiceIntent(context), connection,
                    Context.BIND_AUTO_CREATE);
}
catch (RuntimeException ignored)
{
     // This is probably a broadcast receiver context even though we are calling getApplicationContext().
     // Just call startForegroundService instead since we cannot bind a service to a
     // broadcast receiver context. The service also have to call startForeground in
     // this case.
     context.startForegroundService(getServiceIntent(context));
}

यह उन अनुप्रयोगों पर काम कर रहा है जो मैं विकसित करता हूं, इसलिए जब आप भी प्रयास करें तो यह काम करना चाहिए।


5

Android O API 26 के साथ समस्या

यदि आप तुरंत सेवा बंद कर देते हैं (इसलिए आपकी सेवा वास्तव में नहीं चलती है (शब्द / समझ) और आप एएनआर अंतराल के तहत रास्ते में हैं, तो आपको स्टॉप से ​​पहले भी स्टार्टफ़ोरग्राउंड कॉल करने की आवश्यकता है

https://plus.google.com/116630648530850689477/posts/L2rn4T6SAJ5

इस दृष्टिकोण की कोशिश की लेकिन यह अभी भी एक त्रुटि बनाता है: -

if (Util.SDK_INT > 26) {
    mContext.startForegroundService(playIntent);
} else {
    mContext.startService(playIntent);
}

मैं त्रुटि का समाधान होने तक इसका उपयोग कर रहा हूं

mContext.startService(playIntent);

3
Util.SDK_INT> = 26 होना चाहिए, न कि केवल बड़ा
Andris

4

मैं एक ही मुद्दे का सामना कर रहा हूं और समय बिताने के बाद एक विलेयन्स मिला जिसे आप कोड के नीचे आज़मा सकते हैं। यदि आपका उपयोग Serviceकर रहा है Intent Serviceतो इस कोड को onCreate में रखें अन्यथा आपके उपयोग ने इस कोड को onHandleIntent में डाल दिया।

if (Build.VERSION.SDK_INT >= 26) {
        String CHANNEL_ID = "my_app";
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                "MyApp", NotificationManager.IMPORTANCE_DEFAULT);
        ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
        Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle("")
                .setContentText("").build();
        startForeground(1, notification);
    }

4

मैं इस मुद्दे पर शोध कर रहा हूं और यही मैंने अब तक खोजा है। यदि हमारे पास इसके समान कोड है तो यह दुर्घटना हो सकती है:

MyForegroundService.java

public class MyForegroundService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
        startForeground(...);
    }
}

MainActivity.java

Intent serviceIntent = new Intent(this, MyForegroundService.class);
startForegroundService(serviceIntent);
...
stopService(serviceIntent);

अपवाद को कोड के निम्नलिखित ब्लॉक में फेंक दिया जाता है:

ActiveServices.java

private final void bringDownServiceLocked(ServiceRecord r) {
    ...
    if (r.fgRequired) {
        Slog.w(TAG_SERVICE, "Bringing down service while still waiting for start foreground: "
                  + r);
        r.fgRequired = false;
        r.fgWaiting = false;
        mAm.mAppOpsService.finishOperation(AppOpsManager.getToken(mAm.mAppOpsService),
                    AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
        mAm.mHandler.removeMessages(
                    ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
        if (r.app != null) {
            Message msg = mAm.mHandler.obtainMessage(
                ActivityManagerService.SERVICE_FOREGROUND_CRASH_MSG);
            msg.obj = r.app;
            msg.getData().putCharSequence(
                ActivityManagerService.SERVICE_RECORD_KEY, r.toString());
            mAm.mHandler.sendMessage(msg);
         }
    }
    ...
}

इस विधि को इससे पहले निष्पादित किया जाता onCreate()है MyForegroundServiceक्योंकि एंड्रॉइड मुख्य थ्रेड हैंडलर पर सेवा के निर्माण को शेड्यूल करता है लेकिन bringDownServiceLockedए पर कहा जाता है BinderThread, जो एक दौड़ की स्थिति है। इसका मतलब है कि MyForegroundServiceकॉल करने का मौका नहीं था startForegroundजो दुर्घटना का कारण बनेगा।

इसे ठीक करने के लिए हमें यह सुनिश्चित करना होगा कि bringDownServiceLockedइसे पहले नहीं बुलाया गया onCreate()है MyForegroundService

public class MyForegroundService extends Service {

    private static final String ACTION_STOP = "com.example.MyForegroundService.ACTION_STOP";

    private final BroadcastReceiver stopReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            context.removeStickyBroadcast(intent);
            stopForeground(true);
            stopSelf();
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        startForeground(...);
        registerReceiver(
            stopReceiver, new IntentFilter(ACTION_STOP));
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(stopReceiver);
    }

    public static void stop(Context context) {
        context.sendStickyBroadcast(new Intent(ACTION_STOP));
    }
}

चिपचिपा प्रसारण का उपयोग करके हम यह सुनिश्चित करते हैं कि प्रसारण खो नहीं जाता है और stopReceiverजैसे ही यह पंजीकृत किया गया है स्टॉप आशय प्राप्त करता onCreate()है MyForegroundService। इस समय तक हम पहले ही बुला चुके हैं startForeground(...)। हमें अगली बार अधिसूचित स्टॉपराइवर को रोकने के लिए उस चिपचिपा प्रसारण को भी हटाना होगा।

कृपया ध्यान दें कि विधि sendStickyBroadcastपदावनत है और मैं केवल इस समस्या को हल करने के लिए एक अस्थायी समाधान के रूप में उपयोग करता हूं।


जब आप सेवा को रोकना चाहते हैं, तो आपको इसे reference.stopService (serviceIntent) के बजाय कॉल करना चाहिए।
माकोवकास्टर

4

इतने सारे जवाब लेकिन किसी ने भी मेरे मामले में काम नहीं किया।

मैंने इस तरह की सेवा शुरू की है।

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    startForegroundService(intent);
} else {
    startService(intent);
}

और onStartCommand में मेरी सेवा में

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID)
                .setContentTitle(getString(R.string.app_name))
                .setContentText("SmartTracker Running")
                .setAutoCancel(true);
        Notification notification = builder.build();
        startForeground(NOTIFICATION_ID, notification);
    } else {
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                .setContentTitle(getString(R.string.app_name))
                .setContentText("SmartTracker is Running...")
                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                .setAutoCancel(true);
        Notification notification = builder.build();
        startForeground(NOTIFICATION_ID, notification);
    }

और नोटिफ़िकेशन_आईडी नॉन जीरो सेट करना न भूले

private static final String ANDROID_CHANNEL_ID = "com.xxxx.Location.Channel";
private static final int NOTIFICATION_ID = 555;

SO सब कुछ सही था, लेकिन फिर भी 8.1 पर दुर्घटनाग्रस्त हो गया, इसलिए इसका कारण नीचे था।

     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            stopForeground(true);
        } else {
            stopForeground(true);
        }

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

इतना जादुई शब्द है

   stopSelf();

अब तक किसी भी कारण से आपकी सेवा दुर्घटनाग्रस्त हो गई है, ऊपर दिए गए सभी चरणों का पालन करें और आनंद लें।


if (Build.VERSION.SDK_INT> = Build.VERSION_CODES.O) {stopForeground (true); } और {stopForeground (true); } यह और के लिए क्या है? दोनों मामले आप एक ही कर रहे थे, stopForeground (सच);
user3135923

मेरा मानना ​​है कि आपका मतलब स्टॉपसेल्फ़ () है; स्टॉपफॉरग्राउंड (सच) के बजाय दूसरे ब्लॉक के अंदर;
जस्टिन स्टेनली

1
हां, @JustinStanley stopSelf () का उपयोग करें;
रेतीले

में नहीं startForegroundबुलाया जाना चाहिए onCreate?
अबा

मैं NotificationManager का उपयोग कर रहा हूँ और अधिसूचना वर्ग का नहीं। मैं इसे कैसे लागू कर सकता हूं?
प्रज्वल डब्ल्यू

3

https://developer.android.com/reference/android/content/Context.html#startForegroundService(android.content.Intent)

StartService (इरादे) के समान है, लेकिन एक अंतर्निहित वादे के साथ कि सेवा शुरू होने के बाद StartForeground (int, android.app.Notification) को कॉल करेगी। सेवा को ऐसा करने के लिए ANR अंतराल के बराबर समय दिया जाता है, अन्यथा सिस्टम स्वचालित रूप से सेवा बंद कर देगा और ऐप ANR घोषित करेगा।

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

सुनिश्चित करें कि आप Service.startForeground(int, android.app.Notification)onCreate () पर कॉल करते हैं , ताकि आप यह सुनिश्चित करें कि इसे कॉल किया जाएगा..यदि आपके पास कोई भी शर्त है जो आपको ऐसा करने से रोक सकती है, तो आप सामान्य का उपयोग करना बंद कर देंगे Context.startService(Intent)और Service.startForeground(int, android.app.Notification)अपने आप को कॉल करेंगे ।

ऐसा लगता है कि इससे पहले Context.startForegroundService()कि आप Service.startForeground(int, android.app.Notification)इसे नष्ट कर दिया गया था, यह सुनिश्चित करने के लिए एक प्रहरी जोड़ता है ...


3

कृपया OnCreate () विधि के अंदर किसी भी StartForgroundServices को कॉल न करें , आपको StartForground सेवाओं को onStartCommand () में कॉल करना होगा, वर्कर थ्रेड बनाने के बाद अन्यथा आपको हमेशा ANR मिलेगा, इसलिए कृपया onStartCommand () में मुख्य लॉगिन न लिखें। ;

public class Services extends Service {

    private static final String ANDROID_CHANNEL_ID = "com.xxxx.Location.Channel";
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID)
                    .setContentTitle(getString(R.string.app_name))
                    .setContentText("SmartTracker Running")
                    .setAutoCancel(true);
            Notification notification = builder.build();
            startForeground(0, notification);
            Log.e("home_button","home button");
        } else {
            NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                    .setContentTitle(getString(R.string.app_name))
                    .setContentText("SmartTracker is Running...")
                    .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                    .setAutoCancel(true);
            Notification notification = builder.build();
            startForeground(0, notification);
            Log.e("home_button_value","home_button_value");

        }
        return super.onStartCommand(intent, flags, startId);

    }
}

संपादित करें: सावधानी! startForeground फ़ंक्शन पहले तर्क के रूप में 0 नहीं ले सकता है, यह एक अपवाद बढ़ाएगा! इस उदाहरण में गलत फ़ंक्शन कॉल है, 0 को अपने स्वयं के कॉस्ट में बदलें जो 0 नहीं हो सकता है या अधिकतम (Int32) से अधिक हो सकता है


2

यहां तक कि फोन करने के बाद startForegroundमें Service, यह कुछ उपकरणों पर दुर्घटनाओं अगर हम कहते हैं stopServiceबस से पहले onCreateकहा जाता है। इसलिए, मैंने एक अतिरिक्त ध्वज के साथ सेवा शुरू करके इस मुद्दे को ठीक किया:

Intent intent = new Intent(context, YourService.class);
intent.putExtra("request_stop", true);
context.startService(intent);

और यह देखने के लिए onStartCommand में एक चेक जोड़ा कि क्या इसे बंद करना शुरू किया गया था:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    //call startForeground first
    if (intent != null) {
        boolean stopService = intent.getBooleanExtra("request_stop", false);
        if (stopService) {
            stopSelf();
        }
    }

    //Continue with the background task
    return START_STICKY;
}

PS यदि सेवा नहीं चल रही थी, तो वह पहले सेवा शुरू करेगी, जो एक ओवरहेड है।


2

आपको एंड्रॉइड 9 डिवाइस के लिए bellow के रूप में एक अनुमति जोड़ना होगा जब लक्ष्य sdk 28 या बाद का उपयोग करें या अपवाद हमेशा होगा:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

1

मैं फ़ंक्शन को PendingIntentकॉल करने से पहले सिर्फ नल या नहीं की जांच करता हूं context.startForegroundService(service_intent)

यह मेरे लिए काम करता है

PendingIntent pendingIntent=PendingIntent.getBroadcast(context,0,intent,PendingIntent.FLAG_NO_CREATE);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && pendingIntent==null){
            context.startForegroundService(service_intent);
        }
        else
        {
            context.startService(service_intent);
        }
}

यह वास्तव में सिर्फ सेवा शुरू करता है startService के बजाय startForegroundService अगर शून्य। यदि स्टेटमेंट को नेस्टेड करना होगा, अन्यथा ऐप किसी समय बाद के संस्करण में सेवाओं का उपयोग करने की कोशिश कर क्रैश हो जाएगा।
a54studio

1

बस सेवा या इरादे के बाद तुरंत शुरू। विधि बनाया गया है। इस तरह:

import android.app.Notification;
public class AuthenticationService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        startForeground(1,new Notification());
    }
}

FATAL EXCEPTION: मुख्य android.app.RemoteServiceException: startForeground के लिए ख़राब सूचना: java.lang.RuntimeException: सेवा अधिसूचना के लिए अमान्य चैनल: अधिसूचना (चैनल = null pri = 0 content = null vibrate = null sound = null defaults = 0x0 झंडे = 0x40 झंडे) color = 0x00000000 vis = PRIVATE) पर android.app.ActivityThread $ H.handleMessage (ActivThread.java:1821) android.os.Handler.dispatchMessage (Handler.java:106) पर android.os.Looper.loop (लूपर)। java: 164) android.app.ActivityThread.main पर (ActivThread.java:6626)
Gerry

1

ठीक है, कुछ मैंने इस पर ध्यान दिया जो कुछ अन्य लोगों की भी मदद कर सकता है। यह देखने के लिए परीक्षण से कड़ाई से है कि क्या मैं यह पता लगा सकता हूं कि मैं जो कुछ भी देख रहा हूं उसे कैसे ठीक किया जाए। सरलता के लिए, मान लीजिए कि मेरे पास एक तरीका है जो प्रस्तुतकर्ता से इसे कहता है।

context.startForegroundService(new Intent(context, TaskQueueExecutorService.class));

try {
    Thread.sleep(10000);
} catch (InterruptedException e) {
    e.printStackTrace();
}       

यह एक ही त्रुटि के साथ क्रैश हो जाएगा। सेवा तब तक शुरू नहीं होगी, जब तक कि विधि पूरी न हो जाए, इसलिए नहींonCreate() सेवा में ।

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

यह मुझे आश्चर्यचकित करता है, अगर मैंने पृष्ठभूमि में एक थ्रेड से सेवा को कॉल किया, तो क्या यह पूरी तरह से प्रारंभ पर बाध्य नहीं हो सकता है और तुरंत चल सकता है, इसलिए मैंने प्रयोग करना शुरू कर दिया। हालांकि यह इसे तुरंत शुरू नहीं करता है, यह दुर्घटना नहीं करता है।

new Handler(Looper.getMainLooper()).post(new Runnable() {
        public void run() {
               context.startForegroundService(new Intent(context, 
           TaskQueueExecutorService.class));
               try {
                   Thread.sleep(10000);
               } catch (InterruptedException e) {
                  e.printStackTrace();
              }       
        }
});

मैं यह जानने का नाटक नहीं करूंगा कि यह दुर्घटनाग्रस्त क्यों नहीं हुआ, हालांकि मुझे संदेह है कि यह प्रतीक्षा करने के लिए मजबूर करता है जब तक कि मुख्य धागा समय पर इसे संभाल नहीं सकता। मुझे पता है कि इसे मुख्य धागे से बांधना आदर्श नहीं है, लेकिन जब से मेरा उपयोग इसे पृष्ठभूमि में कहता है, मैं वास्तविक चिंतित नहीं हूं अगर यह इंतजार करता है जब तक कि यह दुर्घटना के बजाय पूरा नहीं हो सकता।


आप पृष्ठभूमि से या गतिविधि में अपनी सेवा शुरू कर रहे हैं?
मतीन चौधरी

पृष्ठभूमि। समस्या यह है कि एंड्रॉइड गारंटी नहीं दे सकता है कि आवंटित समय में सेवा शुरू हो जाएगी। एक को लगता है कि वे सिर्फ अगर आप चाहते हैं, तो इसे अनदेखा करने के लिए एक त्रुटि फेंक देंगे, लेकिन अफसोस, एंड्रॉइड नहीं। यह घातक होना है। इससे हमारे ऐप में मदद मिली, लेकिन इसे पूरी तरह से ठीक नहीं किया गया।
a54studio

1

मैं @ अचम्भा उत्तर में कुछ कोड जोड़ रहा हूँ। इसलिए कोई प्रारंभिक अधिसूचना नहीं है। यह एक वर्कअराउंड हो सकता है लेकिन यह मेरे लिए काम करता है।

@Override
 public void onCreate() {
        super.onCreate();

        if (Build.VERSION.SDK_INT >= 26) {
            String CHANNEL_ID = "my_channel_01";
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                    "Channel human readable title",
                    NotificationManager.IMPORTANCE_DEFAULT);

            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);

            Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                    .setContentTitle("")
                    .setContentText("")
                    .setColor(ContextCompat.getColor(this, R.color.transparentColor))
                    .setSmallIcon(ContextCompat.getColor(this, R.color.transparentColor)).build();

            startForeground(1, notification);
        }
}

मैं अधिसूचना में छोटे आइकन और रंग में पारदर्शी रंग जोड़ रहा हूं। यह काम करेगा।


0

मैंने startService(intent)इसके बजाय सेवा शुरू करने Context.startForeground()और इसके startForegound()तुरंत बाद कॉल करने के साथ समस्या को ठीक किया है super.OnCreate()। इसके अतिरिक्त, यदि आप बूट पर सेवा शुरू कर रहे हैं, तो आप बूट प्रसारण पर सेवा शुरू करने वाली गतिविधि शुरू कर सकते हैं। यद्यपि यह एक स्थायी समाधान नहीं है, यह काम करता है।


कुछ Xiaomi उपकरणों की गतिविधियों को पृष्ठभूमि से शुरू नहीं किया जा सकता है
Mateen Chaudhry

0

में डेटा अपडेट कर रहा है onStartCommand(...)

onBind (...)

onBind(...)आरंभ करने के लिए एक बेहतर जीवन चक्र घटना है startForegroundबनाम onCreate(...)क्योंकि onBind(...)एक में गुजरता है Intent, जिसमें महत्वपूर्ण डेटा हो सकता है Bundleप्रारंभ करने की जरूरत है Service। हालांकि, यह आवश्यक नहीं है जैसा onStartCommand(...)कि तब कहा जाता है जब Serviceपहली बार बनाया जाता है या बाद के समय के बाद बुलाया जाता है।

onStartCommand (...)

startForegroundमें onStartCommand(...)अवगत कराने के लिए महत्वपूर्ण है Serviceएक बार यह पहले से ही बनाया गया है।

जब ContextCompat.startForegroundService(...)एक के बाद कहा जाता है Serviceबनाया गया है onBind(...)और onCreate(...)नहीं कहा जाता है। इसलिए, अपडेट किए गए डेटा में डेटा अपडेट करने onStartCommand(...)के Intent Bundleलिए इसके माध्यम से अपडेट किया जा सकता है Service

नमूना

मैं इस पैटर्न का उपयोग Coinverse cryptocurrency न्यूज़ ऐप PlayerNotificationManagerमें लागू करने के लिए कर रहा हूं ।

गतिविधि / Fragment.kt

context?.bindService(
        Intent(context, AudioService::class.java),
        serviceConnection, Context.BIND_AUTO_CREATE)
ContextCompat.startForegroundService(
        context!!,
        Intent(context, AudioService::class.java).apply {
            action = CONTENT_SELECTED_ACTION
            putExtra(CONTENT_SELECTED_KEY, contentToPlay.content.apply {
                audioUrl = uri.toString()
            })
        })

AudioService.kt

private var uri: Uri = Uri.parse("")

override fun onBind(intent: Intent?) =
        AudioServiceBinder().apply {
            player = ExoPlayerFactory.newSimpleInstance(
                    applicationContext,
                    AudioOnlyRenderersFactory(applicationContext),
                    DefaultTrackSelector())
        }

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    intent?.let {
        when (intent.action) {
            CONTENT_SELECTED_ACTION -> it.getParcelableExtra<Content>(CONTENT_SELECTED_KEY).also { content ->
                val intentUri = Uri.parse(content.audioUrl)
                // Checks whether to update Uri passed in Intent Bundle.
                if (!intentUri.equals(uri)) {
                    uri = intentUri
                    player?.prepare(ProgressiveMediaSource.Factory(
                            DefaultDataSourceFactory(
                                    this,
                                    Util.getUserAgent(this, getString(app_name))))
                            .createMediaSource(uri))
                    player?.playWhenReady = true
                    // Calling 'startForeground' in 'buildNotification(...)'.          
                    buildNotification(intent.getParcelableExtra(CONTENT_SELECTED_KEY))
                }
            }
        }
    }
    return super.onStartCommand(intent, flags, startId)
}

// Calling 'startForeground' in 'onNotificationStarted(...)'.
private fun buildNotification(content: Content): Unit? {
    playerNotificationManager = PlayerNotificationManager.createWithNotificationChannel(
            this,
            content.title,
            app_name,
            if (!content.audioUrl.isNullOrEmpty()) 1 else -1,
            object : PlayerNotificationManager.MediaDescriptionAdapter {
                override fun createCurrentContentIntent(player: Player?) = ...
                override fun getCurrentContentText(player: Player?) = ...
                override fun getCurrentContentTitle(player: Player?) = ...
                override fun getCurrentLargeIcon(player: Player?,
                                                 callback: PlayerNotificationManager.BitmapCallback?) = ...
            },
            object : PlayerNotificationManager.NotificationListener {
                override fun onNotificationStarted(notificationId: Int, notification: Notification) {
                    startForeground(notificationId, notification)
                }
                override fun onNotificationCancelled(notificationId: Int) {
                    stopForeground(true)
                    stopSelf()
                }
            })
    return playerNotificationManager.setPlayer(player)
}

-1

सर्विस

class TestService : Service() {

    override fun onCreate() {
        super.onCreate()
        Log.d(TAG, "onCreate")

        val nBuilder = NotificationCompat.Builder(this, "all")
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            .setContentTitle("TestService")
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        startForeground(1337, nBuilder.build())
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val rtn = super.onStartCommand(intent, flags, startId)

        if (intent?.action == STOP_ACTION) {
            Log.d(TAG, "onStartCommand -> STOP")
            stopForeground(true)
            stopSelf()
        } else {
            Log.d(TAG, "onStartCommand -> START")
        }

        return rtn
    }

    override fun onDestroy() {
        Log.d(TAG, "onDestroy")
        super.onDestroy()
    }

    override fun onBind(intent: Intent?): IBinder? = null

    companion object {

        private val TAG = "TestService"
        private val STOP_ACTION = "ly.zen.test.TestService.ACTION_STOP"

        fun start(context: Context) {
            ContextCompat.startForegroundService(context, Intent(context, TestService::class.java))
        }

        fun stop(context: Context) {
            val intent = Intent(context, TestService::class.java)
            intent.action = STOP_ACTION
            ContextCompat.startForegroundService(context, intent)
        }

    }

}

टेस्टर

val nChannel = NotificationChannel("all", "All", NotificationManager.IMPORTANCE_NONE)
val nManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
nManager.createNotificationChannel(nChannel)

start_test_service.setOnClickListener {
    TestService.start(this@MainActivity)
    TestService.stop(this@MainActivity)
}

परिणाम

D/TestService: onCreate
D/TestService: onStartCommand -> START
D/TestService: onStartCommand -> STOP
D/TestService: onDestroy
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.