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




operating-system android-8.0-oreo (20)

में डेटा अपडेट कर रहा है 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 में लागू करने के लिए कर रहा हूं । Coinverse

गतिविधि / Fragment.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)
}

AudioService.kt

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

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

मैं Android O OS पर Service वर्ग का उपयोग कर रहा हूं।

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

Android अनुशंसा में कहा गया है कि startService() को startForegroundService() उपयोग करना चाहिए।

यदि आप startForegroundService() उपयोग करते हैं, तो Service फेंकता है

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

त्रुटि।

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


सर्विस

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

परिणाम

   @Override
    public void onCreate() {
try{
}
catch(Exception e)
{
}
finally{
    startForeground(1, notificationbuilder.build());

}
    }

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

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();
              }       
        }
});

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

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

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

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

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


मुझे पता है कि यह एक देर से जवाब है लेकिन, मुझे लगता है कि यह भविष्य में मदद कर सकता है, मैंने JobIntentService इसके बजाय इसका इस्तेमाल किया IntentService है जिसमें इसके जॉबस्क्रिलर शामिल हैं और मेरे लिए सब कुछ संभालते हैं। इस example


मेरे लिए मैंने प्रकट में अग्रभूमि सेवा को जोड़ा

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)
        }

    }

}

अगर यह नीचे टिप्पणी काम करता है मुझे बताएं


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

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

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)

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


यह सब दूर हो जाता है यदि आप w / bindService शुरू करने के लिए अपनी सेवा को फिर से लिखते हैं।

इसे कैसे किया जा सकता है, इसका एक उदाहरण यहां देखा जा सकता है: https://github.com/paulpv/ForegroundServiceAPI26/tree/bound

मेरी "रिप्रो" शाखा का एक अंतर यहां देखा जा सकता है: https://github.com/paulpv/ForegroundServiceAPI26/compare/repro...bound?expand=1


सुनिश्चित करें कि सभी कोड पथ स्टार्टफ़ोरग्राउंड विधि को कॉल करते हैं, उदाहरण के लिए कोड ऑन-क्रिएट पर एक अपवाद उत्पन्न कर सकता है आपकी सेवा का तरीका जो स्टार्ट फ़ॉरग्राउंड को रोकने से रोकता है

 @Override public void onCreate() { try{ } catch(Exception e) { } finally{ startForeground(1, notificationbuilder.build()); } } 

सेवा या आशय सेवा के बाद तुरंत कॉल स्टार्टफोरग्राउंड विधि। इस तरह:

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()
            })
        })

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

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

मैंने इस पर एक बग खोला है: issuetracker.google.com/issues/76112072

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

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

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

issuetracker.google.com/issues/76112072#comment36

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

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

issuetracker.google.com/issues/76112072#comment56

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

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

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


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

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

              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);
    }

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

निजी स्थिर अंतिम स्ट्रिंग ANDROID_CHANNEL_ID = "com.xxxx.Location.Channel"; निजी स्थिर अंतिम int NOTIFICATION_ID = 555;

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

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

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

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

  stopSelf();

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


कृपया 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) से अधिक हो सकता है


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


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

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

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

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

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

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

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

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);
         }
    }
    ...
}

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

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

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 को स्टॉप आशय प्राप्त हो, क्योंकि यह MyForegroundService के MyForegroundService onCreate() में पंजीकृत हो गया है। इस समय तक हम पहले ही startForeground(...) चुके हैं। हमें अगली बार अधिसूचित स्टॉपराइवर को रोकने के लिए उस चिपचिपे प्रसारण को भी हटाना होगा।

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


मैंने कुछ दिनों के लिए इस पर शोध किया है और समाधान प्राप्त किया है। अब 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
    }
}

मैंने तब सेवा शुरू करने के लिए 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);
        }
}

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

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

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

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

  3. stopSelf से पहले stopSelf को नहीं बुलाया जाना चाहिए।

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

और ध्यान दें कि सबसे अधिक संभावना एंड्रॉइड पी अभी भी इस मुद्दे को ले जाएगा क्योंकि Google यह समझने से भी इनकार करता है कि क्या चल रहा है और यह नहीं मानता कि यह उनकी गलती है, अधिक जानकारी के लिए issuetracker.google.com/issues/76112072#comment36 और issuetracker.google.com/issues/76112072#comment56 पढ़ें


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

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

समाधान: उस Service लिए startForeground() में startForeground() कॉल करें जो आप startForeground() का उपयोग करते हैं

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







android-8.0-oreo