Android 8.0: java.lang.IllegalStateException: Non autorisé à démarrer l'intention du service


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

Causé par: java.lang.IllegalStateException: Pas autorisé à démarrer service Intention { cmp=my.app.tt/com.my.service }: app est en arrière-plan uid UidRecord{90372b1 u0a136 CEM idle procs: 1 seq (0,0,0)}

Si je comprends bien lié à: Exécution en arrière-plan les limites de

La méthode startService () lance maintenant une exception IllegalStateException si application ciblant Android 8.0 essaie d'utiliser cette méthode dans une situation où il n'est pas permis de créer des services d'arrière-plan.

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

Author: phnmnn, 2017-09-27

12 answers

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 est sur la liste blanche, elle peut lancer des services sans limitation, et ses services d'arrière-plan sont autorisés à s'exécuter. Une application est placée sur la liste blanche lorsqu'elle gère une tâche visible par l'utilisateur, telle que:

  • Manipulation un message Firebase Cloud Messaging (FCM) hautement prioritaire.
  • 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 se mette en avant.

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

Donc, 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 JobScheduler . C'est fondamentalement la même chose qu'un service d'arrière-plan, mais il est appelé périodiquement au lieu de s'exécuter en arrière-plan en continu.

 87
Author: Murat Karagöz, 2017-09-27 10:28:21

J'ai une solution. Pour les appareils pré-8.0, vous devez simplement utiliser startService(), mais pour les appareils post-7.0, vous devez utiliser 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 Android 26.

J'espère que cela résoudra IllegalArgumentException

 105
Author: Sagar Kacha, 2018-03-31 20:05:51

Si le service s'exécute dans un thread d'arrière-plan en étendant IntentService , vous pouvez remplacer IntentService par {[2] } qui est fourni dans le cadre de la bibliothèque de support Android

L'avantage de l'utilisation de JobIntentService est qu'il se comporte comme un IntentService sur les périphériques pré-O et sur O et supérieur, il l'envoie comme un travail

JobScheduler peut également être utilisé pour des travaux périodiques/à la demande. Mais, assurez-vous de gérer la compatibilité descendante car l'API JobScheduler n'est disponible qu'à partir de l'API 21

 22
Author: Harini S, 2018-04-01 10:00:22

La meilleure façon 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
    }

}

Ensuite, vous commencez votre service avec:

YourService.enqueueWork(context, new Intent());
 21
Author: kosev, 2018-04-15 20:21:52

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

Vous devrez utiliser

ContextCompat.startForegroundService(...)

Et poster une notification lors du traitement de la fuite.

 11
Author: P.K, 2018-05-07 05:48:30

À partir des notes de version firebase , ils indiquent que la prise en charge d'Android O a été publiée pour la première fois dans 10.2.1 (bien que je recommande d'utiliser la version la plus récente).

Veuillez ajouter de nouvelles dépendances de messagerie firebase pour Android O

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

Mettez à niveau les services Google play et les référentiels Google si nécessaire.

 3
Author: Dhaval Jivani, 2017-12-13 06:53:53

Si vous avez intégré la notification push de messagerie firebase,

Ajouter de nouvelles/mettre à jour les dépendances de messagerie firebase pour Android O (Android 8.0), en raison de Limites d'exécution en arrière-plan .

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

Mettez à niveau les services Google play et les référentiels Google si nécessaire.

Mise à Jour:

 compile 'com.google.firebase:firebase-messaging:11.4.2'
 2
Author: Samir Mangroliya, 2017-10-04 11:38:45

Utiliser startForegroundService() au lieu de startService() et n'oubliez pas de créer startForeground(1,new Notification()); dans votre service dans les 5 secondes suivant le démarrage du service.

 1
Author: Arpit, 2018-04-05 19:33:35

Les autres réponses sont toutes correctes, mais je voudrais souligner qu'une autre façon de contourner cela est de demander à l'utilisateur de désactiver les optimisations de batterie pour votre application (ce n'est généralement pas une bonne idée à moins que votre application ne soit liée au système). Voir cette réponse pour savoir comment demander de désactiver les optimisations de batterie sans que votre application soit interdite dans Google Play.

Vous devez également vérifier si les optimisations de batterie sont désactivées dans votre récepteur pour éviter les plantages via:

if (Build.VERSION.SDK_INT < 26 || getSystemService<PowerManager>()
        ?.isIgnoringBatteryOptimizations(packageName) != false) {
    startService(Intent(context, MyService::class.java))
} // else calling startService will result in crash
 0
Author: Mygod, 2018-07-22 09:13:15

Si vous exécutez votre code sur 8.0, l'application se bloque. Démarrez donc le service au premier plan. Si inférieur à 8.0, utilisez ceci:

Intent serviceIntent = new Intent(context, RingtonePlayingService.class);
context.startService(serviceIntent);

Si ci-dessus ou 8.0, utilisez ceci:

Intent serviceIntent = new Intent(context, RingtonePlayingService.class);
ContextCompat.startForegroundService(context, serviceIntent );
 0
Author: Faizy, 2018-09-18 11:14:11

- La première méthode d'appel dans l'application de démarrage ou l'activité principale:

public static void createChancelNotification(Context mContext) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        NotificationManager notificationManager =
                (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
        NotificationChannel androidChannel = new NotificationChannel(Constants.ANDROID_CHANNEL_ID,
                Constants.ANDROID_CHANNEL_NAME, NotificationManager.IMPORTANCE_MAX);
        notificationManager.createNotificationChannel(androidChannel);
    }
}

- La seconde lorsque vous fermez app et firebase push:

public class FirebaseDataReceiver extends WakefulBroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
    if (intent.getExtras() != null) {
        for (String key : intent.getExtras().keySet()) {
            Object value = intent.getExtras().get(key);
            Log.e("FirebaseDataReceiver", "Key: " + key + " Value: " + value);
        }
    }
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
        // Attach component of GCMIntentService that will handle the intent in background thread
        ComponentName componentName = new ComponentName(context.getPackageName(), MyFirebaseMessageService.class.getName());
        // Start the service, keeping the device awake while it is launching.
        startWakefulService(context, intent.setComponent(componentName));
        setResultCode(Activity.RESULT_OK);
    }

}

}

- La dernière poignée dans le fichier:

public class MyFirebaseMessageService extends FirebaseMessagingService {
private static final String TAG = MyFirebaseMessageService.class.getSimpleName();

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
    if (remoteMessage.getData() != null) {
        // Do some thing here
    }
}
 -1
Author: Nguyễn Văn Linh, 2018-05-04 03:02:58

Ne pas utiliser dans onStartCommand:

return START_NOT_STICKY

Il suffit de le changer en:

return START_STICKY

Et c'est la volonté qui fonctionne

 -5
Author: Omar Othman, 2018-05-01 15:17:41