Search code examples
androidandroid-serviceforeground-service

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


First of all, I looked at these;

enter image description here

I have a streaming application used by almost a million people. I'm using foreground service for the player. I didn't implement MediaSession yet. I have a 99.95% crash-free sessions. So this app works on all versions but I started getting crash reports(ANR) with android 9. This crash occurs only Samsung phones, especially s9, s9+, s10, s10+, note9 models.

I tried these,

  • Calling startForeground() method in onCreate()
  • Calling Service.startForeground() before Context.stopService()
  • Other stackoverflow answers to similar questions

I read some comments from developers of Google, they said it's just Intended Behavior. I wonder is it occurred by Samsung's system or Android OS. Does anyone have an opinion about this? How can I fix this?


Solution

  • 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.