475 votes

Android 8.0 : java.lang.IllegalStateException : Non autorisé à démarrer le service Intent

Au lancement de l'application, celle-ci démarre le service qui doit effectuer une tâche réseau. Après avoir ciblé le niveau 26 de l'API, mon application ne parvient pas à démarrer le service sur Android 8.0 en arrière-plan.

Causé par : java.lang.IllegalStateException : Non autorisé à démarrer service Intent { cmp=my.app.tt/com.my.service } : app is in background uid UidRecord{90372b1 u0a136 CEM idle procs:1 seq(0,0,0)}

tel que je le comprends lié à : Limites d'exécution en arrière-plan

La méthode startService() génère désormais une IllegalStateException si une application application ciblant Android 8.0 essaie d'utiliser cette méthode dans une situation où elle n'est pas autorisée à créer des services d'arrière-plan.

" dans une situation où il n'est pas permis "Qu'est-ce que cela signifie réellement ? Et comment le réparer. Je ne veux pas définir mon service comme "premier plan".

9 votes

Cela signifie que vous ne pouvez pas démarrer un service lorsque votre application est en arrière-plan.

0 votes

0 votes

Essayez le répartiteur de tâches de Firebase.

341voto

Sagar Kacha Points 4093

J'ai une solution. Pour les appareils pré-8.0, vous devez juste utiliser startService() mais pour les appareils post-7.0, vous devez utiliser la fonction startForgroundService() . Voici un exemple de code pour démarrer le service.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        context.startForegroundService(new Intent(context, ServedService.class));
    } else {
        context.startService(new Intent(context, ServedService.class));
    }

Et dans la classe de service, veuillez ajouter le code ci-dessous pour la notification :

@Override
public void onCreate() {
    super.onCreate();
    startForeground(1,new Notification());
}

Où O est la version 26 d'Android.

Si vous ne voulez pas que votre service s'exécute en avant-plan et souhaitez qu'il s'exécute en arrière-plan, après Android O, vous devez lier le service à une connexion comme ci-dessous :

Intent serviceIntent = new Intent(context, ServedService.class);
context.startService(serviceIntent);
context.bindService(serviceIntent, new ServiceConnection() {
     @Override
     public void onServiceConnected(ComponentName name, IBinder service) {
         //retrieve an instance of the service here from the IBinder returned 
         //from the onBind method to communicate with 
     }

     @Override
     public void onServiceDisconnected(ComponentName name) {
     }
}, Context.BIND_AUTO_CREATE);

16 votes

Un service de premier plan est quelque chose dont l'utilisateur aura connaissance et qui nécessite une notification. Il se déclenchera également s'il s'exécute trop longtemps. Ce n'est donc pas vraiment une réponse adaptée si l'application fonctionne déjà en arrière-plan.

90 votes

Il existe un ContextCompat.startForegroundService(...) dans la bibliothèque de support qui peut être utilisée à la place.

42 votes

Ce n'est pas une solution.

226voto

Murat Karagöz Points 13113

Les situations autorisées sont une liste blanche temporaire où le service d'arrière-plan se comporte de la même manière qu'avant Android O.

Dans certaines circonstances, une application en arrière-plan est placée sur une liste blanche temporaire pendant plusieurs minutes. Lorsqu'une application figure sur la liste blanche, elle peut lancer des services sans restriction et ses services d'arrière-plan sont autorisés à fonctionner. Une application est placée sur la liste blanche lorsqu'elle gère une tâche visible pour l'utilisateur, par exemple :

  • Traitement d'un message Firebase Cloud Messaging (FCM) de haute priorité.
  • Réception d'une diffusion, telle qu'un message SMS/MMS.
  • Exécution d'un PendingIntent à partir d'une notification.
  • Démarrer un VpnService avant que l'application VPN ne passe au premier plan.

Source : https://developer.Android.com/about/versions/oreo/background.html

En d'autres termes, si votre service d'arrière-plan ne répond pas aux exigences de la liste blanche, vous devez utiliser le nouveau service d'arrière-plan. JobScheduler . C'est essentiellement la même chose qu'un service d'arrière-plan, mais il est appelé périodiquement au lieu de fonctionner en arrière-plan en permanence.

Si vous utilisez un IntentService, vous pouvez passer à un JobIntentService. Voir l'article de @kosev réponse ci-dessous .

0 votes

J'obtiens un crash après avoir voulu démarrer le service juste après avoir reçu le message GCM de "high" prio. J'utilise toujours GCM : "com.google.Android.gms:play-services-gcm:11.4.2", et non "com.google.firebase:firebase-messaging:11.4.2". Pas sûr que cela ait de l'importance cependant

0 votes

"C'est fondamentalement la même chose qu'un service d'arrière-plan, mais il est appelé périodiquement au lieu de fonctionner en arrière-plan en permanence." - Je ne suis pas sûr de ce que vous voulez dire par là, car les services Android n'ont jamais exécuté en continu. Ils démarrent, s'exécutent, puis s'arrêtent.

2 votes

Est-ce que le FirebaseInstanceIdService et son onTokenRefresh méthode un message FCM de haute priorité ?

104voto

kosev Points 840

Le meilleur moyen est d'utiliser JobIntentService qui utilise le nouveau JobScheduler pour Oreo ou les anciens services s'ils ne sont pas disponibles.

Déclarez dans votre manifeste :

<service android:name=".YourService"
         android:permission="android.permission.BIND_JOB_SERVICE"/>

Et dans votre service, vous devez remplacer onHandleIntent par onHandleWork :

public class YourService extends JobIntentService {

    public static final int JOB_ID = 1;

    public static void enqueueWork(Context context, Intent work) {
        enqueueWork(context, YourService.class, JOB_ID, work);
    }

    @Override
    protected void onHandleWork(@NonNull Intent intent) {
        // your code
    }

}

Alors vous commencez votre service par :

YourService.enqueueWork(context, new Intent());

1 votes

0 votes

Comment pouvez-vous appeler une méthode non statique à l'intérieur d'une méthode statique ? Pouvez-vous nous expliquer ?

1 votes

@Maddy enqueueWork(...) est également une méthode statique.

38voto

Harini S Points 383

Si le service est exécuté dans un thread d'arrière-plan en étendant IntentService vous pouvez remplacer IntentService avec JobIntentService qui est fourni dans le cadre de la bibliothèque de support Android

L'avantage d'utiliser JobIntentService est, il se comporte comme un IntentService sur les appareils pré-O et sur les appareils O et supérieurs, il le distribue comme un travail

JobScheduler peut également être utilisé pour les tâches périodiques/à la demande. Mais, assurez-vous de gérer la rétrocompatibilité comme JobScheduler L'API n'est disponible qu'à partir de l'API 21

1 votes

Le problème avec JobIntentService est qu'Android peut programmer votre travail de manière plutôt arbitraire, et qu'il ne peut pas être démarré implicitement sans quelques bricoles, contrairement à un IntentService. Voir stackoverflow.com/questions/52479262/

21voto

P.K Points 155

Oui, c'est parce que tu ne peux plus lancer de services en arrière-plan sur l'API 26. Donc vous pouvez démarrer ForegroundService au-dessus de l'API 26.

Vous devrez utiliser

ContextCompat.startForegroundService(...)

et afficher une notification pendant le traitement de la fuite.

2 votes

Le PO a spécifiquement dit qu'il ne voulait pas de premier plan. Cela devrait être mis en commentaire ou faire partie d'une réponse plus complète.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X