在应用程序启动时,应用程序启动应该执行一些网络任务的服务。 在目标API级别26后,我的应用程序无法在Android 8.0后台启动服务。

导致原因:java.lang.IllegalStateException:不允许启动 服务意图{ cmp = my.app.tt / com.my.service }: app是在后台uid UidRecord{90372b1 u0a136 CEM空闲procs:1 seq (0, 0, 0)}

我的理解是: 后台执行限制

startService()方法现在抛出一个IllegalStateException 针对Android 8.0的应用程序尝试使用这种方法 不允许创建后台服务。

“在不被允许的情况下”——这实际上是什么意思?以及如何修复它。我不想把我的服务设置为前台


当前回答

允许的情况是一个临时白名单,其中后台服务的行为与Android O之前相同。

Under certain circumstances, a background app is placed on a temporary whitelist for several minutes. While an app is on the whitelist, it can launch services without limitation, and its background services are permitted to run. An app is placed on the whitelist when it handles a task that's visible to the user, such as: Handling a high-priority Firebase Cloud Messaging (FCM) message. Receiving a broadcast, such as an SMS/MMS message. Executing a PendingIntent from a notification. Starting a VpnService before the VPN app promotes itself to the foreground.

来源:https://developer.android.com/about/versions/oreo/background.html

换句话说,如果您的后台服务不符合白名单要求,您必须使用新的JobScheduler。它基本上与后台服务相同,但它周期性地被调用,而不是持续地在后台运行。

如果你使用的是IntentService,你可以改成JobIntentService。请看下面@kosev的回答。

其他回答

允许的情况是一个临时白名单,其中后台服务的行为与Android O之前相同。

Under certain circumstances, a background app is placed on a temporary whitelist for several minutes. While an app is on the whitelist, it can launch services without limitation, and its background services are permitted to run. An app is placed on the whitelist when it handles a task that's visible to the user, such as: Handling a high-priority Firebase Cloud Messaging (FCM) message. Receiving a broadcast, such as an SMS/MMS message. Executing a PendingIntent from a notification. Starting a VpnService before the VPN app promotes itself to the foreground.

来源:https://developer.android.com/about/versions/oreo/background.html

换句话说,如果您的后台服务不符合白名单要求,您必须使用新的JobScheduler。它基本上与后台服务相同,但它周期性地被调用,而不是持续地在后台运行。

如果你使用的是IntentService,你可以改成JobIntentService。请看下面@kosev的回答。

如果你有集成的firebase消息推送通知,

由于后台执行限制,为android O (android 8.0)添加新的/更新firebase消息依赖项。

compile 'com.google.firebase:firebase-messaging:11.4.0'

如果需要,升级谷歌播放服务和谷歌存储库。

更新:

 compile 'com.google.firebase:firebase-messaging:11.4.2'

是的,那是因为在API 26上你不能在后台启动服务了。所以你可以在API 26之上启动ForegroundService。

你必须使用

ContextCompat.startForegroundService(...)

并在处理泄漏时发布通知。

由于对这个答案的争议投票(+4/-4),请先看看其他答案,并仅作为最后的手段。我只在一个以root身份运行的网络应用程序中使用过一次,我同意一般的意见,即在正常情况下不应该使用这种解决方案。

原答案如下:

其他答案都是正确的,但我想指出的是,另一种解决这个问题的方法是要求用户禁用应用程序的电池优化(这通常不是一个好主意,除非你的应用程序与系统相关)。看看这个答案如何请求选择退出电池优化而不让你的应用程序在谷歌播放被禁止。

你也应该检查电池优化是否在你的接收器关闭,以防止崩溃通过:

if (Build.VERSION.SDK_INT < 26 || getSystemService<PowerManager>()
        ?.isIgnoringBatteryOptimizations(packageName) != false) {
    startService(Intent(context, MyService::class.java))
} // else calling startService will result in crash

正如@kosev在他的回答中所说,你可以使用JobIntentService。 但是我使用了另一种解决方案——捕获IllegalStateException并将服务作为前台启动。 例如,这个函数启动我的服务:

@JvmStatic
protected fun startService(intentAction: String, serviceType: Class<*>, intentExtraSetup: (Intent) -> Unit) {
    val context = App.context
    val intent = Intent(context, serviceType)
    intent.action = intentAction
    intentExtraSetup(intent)
    intent.putExtra(NEED_FOREGROUND_KEY, false)

    try {
        context.startService(intent)
    }
    catch (ex: IllegalStateException) {
        intent.putExtra(NEED_FOREGROUND_KEY, true)
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(intent)
        }
        else {
            context.startService(intent)
        }
    }
}

当我处理意图时,我做了这样的事情:

override fun onHandleIntent(intent: Intent?) {
    val needToMoveToForeground = intent?.getBooleanExtra(NEED_FOREGROUND_KEY, false) ?: false
    if(needToMoveToForeground) {
        val notification = notificationService.createSyncServiceNotification()
        startForeground(notification.second, notification.first)

        isInForeground = true
    }

    intent?.let {
        getTask(it)?.process()
    }
}