当应用在Android Pie上受后台限制时启动前台服务

时间:2018-10-03 21:19:57

标签: android foreground-service

使用Android 9.0(Pie),用户可以通过设置限制您的应用执行后台工作。即使在应用程序活动完全在前台的情况下,如果该应用程序受后台限制,当我们尝试在Pie设备上启动前台服务时,我们的应用程序也会遇到以下异常。

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

我们确实在已启动的Service中调用了startForeground(),但是尝试启动该服务时的系统日志显示:

system_process W/ActivityManager: Service.startForeground() not allowed due to bg restriction

当您遵循the documented steps进行前台服务时,似乎很奇怪收到异常,并且当您的应用程序也位于前台时,系统拒绝该应用程序启动前台服务。我还没有找到很多与此相关的文档,但是这种记录的行为吗?并至少有一种方法可以让您的应用知道其是否受后台限制,从而不尝试在前台启动该服务?

enter image description here

我的服务代码基本上如下所示。我们的目标api是27。

class MyService : Service() {

    override fun onCreate() {
        super.onCreate()
        // create notification
        startForeground(NOTIFICATION_ID, notification)
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        if (intent == null || intent.action == null || intent.action == ACTION_STOP) {
            quit()
        }
        doWork()
        return START_NOT_STICKY
    }

    override fun onBind(intent: Intent?): IBinder? {
        return null
    }

    override fun onTaskRemoved(rootIntent: Intent?) {
        super.onTaskRemoved(rootIntent)
        quit()
    }

    private fun quit() {
        stopForeground(true)
        stopSelf()
    }

    companion object {

        fun start(context: Context) {
            val intent = Intent(context, MyService::class.java)
            intent.action = ACTION_START
            ContextCompat.startForegroundService(context, intent)
        }

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

1 个答案:

答案 0 :(得分:1)

发现崩溃实际上是发生在第二次调用 MyService.stop()中的 ContextCompat.startForegroundService()时发生的,这是我用来发送Intent的使用“停止”操作来停止服务,而不是调用 context.stopService()。起初,我虽然需要在Service中手动调用 stopForeground(),但是调用context.stopService()似乎会停止前台服务并始终删除通知,并且不会导致崩溃,所以我决定重构停止服务的方式。

更新:我认为问题的另一部分也在尝试使用Intents启动和停止服务,尤其是因为在某些情况下,我的服务已启动,然后又停止得太快。很有帮助的thread with Ian Lake提出了有关服务的以下建议:

  

我强烈建议您不要使用startService作为将消息传递到您的服务的方法。使用EventBus或LocalBroadcastReceiver是一种更好的传递消息的方式,无需将其与生命周期操作混淆。我还避免让外部组件直接调用stopService()-让服务本身管理自己的生命周期,对发送方式的事件做出反应。