J'utilise Service
Class sur Android O OS.
J'ai l'intention d'utiliser le Service
en arrière-plan.
La documentation Android indique que
Si votre application cible l'API de niveau 26 ou supérieur, le système impose des restrictions sur l'utilisation ou la création de services d'arrière-plan, sauf si l'application elle-même est au premier plan. Si une application doit créer un service de premier plan, elle doit appeler
startForegroundService()
.
Si vous utilisez startForegroundService()
, le Service
jette l'erreur suivante.
Context.startForegroundService() did not then call
Service.startForeground()
Quel est le problème avec ça?
Réponses:
À partir des documents de Google sur les changements de comportement d'Android 8.0 :
Solution: Appel
startForeground()
àonCreate()
laService
que vous utilisezContext.startForegroundService()
Voir aussi: Limites d'exécution en arrière - plan pour Android 8.0 (Oreo)
la source
onStartCommand
méthode, mais j'obtiens toujours cette erreur. J'ai appeléstartForegroundService(intent)
monMainActivity
. Peut-être que le service est démarré trop lentement. Je pense que la limite de cinq secondes ne devrait pas exister avant de pouvoir promettre que le service est démarré immédiatement.onStartCommand()
lieu deonCreate
, parce que si vous fermez le service et le redémarrez, il pourrait passeronStartCommand()
sans appeleronCreate
...J'ai appelé
ContextCompat.startForegroundService(this, intent)
pour démarrer le serviceEn service
onCreate
la source
onCreate
dans 5 secondes. Ils devraient donc le repenser avant de nous forcer à suivre les règles.StopService
après avoir appeléstartForegroundService
.Ce problème se produit parce que le framework Android ne peut pas garantir que votre service démarre dans les 5 secondes mais d'autre part, le framework a une limite stricte sur la notification au premier plan doit être déclenchée dans les 5 secondes, sans vérifier si le framework a essayé de démarrer le service .
Il s'agit certainement d'un problème de cadre, mais tous les développeurs confrontés à ce problème ne font pas de leur mieux:
startForeground
une notification doit être dans les deuxonCreate
etonStartCommand
, parce que si votre service est déjà créé et que votre activité tente de le redémarrer,onCreate
il ne sera pas appelé.l'ID de notification ne doit pas être 0 sinon le même crash se produira même si ce n'est pas la même raison.
stopSelf
ne doit pas être appelé avantstartForeground
.Avec tous les 3 ci-dessus, ce problème peut être réduit un peu mais toujours pas un correctif, le vrai correctif ou disons que la solution consiste à rétrograder votre version de SDK cible à 25.
Et notez que très probablement Android P portera toujours ce problème parce que Google refuse même de comprendre ce qui se passe et ne pense pas que ce soit leur faute, lisez # 36 et # 56 pour plus d'informations
la source
startForeground
. Le changer en 1 résout le problème pour moi (j'espère)Je sais que trop de réponses ont déjà été publiées, mais la vérité est que startForegroundService ne peut pas être corrigé au niveau d'une application et vous devez cesser de l'utiliser. Cette recommandation de Google d'utiliser l'API Service # startForeground () dans les 5 secondes après l'appel de Context # startForegroundService () n'est pas quelque chose qu'une application peut toujours faire.
Android exécute de nombreux processus simultanément et il n'y a aucune garantie que Looper appellera votre service cible qui est censé appeler startForeground () dans les 5 secondes. Si votre service cible n'a pas reçu l'appel dans les 5 secondes, vous n'avez pas de chance et vos utilisateurs connaîtront une situation ANR. Dans votre trace de pile, vous verrez quelque chose comme ceci:
Si je comprends bien, Looper a analysé la file d'attente ici, a trouvé un "agresseur" et l'a simplement tué. Le système est heureux et sain maintenant, alors que les développeurs et les utilisateurs ne le sont pas, mais puisque Google limite leurs responsabilités au système, pourquoi devraient-ils se soucier des deux derniers? Apparemment non. Pourraient-ils faire mieux? Bien sûr, par exemple, ils auraient pu ouvrir la boîte de dialogue "L'application est occupée", demandant à un utilisateur de décider d'attendre ou de tuer l'application, mais pourquoi, ce n'est pas de sa responsabilité. L'essentiel est que le système soit sain maintenant.
D'après mes observations, cela se produit relativement rarement, dans mon cas, environ 1 crash en un mois pour les utilisateurs 1K. La reproduire est impossible, et même si elle est reproduite, vous ne pouvez rien faire pour la réparer définitivement.
Il y avait une bonne suggestion dans ce fil pour utiliser "bind" au lieu de "start" et puis quand le service est prêt, traiter onServiceConnected, mais encore une fois, cela signifie ne pas utiliser du tout les appels startForegroundService.
Je pense que la bonne et honnête action de la part de Google serait de dire à tout le monde que startForegourndServcie a une déficience et ne doit pas être utilisé.
La question demeure: quoi utiliser à la place? Heureusement pour nous, il existe maintenant JobScheduler et JobService, qui sont une meilleure alternative pour les services de premier plan. C'est une meilleure option, à cause de cela:
Cela signifie que vous n'avez plus besoin de vous soucier de la manipulation des verrous de réveil et c'est pourquoi ce n'est pas différent des services de premier plan. Du point de vue de l'implémentation, JobScheduler n'est pas votre service, c'est celui d'un système, il gèrera probablement la file d'attente correctement, et Google ne terminera jamais son propre enfant :)
Samsung est passé de startForegroundService à JobScheduler et JobService dans leur Samsung Accessory Protocol (SAP). C'est très utile lorsque des appareils comme les montres connectées doivent parler à des hôtes comme les téléphones, où le travail doit interagir avec un utilisateur via le thread principal d'une application. Étant donné que les tâches sont publiées par le planificateur dans le thread principal, cela devient possible. Vous devez cependant vous rappeler que le travail s'exécute sur le thread principal et décharger toutes les tâches lourdes sur d'autres threads et tâches asynchrones.
Le seul écueil du passage à JobScheduler / JobService est que vous devrez refactoriser l'ancien code, et ce n'est pas amusant. J'ai passé les deux derniers jours à faire exactement cela pour utiliser la nouvelle implémentation SAP de Samsung. Je regarderai mes rapports de plantage et je vous ferai savoir si je reverrai les plantage. Théoriquement, cela ne devrait pas se produire, mais il y a toujours des détails dont nous ne sommes peut-être pas conscients.
MISE À JOUR Plus de plantages signalés par le Play Store. Cela signifie que JobScheduler / JobService n'ont pas un tel problème et le passage à ce modèle est la bonne approche pour se débarrasser du problème startForegroundService une fois pour toujours. J'espère que Google / Android le lit et finira par commenter / conseiller / fournir un guide officiel pour tout le monde.
MISE À JOUR 2
Pour ceux qui utilisent SAP et qui demandent comment SAP V2 utilise JobService, l'explication est ci-dessous.
Dans votre code personnalisé, vous devrez initialiser SAP (c'est Kotlin):
Vous devez maintenant décompiler le code de Samsung pour voir ce qui se passe à l'intérieur. Dans SAAgentV2, jetez un œil à l'implémentation de requestAgent et à la ligne suivante:
Accédez maintenant à la classe SAAdapter et recherchez la fonction onServiceConnectionRequested qui planifie un travail à l'aide de l'appel suivant:
SAJobService n'est qu'une implémentation d'Android'd JobService et c'est celle qui fait la planification des travaux:
Comme vous le voyez, la dernière ligne ici utilise Android'd JobScheduler pour obtenir ce service système et planifier un travail.
Dans l'appel requestAgent, nous avons passé mAgentCallback, qui est une fonction de rappel qui recevra le contrôle lorsqu'un événement important se produit. Voici comment le rappel est défini dans mon application:
MessageJobs ici est une classe que j'ai implémentée pour traiter toutes les demandes provenant d'une smartwatch Samsung. Ce n'est pas le code complet, seulement un squelette:
Comme vous le voyez, MessageJobs nécessite également la classe MessageSocket que vous devez implémenter et qui traite tous les messages provenant de votre appareil.
En bout de ligne, ce n'est pas si simple et cela nécessite quelques recherches sur les internes et le codage, mais cela fonctionne, et surtout - il ne plante pas.
la source
JobIntentService
s'exécute immédiatement enIntentService
dessous d'Oreo, mais planifie un travail sur et au-dessus d'Oreo, doncJobIntentService
ne démarre pas immédiatement. Plus d'infosJob...
prend du temps pour démarrer et c'est une version avancée deAlarmManager
.Votre application se bloque si vous appelez
Context.startForegroundService(...)
, puis appelezContext.stopService(...)
avantService.startForeground(...)
est appelé.J'ai une repro claire ici ForegroundServiceAPI26
J'ai ouvert un bug à ce sujet sur: Google issue tracker
Plusieurs bugs sur ce sujet ont été ouverts et fermés ne seront pas corrigés.
J'espère que le mien avec des étapes de reproduction claires fera la coupe.
Informations fournies par l'équipe Google
Outil de suivi des problèmes de Google Commentaire 36
Ce n'est pas un bug de framework; c'est intentionnel. Si l'application démarre une instance de service avec
startForegroundService()
, elle doit passer cette instance de service à l'état de premier plan et afficher la notification. Si l'instance de service est arrêtée avantstartForeground()
être appelée dessus, cette promesse n'est pas tenue: il s'agit d'un bogue dans l'application.Concernant # 31 , publier un service que d'autres applications peuvent démarrer directement est fondamentalement dangereux. Vous pouvez atténuer cela un peu en traitant toutes les actions de démarrage de ce service comme nécessitant
startForeground()
, bien que ce ne soit évidemment pas ce que vous aviez en tête.Outil de suivi des problèmes de Google Commentaire 56
Il y a quelques scénarios différents qui mènent au même résultat ici.
Le problème purement sémantique, que c'est simplement une erreur de lancer quelque chose,
startForegroundService()
mais néglige de le faire passer au premier plan viastartForeground()
, n'est que cela: un problème sémantique. C'est traité comme un bug d'application, intentionnellement. L'arrêt du service avant de le faire passer au premier plan est une erreur d'application. C'était le nœud du PO, et c'est pourquoi ce problème a été marqué «fonctionnant comme prévu».Cependant, il existe également des questions sur la détection parasite de ce problème. C'est est en cours de traitement comme un véritable problème, bien que son être suivi séparément de cette question de bug tracker. Nous ne sommes pas sourds à la plainte.
la source
J'ai fait des recherches à ce sujet pendant quelques jours et j'ai trouvé la solution. Maintenant, dans Android O, vous pouvez définir la limitation d'arrière-plan comme ci-dessous
Le service qui appelle une classe de service
et la classe de service devrait être comme
la source
J'ai un widget qui fait des mises à jour relativement fréquentes lorsque l'appareil est réveillé et je voyais des milliers de plantages en quelques jours seulement.
Le déclencheur du problème
J'ai même remarqué le problème même sur mon Pixel 3 XL alors que je n'aurais pas pensé que l'appareil aurait beaucoup de charge. Et tous les chemins de code étaient couverts
startForeground()
. Mais j'ai réalisé que dans de nombreux cas, mon service fait le travail très rapidement.Je crois que le déclencheur de mon application était que le service se terminait avant que le système ne parvienne à afficher une notification.La solution de contournement / solution
J'ai pu me débarrasser de tous les accidents. J'ai supprimé l'appel
stopSelf()
. (Je pensais à retarder l'arrêt jusqu'à ce que je sois presque sûr que la notification a été affichée, mais je ne veux pas que l'utilisateur voit la notification si elle n'est pas nécessaire.) Lorsque le service est inactif depuis une minute ou que le système le détruit normalement sans lancer d'exceptions.la source
Juste un avertissement car j'ai perdu trop d'heures à ce sujet. J'ai continué à recevoir cette exception même si j'appelais
startForeground(..)
en premieronCreate(..)
. En fin de compte, j'ai constaté que le problème était dû à l'utilisationNOTIFICATION_ID = 0
. L'utilisation de toute autre valeur semble résoudre ce problème.la source
J'ai un travail autour de ce problème. J'ai vérifié ce correctif dans ma propre application (300K + DAU), ce qui peut réduire au moins 95% de ce type de plantage, mais ne peut toujours pas éviter à 100% ce problème.
Ce problème se produit même lorsque vous vous assurez d'appeler startForeground () juste après le démarrage du service, comme indiqué par Google. Cela peut être dû au fait que le processus de création et d'initialisation de service coûte déjà plus de 5 secondes dans de nombreux scénarios, alors peu importe quand et où vous appelez la méthode startForeground (), ce plantage est inévitable.
Ma solution consiste à garantir que startForeground () sera exécuté dans les 5 secondes après la méthode startForegroundService (), quelle que soit la durée de création et d'initialisation de votre service. Voici la solution détaillée.
N'utilisez pas startForegroundService en premier lieu, utilisez bindService () avec l'indicateur auto_create. Il attendra l'initialisation du service. Voici le code, mon exemple de service est MusicService:
Alors voici l'implémentation de MusicBinder:
La partie la plus importante, l'implémentation de MusicService, la méthode forceForeground () garantira que la méthode startForeground () est appelée juste après startForegroundService ():
Si vous souhaitez exécuter l'extrait de code de l'étape 1 dans une intention en attente, par exemple si vous souhaitez démarrer un service de premier plan dans un widget (un clic sur le bouton du widget) sans ouvrir votre application, vous pouvez envelopper l'extrait de code dans un récepteur de diffusion. et déclenchez un événement de diffusion au lieu de la commande start service.
C'est tout. J'espère que ça aide. Bonne chance.
la source
Cette erreur se produit également sur Android 8+ lorsque Service.startForeground (int id, Notification notification) est appelé alors que id est défini sur 0.
la source
Étant donné que tous ceux qui visitent ici souffrent de la même chose, je veux partager ma solution que personne d'autre n'a essayée auparavant (dans cette question de toute façon). Je peux vous assurer que cela fonctionne, même sur un point d'arrêt arrêté qui confirme cette méthode.
Le problème est d'appeler à
Service.startForeground(id, notification)
partir du service lui-même, non? Android Framework ne garantit malheureusement pas d'appelerService.startForeground(id, notification)
dans lesService.onCreate()
5 secondes mais lève l'exception quand même, donc j'ai trouvé cette façon.Context.startForegroundService()
Context.startForegroundService()
partir de la connexion de service et appelez immédiatementService.startForeground()
à l'intérieur de la connexion de service.Context.bindService()
méthode dans un try-catch, car dans certains cas, l'appel peut lever une exception, auquel cas vous devez vous fier à l'appelContext.startForegroundService()
direct et espérer qu'il n'échouera pas. Un exemple peut être un contexte de récepteur de diffusion, mais l'obtention du contexte d'application ne lève pas d'exception dans ce cas, mais l'utilisation directe du contexte le fait.Cela fonctionne même lorsque j'attends un point d'arrêt après avoir lié le service et avant de déclencher l'appel "startForeground". Attendre entre 3 et 4 secondes ne déclenche pas l'exception tandis qu'au bout de 5 secondes, il lève l'exception. (Si l'appareil ne peut pas exécuter deux lignes de code en 5 secondes, il est temps de le jeter à la poubelle.)
Commencez donc par créer une connexion de service.
Dans votre service, créez un classeur qui renvoie l'instance de votre service.
Ensuite, essayez de lier le service à partir de ce contexte. S'il réussit, il appellera la
ServiceConnection.onServiceConnected()
méthode à partir de la connexion de service que vous utilisez. Ensuite, gérez la logique du code illustré ci-dessus. Un exemple de code ressemblerait à ceci:Il semble fonctionner sur les applications que je développe, donc ça devrait marcher quand vous essayez aussi.
la source
Problème avec Android O API 26
Si vous arrêtez le service immédiatement (de sorte que votre service ne fonctionne pas vraiment (formulation / compréhension) et que vous êtes bien en dessous de l'intervalle ANR, vous devez toujours appeler startForeground avant stopSelf
https://plus.google.com/116630648530850689477/posts/L2rn4T6SAJ5
J'ai essayé cette approche, mais cela crée toujours une erreur: -
J'utilise ceci jusqu'à ce que l'erreur soit résolue
la source
Je suis confronté au même problème et après avoir passé du temps à trouver des solutions, vous pouvez essayer le code ci-dessous. Si vous utilisez
Service
alors mettez ce code dans onCreate sinon votre utilisationIntent Service
mettez alors ce code dans onHandleIntent.la source
J'ai fait des recherches sur ce problème et c'est ce que j'ai découvert jusqu'à présent. Ce plantage pourrait se produire si nous avons un code similaire à ceci:
MyForegroundService.java
MainActivity.java
L'exception est levée dans le bloc suivant du code:
ActiveServices.java
Cette méthode est exécutée avant
onCreate()
deMyForegroundService
parce que les horaires Android la création du service sur le principal gestionnaire de threads , maisbringDownServiceLocked
est appelé unBinderThread
, Wich est une condition de course. Cela signifie que vousMyForegroundService
n'avez pas eu la possibilité d'appeler,startForeground
ce qui entraînera le crash.Pour résoudre ce problème, nous devons nous assurer que ce
bringDownServiceLocked
n'est pas appelé avantonCreate()
deMyForegroundService
.En utilisant des émissions collantes, nous nous assurons que l'émission ne se perd pas et
stopReceiver
reçoit l'intention d'arrêt dès qu'elle a été enregistrée dansonCreate()
ofMyForegroundService
. À ce moment-là, nous avons déjà appeléstartForeground(...)
. Nous devons également supprimer cette diffusion collante pour éviter que stopReceiver ne soit averti la prochaine fois.Veuillez noter que la méthode
sendStickyBroadcast
est obsolète et je l'utilise uniquement comme solution de contournement temporaire pour résoudre ce problème.la source
Tant de réponses mais aucune n'a fonctionné dans mon cas.
J'ai commencé un service comme celui-ci.
Et à mon service dans onStartCommand
Et n'oubliez pas de mettre NOTIFICATION_ID non nul
Donc, tout était parfait mais plantait toujours sur 8.1, donc la cause était comme ci-dessous.
J'ai appelé arrêter le premier plan avec supprimer la notification, mais une fois que le service de notification a été supprimé, le service d'arrière-plan ne peut pas fonctionner en arrière-plan. commencé après la réception du push.
Donc, le mot magique est
Jusqu'à présent, quelle que soit la raison de la panne de votre service, suivez toutes les étapes ci-dessus et profitez-en.
la source
startForeground
être appeléonCreate
?https://developer.android.com/reference/android/content/Context.html#startForegroundService(android.content.Intent)
assurez-vous d'appeler le
Service.startForeground(int, android.app.Notification)
onCreate () afin de vous assurer qu'il sera appelé .. si vous avez une condition qui peut vous empêcher de le faire, alors vous feriez mieux d'utiliser la normaleContext.startService(Intent)
et d'appelerService.startForeground(int, android.app.Notification)
vous - même.Il semble que l'
Context.startForegroundService()
ajoute un chien de garde pour s'assurer que vous avez appeléService.startForeground(int, android.app.Notification)
avant qu'il ne soit détruit ...la source
Veuillez ne pas appeler de StartForgroundServices dans la méthode onCreate () , vous devez appeler les services StartForground dans onStartCommand () après avoir créé le thread de travail, sinon vous obtiendrez toujours ANR, donc s'il vous plaît n'écrivez pas de connexion complexe dans le thread principal de onStartCommand () ;
EDIT: Attention! La fonction startForeground ne peut pas prendre 0 comme premier argument, elle lèvera une exception! cet exemple contient un mauvais appel de fonction, remplacez 0 par votre propre const qui ne peut pas être 0 ou être supérieur à Max (Int32)
la source
Même après avoir appelé l'
startForeground
enService
, il se bloque sur certains appareils si l' on appellestopService
juste avant que l'onCreate
on appelle. J'ai donc résolu ce problème en démarrant le service avec un indicateur supplémentaire:et ajouté une vérification dans onStartCommand pour voir si elle a commencé à s'arrêter:
PS Si le service n'était pas en cours d'exécution, il démarrerait le service en premier, ce qui est une surcharge.
la source
Vous devez ajouter une autorisation en tant que ci-dessous pour l'appareil Android 9 lorsque vous utilisez le SDK cible 28 ou ultérieur, sinon l'exception se produira toujours:
la source
Je vérifie simplement le
PendingIntent
null ou non avant d'appeler lacontext.startForegroundService(service_intent)
fonction.ça marche pour moi
la source
il suffit d'appeler la méthode startForeground immédiatement après la création de Service ou IntentService. comme ça:
la source
Ok, quelque chose que j'ai remarqué à ce sujet qui pourrait aussi en aider quelques autres. Il s'agit strictement de tests pour voir si je pouvais comprendre comment corriger les occurrences que je vois. Par souci de simplicité, disons que j'ai une méthode qui appelle cela du présentateur.
Cela plantera avec la même erreur. Le service ne démarrera PAS tant que la méthode n'est pas terminée, donc pas
onCreate()
dans le service.Donc, même si vous mettez à jour l'interface utilisateur hors du thread principal, SI vous avez quelque chose qui pourrait retarder cette méthode après, elle ne démarrera pas à temps et vous donnera l'erreur redoutée de premier plan. Dans mon cas, nous étions en train de charger certaines choses dans une file d'attente et chacun a appelé
startForegroundService
, mais une logique était impliquée avec chacun en arrière-plan. Donc, si la logique a pris trop de temps pour terminer cette méthode puisqu'elles ont été rappelées, le temps de plantage. L'ancien l'astartService
simplement ignoré et a continué son chemin et puisque nous l'avons appelé à chaque fois, le prochain tour allait se terminer.Cela m'a laissé me demander, si j'appelais le service à partir d'un thread en arrière-plan, s'il ne pouvait pas être entièrement lié au démarrage et s'exécuter immédiatement, j'ai donc commencé à expérimenter. Même si cela ne démarre pas immédiatement, il ne se bloque pas.
Je ne prétendrai pas savoir pourquoi il ne plante pas bien que je soupçonne que cela l'oblige à attendre que le thread principal puisse le gérer en temps opportun. Je sais que ce n'est pas idéal pour le lier au thread principal, mais puisque mon utilisation l'appelle en arrière-plan, je ne suis pas vraiment inquiet s'il attend jusqu'à ce qu'il puisse terminer plutôt que de planter.
la source
J'ajoute du code dans la réponse @humazed. Il n'y a donc pas de notification initiale. Ce pourrait être une solution de contournement, mais cela fonctionne pour moi.
J'ajoute transparentColor en petite icône et couleur sur notification. Ça va marcher.
la source
J'ai résolu le problème avec le démarrage du service avec
startService(intent)
au lieu deContext.startForeground()
et en appelantstartForegound()
immédiatement aprèssuper.OnCreate()
. De plus, si vous démarrez le service au démarrage, vous pouvez démarrer l'activité qui démarre le service lors de la diffusion de démarrage. Bien que ce ne soit pas une solution permanente, cela fonctionne.la source
Mise à jour des données dans
onStartCommand(...)
onBind (...)
onBind(...)
est un meilleur événement de cycle de vie à initier parstartForeground
rapport àonCreate(...)
parce queonBind(...)
passe dans unIntent
qui peut contenir des données importantes dans leBundle
nécessaire pour initialiser leService
. Cependant, il n'est pas nécessaire car ilonStartCommand(...)
est appelé lorsque leService
est créé pour la première fois ou appelé par la suite.onStartCommand (...)
startForeground
enonStartCommand(...)
est important afin de mettre à jour leService
une fois qu'il a déjà été créé.Quand
ContextCompat.startForegroundService(...)
est appelé après laService
création d'unonBind(...)
etonCreate(...)
n'est pas appelé. Par conséquent, les données mises à jour peuvent être transmisesonStartCommand(...)
via leIntent
Bundle
pour mettre à jour les données dans leService
.Échantillon
J'utilise ce modèle pour mettre en œuvre la
PlayerNotificationManager
dans la Coinverse application de nouvelles crypto-monnaie.Activity / Fragment.kt
AudioService.kt
la source
Un service
Testeur
Résultat
la source