web-dev-qa-db-fra.com

Android 8.0: exception Java.lang.IllegalStateException: interdiction de démarrer le service Intention

Au lancement de l'application, l'application démarre le service devant 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: non autorisé à démarrer intention de service { cmp = my.app.tt/com.my.service }: l'application est en arrière-plan uid UidRecord {90372b1 u0a136 Procs inactif CEM: 1 seq (0,0,0)}

si je comprends bien, cela concernait: Limites d’exécution en arrière-plan

La méthode startService () lève maintenant une exception IllegalStateException s'il s'agit d'un Une application ciblant Android 8.0 tente 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ù ce 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"

217
phnmnn

Les situations autorisées sont une liste blanche temporaire dans laquelle 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 ajoutée à une liste blanche temporaire pendant plusieurs minutes. Lorsqu'une application figure 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 dans la liste blanche lorsqu'elle traite une tâche visible pour l'utilisateur, telle que:

  • Traitement d'un message FCM (Firebase Cloud Messaging) hautement prioritaire.
  • Réception d'une émission, telle qu'un message SMS/MMS.
  • Exécution d'un PendingIntent à partir d'une notification.
  • Démarrer un service 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 JobScheduler . C'est fondamentalement la même chose qu'un service en arrière-plan, mais il est appelé périodiquement au lieu de fonctionner en arrière-plan de manière continue.

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

125
Murat Karagöz

J'ai la solution. Pour les périphériques antérieurs à la version 8.0, vous devez simplement utiliser startService(), mais pour les périphériques à compter de la version 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 ça va résoudre IllegalArgumentException

140
Sagar Kacha

Le meilleur moyen consiste à 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 à 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());
45
kosev

Si le service s'exécute dans un thread en arrière-plan en développant IntentService, vous pouvez remplacer IntentService par JobIntentService fourni avec Android Support Library.

L’avantage d’utiliser JobIntentService est qu’il se comporte comme un IntentService sur les périphériques pré-O et sur O et les versions supérieures, il le distribue en tant que travail.

JobScheduler peut également être utilisé pour des travaux périodiques/à la demande. Cependant, assurez-vous de gérer la compatibilité avec les versions antérieures car JobScheduler API est uniquement disponible à partir de l’API 21.

26
Harini S

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

Vous devrez utiliser 

ContextCompat.startForegroundService(...)

et poster une notification pendant le traitement de la fuite.

11
P.K

D'après les notes de publication de firebase , ils indiquent que la prise en charge d'Android O a été publiée pour la première fois dans la version 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'

mettre à niveau les services google play et les référentiels google si nécessaire.

4
Dhaval Jivani

Comme @kosev a déclaré dans sa réponse vous pouvez utiliser JobIntentService. Mais j'utilise une solution alternative: j'attrape IllegalStateException et lance le service en avant-plan. Par exemple, cette fonction démarre mon service:

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

et quand je traite l'intention, je fais la chose suivante:

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()
    }
}
3
Alex Shevelev

Je vois beaucoup de réponses qui recommandent d'utiliser simplement un service de premier plan. Pour utiliser un service de premier plan, une notification doit lui être associée. Les utilisateurs verront cette notification. Selon la situation, ils risquent de devenir agacés par votre application et de la désinstaller. 

La solution la plus simple consiste à utiliser le nouveau composant d'architecture appelé WorkManager. Vous pouvez consulter la documentation ici: https://developer.Android.com/topic/libraries/architecture/workmanager/

Vous venez de définir votre classe de travail qui étend Worker.

public class CompressWorker extends Worker {

    public CompressWorker(
        @NonNull Context context,
        @NonNull WorkerParameters params) {
        super(context, params);
    }

    @Override
    public Worker.Result doWork() {

        // Do the work here--in this case, compress the stored images.
        // In this example no parameters are passed; the task is
        // assumed to be "compress the whole library."
        myCompress();

        // Indicate success or failure with your return value:
        return Result.SUCCESS;

        // (Returning RETRY tells WorkManager to try this task again
        // later; FAILURE says not to try again.)
    }
}

Ensuite, vous planifiez quand vous voulez l'exécuter.

    OneTimeWorkRequest compressionWork = 
        new OneTimeWorkRequest.Builder(CompressWorker.class)
            .build();
    WorkManager.getInstance().enqueue(compressionWork);

Facile! Il y a beaucoup de façons de configurer les travailleurs. Il prend en charge les travaux récurrents et vous pouvez même effectuer des tâches complexes comme le chaînage si vous en avez besoin. J'espère que cela t'aides.

2
TALE

si vous avez intégré la notification push de messagerie Firebase, 

Ajouter de nouvelles dépendances de messagerie Firebase/mettre à jour 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'

mettre à niveau les services google play et les référentiels google si nécessaire.

Mettre à jour:

 compile 'com.google.firebase:firebase-messaging:11.4.2'
2
Samir Mangroliya

Si vous exécutez votre code sur la version 8.0, l'application se bloquera. Commencez 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 );
2
Faizy

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

1
Arpit

Solution alternative en utilisant JobScheduler, il peut démarrer le service en arrière-plan à intervalles réguliers.

Tout d’abord rendre la classe nommée comme til.Java

import Android.app.job.JobInfo;
import Android.app.job.JobScheduler;
import Android.content.ComponentName;
import Android.content.Context;

public class Util {
// schedule the start of the service every 10 - 30 seconds
public static void schedulerJob(Context context) {
    ComponentName serviceComponent = new ComponentName(context,TestJobService.class);
    JobInfo.Builder builder = new JobInfo.Builder(0,serviceComponent);
    builder.setMinimumLatency(1*1000);    // wait at least
    builder.setOverrideDeadline(3*1000);  //delay time
    builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);  // require unmetered network
    builder.setRequiresCharging(false);  // we don't care if the device is charging or not
    builder.setRequiresDeviceIdle(true); // device should be idle
    System.out.println("(scheduler Job");

    JobScheduler jobScheduler = null;
    if (Android.os.Build.VERSION.SDK_INT >= Android.os.Build.VERSION_CODES.M) {
        jobScheduler = context.getSystemService(JobScheduler.class);
    }
    jobScheduler.schedule(builder.build());
   }
  }

Ensuite, attribuez à la classe JobService le nom TestJobService.Java

import Android.app.job.JobParameters;
import Android.app.job.JobService;
import Android.widget.Toast;

  /**
   * JobService to be scheduled by the JobScheduler.
   * start another service
   */ 
public class TestJobService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
    Util.schedulerJob(getApplicationContext()); // reschedule the job
    Toast.makeText(this, "Bg Service", Toast.LENGTH_SHORT).show();
    return true;
}

@Override
public boolean onStopJob(JobParameters params) {
    return true;
  }
 }

Après cette classe BroadCast Receiver nommée ServiceReceiver.Java

import Android.content.BroadcastReceiver;
import Android.content.Context;
import Android.content.Intent;

 public class ServiceReceiver extends BroadcastReceiver {
 @Override
public void onReceive(Context context, Intent intent) {
    Util.schedulerJob(context);
 }
}

Mise à jour fichier manifeste avec le code de classe de service et de destinataire

<receiver Android:name=".ServiceReceiver" >
        <intent-filter>
            <action Android:name="Android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>
    <service
        Android:name=".TestJobService"
        Android:label="Word service"
        Android:permission="Android.permission.BIND_JOB_SERVICE" >

    </service>

Gauche main_intent launcher au fichier mainActivity.Java qui est créé par défaut, aucun changement dans MainActivity.Java

Le service d'arrière-plan WOOAAH !! démarre sans service de premier plan

1
Anshul1507

Dans Oreo Android défini limites des services d'arrière-plan .

Pour améliorer l'expérience utilisateur, Android 8.0 (API niveau 26) impose limitations sur ce que les applications peuvent faire lors de l'exécution en arrière-plan.

Si vous avez toujours besoin du service, vous pouvez utiliser le service de premier plan.

Limitations du service en arrière-plan: Lorsqu'une application est inactive, il existe des limites à son utilisation des services d'arrière-plan. Cela ne s'applique pas à l'avant-plan services, qui sont plus visibles pour l'utilisateur.

Vous pouvez donc créer un service de premier plan. Vous devrez afficher un notification à l'utilisateur lorsque votre service est en cours d'exécution. Voir cette réponse (Il y en a beaucoup d'autres)

Une solution si -

vous ne voulez pas de notification pour votre service?

Vous pouvez effectuer une tâche périodique, 1. elle démarre votre service, 2. le service fera son travail et 3. s’arrête tout seul. De ce fait, votre application ne sera pas considérée comme une batterie épuisante.

Vous pouvez utiliser des tâches périodiques avec Alarm Manager , Planificateur de tâches , Evernote-Jobs ou Work Manager _

  • _ {Le gestionnaire de travaux est préférable} _ solution pour les tâches périodiques. Ce qui a été introduit avec composant d'architecture Android
  • Contrairement à Job-Scheduler (uniquement> 21 API), il fonctionnera pour toutes les versions.
  • En outre, il commence à fonctionner après un mode veille
  • Créez un récepteur d'amorçage Android pour le service de planification après le démarrage du périphérique.

J'ai testé le service pour toujours avec Work-Manager.

0
Khemraj

Si une intention fonctionnait déjà bien lorsque l'application est en arrière-plan, ce ne sera plus le cas depuis Android 8 ou supérieur. Ne faisant référence qu’à l’intention qui doit effectuer certains traitements lorsque l’application est en arrière-plan.

Les étapes ci-dessous doivent être suivies:

  1. L'intention susmentionnée devrait utiliser JobIntentService au lieu de IntentService.
  2. La classe qui étend JobIntentService doit implémenter la méthode - onHandleWork(@NonNull Intent intent) et doit avoir en dessous de la méthode , Qui invoquera la méthode onHandleWork:

    public static void enqueueWork(Context context, Intent work) {
        enqueueWork(context, xyz.class, 123, work);
    }
    
  3. Appelez enqueueWork(Context, intent) à partir de la classe où votre intention est définie.

    Exemple de code:

    Public class A {
    ...
    ...
        Intent intent = new Intent(Context, B.class);
        //startService(intent); 
        B.enqueueWork(Context, intent);
    }
    

La classe ci-dessous étendait auparavant la classe Service

Public Class B extends JobIntentService{
...

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

    protected void onHandleWork(@NonNull Intent intent) {
        ...
        ...
    }
}
  1. com.Android.support:support-compat est nécessaire pour JobIntentService - J'utilise 26.1.0 V.

  2. Le plus important est de vous assurer que la version des bibliothèques Firebase est au moins sur 10.2.1. J'ai eu des problèmes avec 10.2.0 - si vous en avez!

  3. Votre manifeste doit avoir l'autorisation ci-dessous pour la classe Service:

    service Android:name=".B"
    Android:exported="false"
    Android:permission="Android.permission.BIND_JOB_SERVICE"
    

J'espère que cela t'aides.

0
chitraju chaithanya

Je pense que la meilleure solution consiste à vérifier la version de la version au moment de l’exécution et, en fonction de cela, si elle est inférieure au niveau 21 de l’API, continuez à utiliser startService (). mais s'il est supérieur, vous devez utiliser un planificateur de travaux. Je suppose que vous pouvez également utiliser le gestionnaire de travaux, mais il est toujours en phase bêta.

0
Meisam