Android 9 (Pie), Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord

前端 未结 6 2319
感情败类
感情败类 2020-12-13 20:17

First of all, I looked at these;

  • Context.startForegroundService() did not then call Service.startForeground()
  • Context.startForegroundService() did not
相关标签:
6条回答
  • 2020-12-13 21:07

    According to this error message this says as when you call Context.startForegroundService() then you must issue notification using Service.startForeground() method. This is what i understand.

    0 讨论(0)
  • 2020-12-13 21:12

    I have almost eliminated the problem with startForeground() in MediaSessionCompat.Callback methods like onPlay(), onPause().

    0 讨论(0)
  • 2020-12-13 21:13

    After having same issue with same phones, I have made few changes and the crashes were gone. I am not sure what made the trick, but I am guessing that calling startForeground in both onCreate and onStartCommand. I am not sure why that is needed if service is already started and everything was called properly in onCreate.

    Other changes: - Changing serviceId to some low number (1-10) - Calling startFororegroundService less often through a singleton synchronous class (this was implemented before with the crashes to prevent stopping service before it started with a callback from onStartCommand, but now it also filters calls if service already started). - using START_REDELIVER_INTENT (shouldn't affect anything)

    The issue has been happening on the mentioned phones just for some users, so I suspect it is related to some new update from Samsung and will eventually be fixed

    0 讨论(0)
  • 2020-12-13 21:15

    I was waiting my crash report to share the solution. I didn't get any crash or ANR almost 20 days. I want to share my solution. It can help those who encounter this problem.

    In onCreate() method

    • First of all, my app is a media application. I didn't implement the mediasession yet. I'm creating a notification channel in the top of onCreate(). Official doc
    • I'm calling Service.startForeground() method after Context.startForegroundService() method. In my prepareAndStartForeground() method.

      Note: I don't know why but ContextCompat.startForegroundService() doesn't work properly.

    For this reason, I've added manually same function to my service class instead of calling ContextCompat.startForegroundService()

    private fun startForegroundService(intent: Intent) {
        if (Build.VERSION.SDK_INT >= 26) {
            context.startForegroundService(intent)
        } else {
            // Pre-O behavior.
            context.startService(intent)
        }
    }
    

    prepareAndStartForeground() method

    private fun prepareAndStartForeground() {
        try {
            val intent = Intent(ctx, MusicService::class.java)
            startForegroundService(intent)
    
            val n = mNotificationBuilder.build()
            // do sth
            startForeground(Define.NOTIFICATION_ID, n)
        } catch (e: Exception) {
            Log.e(TAG, "startForegroundNotification: " + e.message)
        }
    }
    

    It's my onCreate()

    override fun onCreate() {
        super.onCreate()
        createNotificationChannel()
        prepareAndStartForeground()
    }
    

    My onStartCommand()

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        if (intent == null) {
            return START_STICKY_COMPATIBILITY
        } else {
            //....
            //...
        }
        return START_STICKY
    }
    

    onRebind, onBind, onUnbind methods like these

    internal var binder: IBinder? = null
    
    override fun onRebind(intent: Intent) {
        stopForeground(true) // <- remove notification
    }
    
    override fun onBind(intent: Intent): IBinder? {
        stopForeground(true) // <- remove notification
        return binder
    }
    
    override fun onUnbind(intent: Intent): Boolean {
        prepareAndStartForeground() // <- show notification again
        return true
    }
    

    We need to clear something when onDestroy() calling

       override fun onDestroy() {
        super.onDestroy()
        releaseService()
       }
    

    private fun releaseService() {
        stopMedia()
        stopTimer()
        // sth like these
        player = null
        mContext = null
        afChangeListener = null
        mAudioBecomingNoisy = null
        handler = null
        mNotificationBuilder = null
        mNotificationManager = null
        mInstance = null
    }
    

    I hope this solution works properly for you.

    0 讨论(0)
  • 2020-12-13 21:18

    The Android Service component is a bit tricky to get working properly, especially on later Android versions where the OS adds additional restrictions. As mentioned in other answers, when starting your Service, use ContextCompat.startForegroundService(). Next, in Service.onStartCommand(), call startForeground() immediately. Store the Notification you want to show as a member field and use that unless it is null. Example:

    private var notification:Notification? = null
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        if (notification == null) {
            notification = createDefaultNotification()
        }
        startForeground(NOTIFICATION_ID, notification)
    
        // Do any additional setup and work herre
    
        return START_STICKY
    }
    

    Always return START_STICKY in your Service. Anything else is probably the wrong thing, especially if you're doing a audio player of any kind. In fact, if you're doing an audio player, you shouldn't implement your own Service but use MediaBrowserServiceCompat (from AndroidX) instead.

    I also recommend the blog posts I wrote on this: https://hellsoft.se/how-to-service-on-android-part-3-1e24113152cd

    0 讨论(0)
  • 2020-12-13 21:19

    After too much struggle with this crash finally, I fixed this exception completely and find the solution.

    Make sure that have done this stuff in your service I list them as below : (some of this stuff are repetitious as mentioned in another answer I just write them again).

    1- Call

    startForeground()

    in both onCreate and onStartCommand.(it's ok to call startForeground() many times)

      @Override
    public void onCreate() {
        super.onCreate();
        startCommand();
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent == null) {
            return START_NOT_STICKY;
        }
        final int command = intent.getIntExtra(MAIN_SERVICE_COMMAND_KEY, -1);
    
        if (command == MAIN_SERVICE_START_COMMAND) {
            startCommand();
            return START_STICKY;
        }
        return START_NOT_STICKY;
    }
    
     private void startCommand() {
        createNotificationAndStartForeground();
        runningStatus.set(STARTED);
    }
    

    2- Stop your service using

    context.stopService()

    , there is no need to call stopForeground() or stopSelf().

      try {
            context.stopService(
                    new Intent(
                            context,
                            NavigationService.class
                    )
            );
        } catch (Exception ex) {
            Crashlytics.logException(ex);
            LogManager.e("Service manager can't stop service ", ex);
        }
    

    3- Start your service using

    ContextCompat.startForegroundService()

    it will handle different API versions.

       ContextCompat.startForegroundService(
                context,
                NavigationService.getStartIntent(context)
        );
    

    4- If your service has actions ( need pending Intents ) handle your pending intents with a Broadcast receiver rather than your current service( it will call your service on Create() and can be dangerous, or use PendingIntent.FLAG_NO_CREATE) , it's a good practice to have a specific Broadcast receiver for handling your service notification actions, I mean create all your pending intents using PendingIntent.getBroadcast().

        private PendingIntent getStopActionPendingIntent() {
        final Intent stopNotificationIntent = getBroadCastIntent();
    
        stopNotificationIntent.setAction(BROADCAST_STOP_SERVICE);
    
        return getPendingIntent(stopNotificationIntent);
    }
    
    private PendingIntent getPendingIntent(final Intent intent) {
        return PendingIntent.getBroadcast(
                this,
                0,
                intent,
                0
        );
    }
    
    new NotificationCompat.Builder(this, CHANNEL_ID)
                .addAction(
                        new NotificationCompat.Action(
                                R.drawable.notification,
                                getString(R.string.switch_off),
                                getStopActionPendingIntent()
                        )
                )
    

    5- Always before stop your service make sure that your service is created and started (I Create a global class that has my service state)

      if (navigationServiceStatus == STARTED) {
                serviceManager.stopNavigationService();
            }
    

    6- Set your notificationId to a long number such as 121412.

    7- Using NotificationCompat.Builder will handle different API versions you just need to create notification channel for Build versions >= Build.VERSION_CODES.O.(This one is not a solution just make your code more readable)

    8- Add

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

    permission to your manifest. (this one is mentioned in android docs) Android Foreground Service

    hope it helps :))

    0 讨论(0)
提交回复
热议问题