Context.startForegroundService() did not then call Service.startForeground() - still a problem

会有一股神秘感。 提交于 2020-01-13 18:25:53

问题


After checking the google issue and a lot of other issues on SO, I came to the solution I added at the bottom.

What I take care of is following:

  • in onCreate AND in onStartCommand I make sure to move the service to the foregorund (if it is not already in the foreground)
  • I don't simply stop a service, I do this with an extra command that I send to the onStartCommand handler to make sure, that the service is not stopped while it's started (eventually before it could finish going to the foreground)
  • I never stop the service directly (context.stopService(...)), I always stop the service via a command from the running service itself - so I make sure that it can only be stopped while it is running in the foreground and not during start up
  • a service can only be stopped once
  • I stop a service by cancelling it's foreground notification and only afterwards I stop the service itself

I personally use the service for overlays, so I do not handle binders inside my class, as I do not use them.

Log I got

2019-07-29 21:41:27,146 [[BaseOverlayService:62 onCreate]]: onCreate
2019-07-29 21:41:27,146 [[BaseOverlayService:142 b]]: BEFORE moveToForeground (called by onCreate)
2019-07-29 21:41:27,152 [[BaseOverlayService:159 b]]: AFTER moveToForeground (called by onCreate) - moved to foreground: true
2019-07-29 21:41:27,176 [[BaseOverlayService:79 onStartCommand]]: onStartCommand: isForeground: true | action: null | isStopping: false
2019-07-29 21:41:27,945 [[BaseOverlayService:142 b]]: BEFORE moveToForeground (called by updateNotification [OverlayService [onInitFinished]])
2019-07-29 21:41:27,947 [[BaseOverlayService:159 b]]: AFTER moveToForeground (called by updateNotification [OverlayService [onInitFinished]]) - moved to foreground: false

This is the log of one crash report - as you can see, service is moved to foreground in line 3 (moved to foreground: true) and in line 6 it knows that it is running in foreground already.

I use this app on my android 9 device heavily (24/7, it's constantly running) and don't have problems and since I use the base class from below the problem has really minimized itself to a few crashes a month in total. Still, the log above shows, that my service is running in foreground within milliseconds, still it can crash like following:

android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{86fa711 u0 com.my.app/com.my.app.service.OverlayService}
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1855)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loop(Looper.java:214)
    at android.app.ActivityThread.main(ActivityThread.java:6986)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1445)

Does anyone see any issues with my base class?

Code

abstract class BaseOverlayService<T : BaseOverlayService<T>>(
        val foregroundNotificationId: Int,
        val notificationCreator: ((service: T) -> Notification)
) : Service() {

    companion object {

        val DEBUG = true

        // helper function, simply checks if this service is already running by checking the ActivityManager
        inline fun <reified T : BaseOverlayService<T>> isRunning(context: Context): Boolean {
            return Tools.isServiceRunning(context, T::class.java)
        }

        inline fun <reified T : BaseOverlayService<T>> start(context: Context, checkIfServiceIsRunning: Boolean) {
            if (checkIfServiceIsRunning && isRunning<T>(context)) {
                L.logIf { DEBUG }?.d { "IGNORED start intent" }
                return
            }

            L.logIf { DEBUG }?.d { "send start intent" }
            val intent = Intent(context, T::class.java)
            ContextCompat.startForegroundService(context, intent)
        }

        inline fun <reified T : BaseOverlayService<T>> sendAction(context: Context, checkIfServiceIsRunning: Boolean, action: String, intentUpdater: ((Intent) -> Unit) = {}) {
            if (checkIfServiceIsRunning && !isRunning<T>(context)) {
                L.logIf { DEBUG }?.d { "IGNORED action intent - action: $action" }
                return
            }

            L.logIf { DEBUG }?.d { "send action intent - action: $action" }
            val intent = Intent(context, T::class.java)
            intent.action = action
            intentUpdater(intent)
            ContextCompat.startForegroundService(context, intent)
        }
    }

    protected var isForeground = false
        private set
    protected var isStopping: Boolean = false
        private set

    // ------------------------
    // service events
    // ------------------------

    final override fun onCreate() {

        L.logIf { DEBUG }?.d { "onCreate" }

        super.onCreate()

        if (foregroundNotificationId <= 0) {
            throw RuntimeException("foregroundNotificationId must be > 0!")
        }

        moveToForeground("onCreate")

        onCreateEvent()
    }

    final override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {

        val returnValue = START_STICKY

        L.logIf { DEBUG }?.d { "onStartCommand: isForeground: $isForeground | action: ${intent?.action} | isStopping: $isStopping" }

        // 1) if service is stopping, we ignore the event
        if (isStopping) {
            return returnValue
        }

        // 2) if service is not running in foreground we make it run in the foreground
        if (!isForeground) {
            moveToForeground("onStartCommand")
        }

        onStartCommandEvent(intent, flags, startId)

        return returnValue
    }

    final override fun onBind(intent: Intent): IBinder? {
        // overlay service is never bound!
        return null
    }

    // ------------------------
    // Forwarded abstract events
    // ------------------------

    abstract fun onCreateEvent()

    abstract fun onStartCommandEvent(intent: Intent?, flags: Int, startId: Int)

    abstract fun onStopEvent()

    // ------------------------
    // protected functions
    // ------------------------

    protected fun stopService() {

        L.logIf { DEBUG }?.d { "stopService | isStopping: $isStopping" }

        if (isStopping) {
            L.logIf { DEBUG }?.d { "IGNORED stopService" }
            return
        }

        onStopEvent()
        isStopping = true
        moveToBackground(true)
        stopSelf()

        L.logIf { DEBUG }?.d { "stopService finished" }
    }

    protected fun updateNotification(caller: String) {
        moveToForeground("updateNotification [$caller]")
    }

    // ------------------------
    // private foreground/background functions
    // ------------------------

    private fun moveToForeground(caller: String): Boolean {

        L.logIf { DEBUG }?.d { "BEFORE moveToForeground (called by $caller)" }

        // 1) Create notification
        val notification = notificationCreator(this as T)

        // 2.1) Create foreground notification
        val result = if (!isForeground) {
            isForeground = true
            startForeground(foregroundNotificationId, notification)
            true
        }
        // 2.2) Update foreground notification
        else {
            notificationManager.notify(foregroundNotificationId, notification)
            false
        }

        L.logIf { DEBUG }?.d { "AFTER moveToForeground (called by $caller) - moved to foreground: $result" }

        return result
    }

    private fun moveToBackground(cancelNotification: Boolean) {
        isForeground = false
        super.stopForeground(cancelNotification)
    }

    // ------------------------
    // private helper functions
    // ------------------------

    private val notificationManager by lazy {
        getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    }
}

回答1:


I had the same issue and could solve it like this:

From Android 9 Pie if your service does not call startForeground within 5 seconds after it has been started with the command startForegroundService ... then it produces an ANR + Crash.

The solution is to add a startForeground() command, with your notification, right at the beginning of the onStartCommand method of your foreground service, like this:

final override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int 
{
    startForeground(YOUR_FOREGROUND_NOTIFICATION_ID, Notification);
    // rest of your code here...
}



回答2:


Try to check this answer - I guess it might be helpful in your situation.



来源:https://stackoverflow.com/questions/57272203/context-startforegroundservice-did-not-then-call-service-startforeground-s

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!