Avec Android 9.0 (Pie), il est possible pour l'utilisateur d'empêcher votre application d'effectuer des tâches en arrière-plan via les paramètres. Notre application a constaté l'exception suivante lorsque nous essayons de démarrer un service de premier plan sur les appareils Pie si l'application est restreinte par un arrière-plan, même lorsqu'une activité d'application est entièrement à l'avant-plan.
RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground()
Nous appelons startForeground () dans le service démarré, mais le journal système lors de la tentative de démarrage du service indique:
system_process W/ActivityManager: Service.startForeground() not allowed due to bg restriction
Il semble étrange de recevoir une exception lorsque vous suivez les étapes documentées pour les services au premier plan, et que le système refuse l'application depuis le démarrage du service au premier plan alors que votre application est toujours au premier plan. Je n'ai pas encore trouvé beaucoup de documentation à ce sujet, mais s'agit-il d'un comportement documenté? Et y a-t-il au moins un moyen pour votre application de savoir si son arrière-plan a été restreint et de ne pas tenter de démarrer le service au premier plan?
Mon code de service ressemble essentiellement à ci-dessous. Notre cible cible est 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)
}
}
}
Déterminé que le crash se produirait à partir du deuxième appel à ContextCompat.startForegroundService () dans MyService.stop (), que j’utilisais pour envoyer une intention avec une action "stop" pour arrêter le service plutôt que d'appeler context.stopService (). Au début, j’imaginais que j’avais besoin d’appeler manuellement stopForeground () dans le service, mais le fait d'appeler context.stopService () semble arrêter le service de premier plan et supprimer la notification de toute façon, sans provoquer de blocage. Nous avons décidé de revoir comment je gère l’arrêt du service.
UPDATE: Je pense qu'une autre partie du problème consistait également à utiliser Intents pour démarrer et arrêter le service, notamment parce que, dans certains cas, mon service avait été démarré puis arrêté trop rapidement. Un très utile fil avec Ian Lake donne ces recommandations sur les services:
Je suggère fortement de ne pas utiliser startService comme moyen de transmettre des messages à votre service. Utiliser un EventBus ou LocalBroadcastReceiver est un moyen bien plus efficace de transmettre des messages sans le confondre avec des actions de cycle de vie. J'éviterais également que des composants externes appellent directement stopService () - laissez le service lui-même gérer son propre cycle de vie, en réagissant aux événements que vous lui envoyez.