在Android Pie系统中,当应用程序被后台限制时启动前台服务

5

在安卓9.0(Pie)中,用户可以通过设置限制您的应用程序进行后台工作。即使应用程序活动完全在前台,如果应用程序被限制在后台,则我们的应用程序在Pie设备上尝试启动前台服务时会出现以下异常。

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

我们在已启动的服务中调用了startForeground(),但是尝试启动服务时系统日志显示:
system_process W/ActivityManager: Service.startForeground() not allowed due to bg restriction

似乎在按照前台服务的文档步骤操作时,当您的应用程序仍处于前台时,系统拒绝启动前台服务时收到异常是很奇怪的。我还没有找到与此相关的大量文档,但这是否是已记录的行为?并且,是否至少有一种方法让您的应用程序知道它是否已被后台限制,从而不尝试在前台启动服务?

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

你的服务在清单文件中是如何声明的? - Michael Krause
@MichaelKrause 它只有普通的 nameexport="false" - George Ellickson
你能展示一下调用startForegroundService的代码以及调用带有通知ID和通知的startForeground的代码吗?另外,你的targetApi设置是什么? - Michael Krause
@MichaelKrause 好的,我已经更新了帖子。我们的目标是27,但这只会在运行28的设备上发生。无论如何,仍然很难找到与日志消息“Service.startForeground() not allowed due to bg restriction”相关的文档,以及是否有新的API可以告诉您的应用程序是否受到后台限制。有UsageStatsManager.getAppStandbyBucket,但文档没有提到受限制的用户应用程序。 - George Ellickson
啊,我明白了。既然你说你的应用程序在前台可见,那么这很奇怪。你确定你的活动已经恢复了吗?例如,如果在此时记录各种活动生命周期回调,你会看到什么?你真的处于恢复状态吗? - Michael Krause
@MichaelKrause 找到了原因。如果还有兴趣,请看我的回答。感谢您的帮助。 - George Ellickson
1个回答

2
发现崩溃实际上是从MyService.stop()中第二次调用ContextCompat.startForegroundService()时发生的,我使用它发送一个带有“stop”操作的Intent来停止服务,而不是调用context.stopService()。起初我认为我需要手动调用stopForeground()来停止服务,但调用context.stopService()似乎会停止前台服务并删除通知,不会导致崩溃,因此我决定重构如何处理停止服务的方式。
更新:我认为问题的另一个部分也在于尝试使用Intent启动和停止服务,特别是因为在某些情况下我的服务启动后很快就停止了。一篇非常有用的与Ian Lake的线程给出了以下关于服务的建议:

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


我也遇到了同样的问题。我不太明白你的意思。为什么在Service中手动调用stopForeground()会导致API 28崩溃?请告诉我你目前的解决方案是什么? - Huy Duong Tu
有关受限制应用程序的相关内容吗? - Huy Duong Tu
@HuyDuongTu 我认为崩溃会在我第二次调用ContextCompat.startForegroundService()时发生,那是我用来停止服务的,但只有在应用程序被后台限制时才会发生,因为startForeground已经失败了。不完全确定系统为什么选择崩溃。总体而言,更大的问题是我使用意图(Intents)来控制启动和停止服务。Ian Lake强烈建议让服务控制自己的生命周期,以尽可能避免启动和停止问题。请查看此对话: https://plus.google.com/116630648530850689477/posts/L2rn4T6SAJ5 - George Ellickson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接