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"
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 .
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
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());
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.
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.
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.
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()
}
}
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.
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'
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 );
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.
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
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 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 _ .
J'ai testé le service pour toujours avec Work-Manager.
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:
JobIntentService
au lieu de IntentService
.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);
}
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) {
...
...
}
}
com.Android.support:support-compat
est nécessaire pour JobIntentService
- J'utilise 26.1.0 V
.
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!
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.
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.