Service de premier plan tué par Android

Mise à jour : je n’ai pas trouvé de solution réelle au problème. Ce que j’ai trouvé, c’est une méthode de reconnexion automatique à un périphérique Bluetooth précédent à chaque fois que la connexion est perdue. Ce n’est pas idéal, mais cela semble fonctionner assez bien. J’aimerais entendre d’autres suggestions à ce sujet.

J’ai à peu près le même problème que dans cette question: le service est interrompu tout en maintenant le locking de veille et après avoir appelé startForeground, y compris le périphérique (Asus Transformer), wake lock, l’utilisation de startForeground () et le fait que le problème ne se produit pas si l’application est ouverte lorsque l’écran s’éteint.

Mon application maintient une connexion Bluetooth avec un autre appareil et envoie des données entre les deux, elle doit donc être active à tout moment pour écouter les données. L’utilisateur est en mesure de démarrer et d’arrêter le service à volonté et, en fait, c’est le seul moyen mis en œuvre pour démarrer ou arrêter le service. Une fois le service redémarré, la connexion Bluetooth à l’autre appareil est perdue.

Selon la réponse à la question liée, startForeground () «réduit la probabilité qu’un service soit tué, mais ne l’empêche pas». Je comprends que pour être le cas, cependant, j’ai vu de nombreux exemples d’autres applications qui n’ont pas ce problème (Tasker, par exemple).

L’utilité de mon application sera considérablement réduite sans que le service puisse fonctionner jusqu’à ce qu’il soit arrêté par l’utilisateur. Y-a-t-il un moyen d’éviter ça???

Je le vois dans mon logcat chaque fois que le service est arrêté:

ActivityManager: No longer want com.howettl.textab (pid 32321): hidden #16 WindowManager: WIN DEATH: Window{40e2d968 com.howettl.textab/com.howettl.textab.TexTab paused=false ActivityManager: Scheduling restart of crashed service com.howettl.textab/.TexTabService in 5000ms 

EDIT: Je devrais également noter que cela ne semble pas se produire sur l’autre appareil auquel je suis connecté: HTC Legend exécutant Cyanogen

EDIT: Voici le résultat des adb shell dumpsys activity services :

 * ServiceRecord{40f632e8 com.howettl.textab/.TexTabService} intent={cmp=com.howettl.textab/.TexTabService} packageName=com.howettl.textab processName=com.howettl.textab baseDir=/data/app/com.howettl.textab-1.apk resDir=/data/app/com.howettl.textab-1.apk dataDir=/data/data/com.howettl.textab app=ProcessRecord{40bb0098 2995:com.howettl.textab/10104} isForeground=true foregroundId=2 foregroundNoti=Notification(contentView=com.howettl.textab/0x1090087 vibrate=null,sound=null,defaults=0x0,flags=0x6a) createTime=-25m42s123ms lastActivity=-25m42s27ms executingStart=-25m42s27ms restartTime=-25m42s124ms startRequested=true stopIfKilled=false callStart=true lastStartId=1 Bindings: * IntentBindRecord{40a02618}: intent={cmp=com.howettl.textab/.TexTabService} binder=android.os.BinderProxy@40a9ff70 requested=true received=true hasBound=true doRebind=false * Client AppBindRecord{40a3b780 ProcessRecord{40bb0098 2995:com.howettl.textab/10104}} Per-process Connections: ConnectionRecord{40a76920 com.howettl.textab/.TexTabService:@40b998b8} All Connections: ConnectionRecord{40a76920 com.howettl.textab/.TexTabService:@40b998b8} 

Et la sortie de l’activité adb shell dumpsys activity :

 * TaskRecord{40f5c050 #23 A com.howettl.textab} numActivities=1 rootWasReset=false affinity=com.howettl.textab intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.howettl.textab/.TexTab} realActivity=com.howettl.textab/.TexTab lastActiveTime=4877757 (inactive for 702s) * Hist #1: ActivityRecord{40a776c8 com.howettl.textab/.TexTab} packageName=com.howettl.textab processName=com.howettl.textab launchedFromUid=2000 app=ProcessRecord{40bb0098 2995:com.howettl.textab/10104} Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.howettl.textab/.TexTab } frontOfTask=true task=TaskRecord{40f5c050 #23 A com.howettl.textab} taskAffinity=com.howettl.textab realActivity=com.howettl.textab/.TexTab base=/data/app/com.howettl.textab-1.apk/data/app/com.howettl.textab-1.apk data=/data/data/com.howettl.textab labelRes=0x7f060000 icon=0x7f020000 theme=0x0 stateNotNeeded=false componentSpecified=true isHomeActivity=false configuration={ scale=1.0 imsi=0/0 loc=en_CA touch=3 keys=2/1/1 nav=1/2 orien=L layout=0x10000014 uiMode=0x11 seq=6} launchFailed=false haveState=true icicle=Bundle[mParcelledData.dataSize=1644] state=STOPPED stopped=true delayedResume=false finishing=false keysPaused=false inHistory=true visible=false sleeping=true idle=true fullscreen=true noDisplay=false immersive=false launchMode=2 frozenBeforeDestroy=false thumbnailNeeded=false connections=[ConnectionRecord{40a76920 com.howettl.textab/.TexTabService:@40b998b8}] 

 Proc #15: adj=prcp /F 40e75070 959:android.process.acore/10006 (provider) com.android.providers.contacts/.ContactsProvider2<=Proc{40bb0098 2995:com.howettl.textab/10104} Proc #16: adj=bak+2/F 40bb0098 2995:com.howettl.textab/10104 (foreground-service) 

Celles-ci apparaissent pour montrer que le service est en cours d’exécution au premier plan.

Okey Dokey. Je suis passé par l’enfer et le retour sur ce problème. Voici comment procéder. Il y a des bugs. Cette publication décrit comment parsingr les bogues dans l’implémentation et contourner les problèmes.

Pour résumer, voici comment les choses sont censées fonctionner. Les services en cours d’exécution seront régulièrement récupérés et terminés toutes les 30 minutes environ. Les services qui souhaitent restr en vie plus longtemps doivent appeler Service.startForeground, qui place une notification dans la barre de notification, afin que les utilisateurs sachent que votre service est en cours d’exécution et qu’il peut potentiellement aspirer la batterie. Seuls 3 processus de service peuvent se désigner comme services de premier plan à un moment donné. S’il existe plus de trois services de premier plan, Android désignera le service le plus ancien comme candidat pour le nettoyage et la terminaison.

Malheureusement, il existe des bogues dans Android en ce qui concerne la hiérarchisation des services de premier plan, qui sont déclenchés par diverses combinaisons d’indicateurs de liaison de service. Même si vous avez correctement désigné votre service comme service de premier plan, Android peut de toute façon mettre fin à votre service si des connexions à des services de votre processus ont déjà été effectuées avec certaines combinaisons d’indicateurs de liaison. Les détails sont donnés ci-dessous.

Notez que très peu de services doivent être des services de premier plan. En règle générale, vous ne devez être qu’un service de premier plan si vous disposez d’une connexion Internet permanente ou de longue durée pouvant être activée ou désactivée, ou annulée par les utilisateurs. Exemples de services nécessitant un statut de premier plan: serveurs UPNP, téléchargements longs de fichiers très volumineux, synchronisation de systèmes de fichiers par wi-fi et lecture de musique.

Si vous ne faites qu’interroger occasionnellement ou si vous attendez des récepteurs de diffusion système ou des événements système, vous feriez mieux de réveiller votre service sur une timer ou en réponse à des récepteurs de diffusion, puis de laisser votre service mourir une fois terminé. C’est le comportement conçu pour les services. Si vous devez simplement restr en vie, lisez la suite.

Après avoir coché les cases correspondant aux exigences bien connues (par exemple, en appelant Service.startForeground), la prochaine étape consiste à rechercher les indicateurs que vous utilisez dans les appels Context.bindService. Les indicateurs utilisés pour lier affectent la priorité du processus de service cible de diverses manières inattendues. Plus particulièrement, l’utilisation de certains indicateurs de liaison peut entraîner le basculement incorrect de votre service de premier plan vers un service régulier. Le code utilisé pour atsortingbuer la priorité du processus a été fortement modifié. Notamment, il existe des révisions dans l’API 14+ qui peuvent causer des bogues lors de l’utilisation d’anciens indicateurs de liaison. et il y a des bogues précis dans 4.2.1.

Votre ami dans tout cela est l’utilitaire sysdump, qui peut être utilisé pour déterminer la priorité à laquelle le gestionnaire d’activité a atsortingbué votre processus de service, et détecter les cas où il a atsortingbué une priorité incorrecte. Faites fonctionner votre service, puis exécutez la commande suivante à partir d’une invite de commande sur votre ordinateur hôte:

processus d’activité de shell adb dumpsys> tmp.txt

Utilisez le bloc-notes (pas le wordpad / write) pour examiner le contenu.

Vérifiez d’abord que vous avez réussi à exécuter votre service dans l’état d’avant-plan. La première section du fichier dumpsys contient une description des propriétés ActivityManager pour chaque processus. Recherchez une ligne comme celle-ci qui correspond à votre application dans la première section du fichier dumpsys:

APP UID 10068 ProcessRecord {41937d40 2205: tunein.service / u0a10068}

Vérifiez que foregroundServices = true dans la section suivante. Ne vous inquiétez pas des parameters cachés et vides; ils décrivent l’état des activités dans le processus et ne semblent pas être particulièrement pertinents pour les processus avec des services en eux. Si foregroundService n’est pas vrai, vous devez appeler Service.startForeground pour le rendre vrai.

La prochaine chose à examiner est la section à la fin du fichier intitulée “Processus LRU list (sortingé par oom_adj):”. Les entrées de cette liste vous permettent de déterminer si Android a réellement classé votre application en tant que service de premier plan. Si votre processus se trouve au bas de cette liste, c’est un candidat de choix pour l’extermination sommaire. Si votre processus est en tête de liste, il est pratiquement indestructible.

Regardons une ligne dans ce tableau:

  Proc #31: adj=prcp /FS trm= 0 2205:tunein.service/u0a10068 (fg-service) 

Ceci est un exemple de service de premier plan qui a tout fait correctement. Le champ clé ici est le champ “adj =”. Cela indique la priorité de votre processus atsortingbuée par ActivityManagerService après que tout a été dit. Vous voulez qu’il soit “adj = prcp” (service de premier plan visible); ou “adj = vis” (processus visible avec une activité) ou “fore” (processus avec une activité de premier plan). Si c’est “adj = svc” (processus de service), ou “adj = svcb” (service hérité?), Ou “adj = bak” (processus d’arrière-plan vide), votre processus est un candidat potentiel pour la terminaison et sera terminé pas moins souvent que toutes les 30 minutes même s’il n’y a pas de pression pour récupérer la mémoire. Les autres indicateurs sur la ligne sont principalement des informations de débogage de diagnostic pour les ingénieurs de Google. Les décisions de résiliation sont sockets en fonction des champs adj. En bref, / FS indique un service de premier plan; / FA indique un processus de premier plan avec une activité. / B indique un service d’arrière-plan. L’étiquette à la fin indique la règle générale sous laquelle le processus s’est vu atsortingbuer une priorité. Habituellement, il doit correspondre au champ adj =; mais la valeur adj = peut être ajustée à la hausse ou à la baisse dans certains cas en raison des indicateurs de liaison sur les liaisons actives avec d’autres services ou activités.

Si vous avez déclenché un bug avec les indicateurs de liaison, la ligne dumpsys ressemblera à ceci:

  Proc #31: adj=bak /FS trm= 0 2205:tunein.service/u0a10068 (fg-service) 

Notez que la valeur du champ adj est incorrectement définie sur “adj = bak” (processus d’arrière-plan vide), ce qui signifie en gros s’il vous plaît me résilier maintenant pour que je puisse mettre fin à cette existence inutile. Notez également le drapeau (fg-service) à la fin de la ligne qui indique que “les règles du service forground ont été utilisées pour déterminer le paramètre” adj “. Malgré le fait que les règles fg-service aient été utilisées, ce paramètre a été affecté “bak”, et il ne vivra pas longtemps. En clair, c’est un bug.

Le but est donc de s’assurer que votre processus obtient toujours “adj = prcp” (ou mieux). Et la méthode pour atteindre cet objective consiste à modifier les indicateurs de liaison jusqu’à ce que vous réussissiez à éviter les bogues dans l’atsortingbution de priorité.

Voici les bugs que je connais. (1) Si TOUT service ou activité a déjà été lié au service à l’aide de Context.BIND_ABOVE_CLIENT, vous risquez de voir le paramètre adj = rétrograder à “bak” même si cette liaison n’est plus active. Cela est particulièrement vrai si vous avez également des liaisons entre services. Un bogue clair dans les sources 4.2.1. (2) N’utilisez jamais BIND_ABOVE_CLIENT pour une liaison de service à service. Ne l’utilisez pas non plus pour les connexions entre activités. L’indicateur utilisé pour implémenter le comportement BIND_ABOVE_CLIENT semble être défini par processus, au lieu d’une base par connexion, de sorte qu’il déclenche des bogues avec les liaisons de service à service même s’il n’y a pas d’activité vers service active. liaison avec le jeu de drapeaux. Il semble également y avoir des problèmes pour établir la priorité lorsque plusieurs services sont en cours, avec des liaisons de service à service. L’utilisation de Context.BIND_WAIVE_PRIORITY (API 14) sur les liaisons de service à service semble être utile. Context.BIND_IMPORTANT semble être une idée plus ou moins bonne lors de la liaison d’une activité à un service. Faire en sorte que votre processus devienne prioritaire lorsque l’activité est au premier plan, sans faire de mal apparent lorsque l’activité est suspendue ou terminée.

Mais dans l’ensemble, la stratégie consiste à ajuster vos indicateurs bindService jusqu’à ce que sysdump indique que votre processus a reçu une priorité correcte.

Pour mes besoins, en utilisant Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT pour les liaisons d’activité vers service et Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY pour les liaisons de service à service semble faire la bonne chose. Votre kilométrage peut différer.

Mon application est assez complexe: deux services d’arrière-plan, chacun pouvant détenir indépendamment des états de service de premier plan, plus un tiers pouvant également prendre l’état de service de premier plan; deux des services se lient mutuellement sous condition; le troisième se lie toujours au premier. En outre, les activités s’exécutent dans un processus distinct (ce qui facilite l’animation). L’exécution des activités et des services dans le même processus ne semblait pas faire de différence.

L’implémentation des règles de nettoyage des processus (et du code source utilisé pour générer le contenu des fichiers sysdump) se trouve dans le fichier Android de base

 frameworks\base\services\java\com\android\server\am\ActivityManagerService.java. 

Bonne chance.

PS: Voici l’interprétation des chaînes de sysdump pour Android 5.0. Je n’ai pas travaillé avec eux, alors faites-en ce que vous voulez. Je pense que vous voulez que 4 soit «A» ou «S», et que 5 soit «IF» ou «IB», et 1 soit aussi bas que possible (probablement moins de 3, puisque 3 processus de service au premier plan sont maintenus actifs) dans la configuration par défaut).

 Example: Proc # : prcp F/S/IF trm: 0 31719: neirotech.cerebrum.attention:blePrcs/u0a77 (fg-service) Format: Proc # {1}: {2} {3}/{4}/{5} trm: {6} {7}: {8}/{9} ({10} 1: Order in list: lower is less likely to get sortingmmed. 2: Not sure. 3: B: Process.THREAD_GROUP_BG_NONINTERACTIVE F: Process.THREAD_GROUP_DEFAULT 4: A: Foreground Activity S: Foreground Service ' ': Other. 5: -1: procState = "N "; ActivityManager.PROCESS_STATE_PERSISTENT: procState = "P "; ActivityManager.PROCESS_STATE_PERSISTENT_UI:procState = "PU"; ActivityManager.PROCESS_STATE_TOP: procState = "T "; ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND: procState = "IF"; ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND: procState = "IB"; ActivityManager.PROCESS_STATE_BACKUP:procState = "BU"; ActivityManager.PROCESS_STATE_HEAVY_WEIGHT: procState = "HW"; ActivityManager.PROCESS_STATE_SERVICE: procState = "S "; ActivityManager.PROCESS_STATE_RECEIVER: procState = "R "; ActivityManager.PROCESS_STATE_HOME: procState = "HO"; ActivityManager.PROCESS_STATE_LAST_ACTIVITY: procState = "LA"; ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: procState = "CA"; ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: procState = "Ca"; ActivityManager.PROCESS_STATE_CACHED_EMPTY: procState = "CE"; {6}: sortingmMemoryLevel {8} Process ID. {9} process name {10} appUid 

S’il dit “ne plus vouloir …”, ce processus n’a pas de service actif dans l’état startForeground (). Assurez-vous que votre appel a bien abouti – que vous voyez la notification affichée, il n’y a aucun message dans le journal à ce moment-là se plaignant de quoi que ce soit, etc. l’état de votre service et assurez-vous qu’il est marqué comme premier plan. De même, s’il est correctement en avant-plan, vous verrez dans la section “Activité du shell du shell adb” dans la section montrant le MOO des processus que votre processus est actuellement au premier plan en raison de ce service.