Notre application se bloque sur Android O en raison des nouvelles limites d'exécution en arrière-plan. Nous sommes sur la version 10.2.1 de Firebase, qui a ajouté le support Android O.
Cela semble être un problème avec Firebase? Ou faut-il un changement pour supporter cela de notre côté?
Java.lang.IllegalStateException: Not allowed to start service Intent { act=com.google.firebase.INSTANCE_ID_EVENT pkg=my.package.name cmp=my.package.name/my.package.name.MyFcmIdService (has extras) }: app is in background uid UidRecord{30558fa u0a327 RCVR idle procs:1 seq(0,0,0)}
at Android.app.ContextImpl.startServiceCommon(ContextImpl.Java:1505)
at Android.app.ContextImpl.startService(ContextImpl.Java:1461)
at Android.content.ContextWrapper.startService(ContextWrapper.Java:644)
at Android.support.v4.content.WakefulBroadcastReceiver.startWakefulService(WakefulBroadcastReceiver.Java:99)
at com.google.firebase.iid.zzg.b(zzg.Java:9)
at com.google.firebase.iid.zzg.a(zzg.Java:72)
at com.google.firebase.iid.zzg.a(zzg.Java:2)
at com.google.firebase.iid.FirebaseInstanceIdService.a(FirebaseInstanceIdService.Java:23)
at com.google.firebase.iid.FirebaseInstanceIdService.a(FirebaseInstanceIdService.Java:34)
at com.google.firebase.iid.FirebaseInstanceId.<init>(FirebaseInstanceId.Java:31)
at com.google.firebase.iid.FirebaseInstanceId.getInstance(FirebaseInstanceId.Java:47)
at com.google.firebase.iid.FirebaseInstanceId.a(FirebaseInstanceId.Java:4)
at com.google.firebase.iid.FirebaseInstanceIdService.a(FirebaseInstanceIdService.Java:19)
at com.google.firebase.iid.FirebaseInstanceIdService.b(FirebaseInstanceIdService.Java:35)
at com.google.firebase.iid.zzb$zza$1.run(zzb.Java:24)
at Java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.Java:1162)
at Java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.Java:636)
at Java.lang.Thread.run(Thread.Java:764)
Mise à jour La mise à niveau vers 11.4.2 résout ce problème.
@KaMyLL a raison. J'avais le même problème avec notre application et je pouvais le résoudre en remplaçant IntentService (que nous avons commencé dans onTokenRefresh()
) par un JobIntentService.
Comme j'ai trouvé les documents JobScheduler
et JobIntentService
un peu déroutants, j'aimerais tout mettre en place avec des extraits de code. J'espère que cela rend tout clair pour tout le monde ayant ce problème.
Qu'est-ce qui cause ce problème?
En raison de la nouvelle Background Execution Limits d’Android 8, vous ne devez plus lancer de services en arrière-plan lorsque l’application peut être en arrière-plan:
Lorsqu'une application est au premier plan, elle peut créer et exécuter librement des services de premier plan et d'arrière-plan. Lorsqu'une application passe en arrière-plan, elle dispose d'une fenêtre de plusieurs minutes pendant laquelle il est toujours autorisé à créer et à utiliser des services. À la fin de cette fenêtre, l'application est considérée comme étant inactive. À ce stade, le système arrête les services d'arrière-plan de l'application, comme si celle-ci avait appelé les méthodes Service.stopSelf () des services.
Et aussi:
Dans de nombreux cas, votre application peut remplacer les services d'arrière-plan par des travaux JobScheduler.
Donc, pour Android 7.x et inférieur, utiliser startService () lorsque l’application est en arrière-plan n’est (pour autant que je sache) pas de problème. Mais dans Android 8, cela provoque un crash. En conséquence, vous devriez maintenant utiliser une JobScheduler
. La différence de comportement entre JobScheduler
et IntentService
est qu'un service IntentService est exécuté immédiatement. D'autre part, un travail mis en file d'attente dans un JobScheduler n'est pas garanti pour être exécuté immédiatement. Le système d'exploitation Android déterminera le moment opportun pour le faire afin de gagner en efficacité énergétique. Donc, il pourrait y avoir un retard. Et je ne sais pas jusqu'ici combien de temps cela pourrait prendre… .. Donc, une solution pourrait être de vérifier la version du système d'exploitation et de créer une branche pour votre code en utilisant if-else. Heureusement, la bibliothèque de support nous aide à résoudre ce problème de manière plus élégante sans dupliquer le code: JobIntentService
, qui le fait essentiellement pour vous sous le capot.
Comment reproduire le problème?
La première citation ci-dessus indique que l'application still "a une fenêtre de plusieurs minutes pendant laquelle il est toujours autorisé à créer et à utiliser des services.", Donc pour pouvoir reproduire et résoudre le problème (avec l'exemple de onTokenRefresh()
dans Firebase), vous pouvez définir un point d'arrêt avant de démarrer votre service avec startService()
. Fermez l'application et attendez 5 à 10 minutes. Continuez l'exécution et vous verrez la IllegalStateException
de cette question . Pouvoir reproduire le problème comme fondamental pour vous assurer que nos solutions résolvent réellement le problème.
Comment migrer IntenService vers JobIntentService?
J'utilise FirebaseInstanceIdService.onTokenRefresh()
comme exemple:
a) Ajoutez l’autorisation BIND_JOB_SERVICE à votre service:
<service Android:name=".fcm.FcmRegistrationJobIntentService"
Android:exported="false"
Android:permission="Android.permission.BIND_JOB_SERVICE"/>
b) Au lieu d'étendre IntentService
, simplement d'étendre Android.support.v4.app.JobIntentService
, renommez la méthode onHandleIntent(Intent)
en onHandleWork(Intent)
et ajoutez une fonction pratique enqueueWork (Context, Intent):
public class FcmRegistrationJobIntentService extends JobIntentService
{
// Unique job ID for this service.
static final int JOB_ID = 42;
// Convenience method for enqueuing work in to this service.
public static void enqueueWork(Context context, Intent work) {
enqueueWork(context, FcmRegistrationJobIntentService.class, JOB_ID, work);
}
@Override
protected void onHandleWork(@NonNull Intent intent) {
// the code from IntentService.onHandleIntent() ...
}
}
c) Lancez le travail en utilisant la fonction pratique enqueueWork()
:
public class ComfyFirebaseInstanceIdService extends FirebaseInstanceIdService {
@Override
public void onTokenRefresh() {
Intent intent = new Intent(this, FcmRegistrationJobIntentService.class);
// startService(intent);
FcmRegistrationJobIntentService.enqueueWork(this, intent);
}
}
J'espère que cet exemple est utile. Au moins après avoir suivi ces étapes, je ne pouvais plus reproduire le problème sur mon appareil Android 8 et il continue de fonctionner sur mon appareil Android 7.
Mettre à jour
comme FirebaseInstanceIdService
est obsolète, nous devrions le supprimer du code et utiliser onNewToken from FirebaseMessagingService
à la place.
J'ai fait quelques recherches à ce sujet et la meilleure option est de transformer IntentService en JobIntentService disponible dans la bibliothèque d'applications. Il se comporterait comme IntentService sur tous les appareils pré-Oreo. Sur Android 8 et les versions ultérieures, le travail en file d'attente sera mis en Système Android JobScheduler . Par défaut, ce travail a défini le paramètre d'échéance sur 0; il devrait donc théoriquement être déclenché le plus rapidement possible.