Alles klar. Ich habe dieses Problem durch die Hölle und zurück durchgemacht. So gehen Sie vor. Es gibt Fehler. In diesem Beitrag wird beschrieben, wie Sie Fehler in der Implementierung analysieren und Probleme umgehen.
Um es zusammenzufassen, hier ist, wie die Dinge funktionieren sollen. Laufende Dienste werden routinemäßig alle 30 Minuten gelöscht und beendet. Dienste, die länger am Leben bleiben möchten, müssen Service.startForeground aufrufen, wodurch eine Benachrichtigung in der Benachrichtigungsleiste angezeigt wird, damit Benutzer wissen, dass Ihr Dienst permanent ausgeführt wird und möglicherweise die Akkulaufzeit beeinträchtigt. Es können sich jeweils nur 3 Serviceprozesse als Vordergrunddienste nominieren. Wenn es mehr als drei Vordergrunddienste gibt, nominiert Android den ältesten Dienst als Kandidaten für das Aufräumen und Beenden.
Leider gibt es in Android Fehler in Bezug auf die Priorisierung von Vordergrunddiensten, die durch verschiedene Kombinationen von Dienstbindungsflags ausgelöst werden. Obwohl Sie Ihren Dienst korrekt als Vordergrunddienst nominiert haben, kann Android Ihren Dienst trotzdem beenden, wenn jemals Verbindungen zu Diensten in Ihrem Prozess mit bestimmten Kombinationen von Bindungsflags hergestellt wurden. Details sind unten angegeben.
Beachten Sie, dass nur sehr wenige Dienste Vordergrunddienste sein müssen. Im Allgemeinen müssen Sie nur dann ein Vordergrunddienst sein, wenn Sie über eine ständig aktive oder lang laufende Internetverbindung verfügen, die von Benutzern ein- und ausgeschaltet oder abgebrochen werden kann. Beispiele für Dienste, die den Vordergrundstatus benötigen: UPNP-Server, lang laufende Downloads sehr großer Dateien, Synchronisierung von Dateisystemen über WLAN und Musikwiedergabe.
Wenn Sie nur gelegentlich abfragen oder auf System-Broadcast-Empfänger oder Systemereignisse warten, ist es besser, wenn Sie Ihren Dienst über einen Timer oder als Reaktion auf Broadcast-Empfänger aktivieren und Ihren Dienst nach Abschluss des Vorgangs abbrechen lassen. Das ist das geplante Verhalten für Dienste. Wenn Sie einfach am Leben bleiben müssen, lesen Sie weiter.
Nachdem Sie die Kontrollkästchen für bekannte Anforderungen aktiviert haben (z. B. Service.startForeground aufrufen), müssen Sie als Nächstes die Flags überprüfen, die Sie in Context.bindService-Aufrufen verwenden. Die zum Binden verwendeten Flags wirken sich auf verschiedene unerwartete Arten auf die Priorität des Zieldienstprozesses aus. Insbesondere die Verwendung bestimmter Bindungsflags kann dazu führen, dass Android Ihren Vordergrunddienst fälschlicherweise auf einen regulären Dienst herunterstuft. Der Code, der zum Zuweisen der Prozesspriorität verwendet wird, wurde ziemlich stark verändert. Insbesondere gibt es Revisionen in API 14+, die bei Verwendung älterer Bindungsflags Fehler verursachen können. und es gibt bestimmte Fehler in 4.2.1.
Ihr Freund in all dem ist das Dienstprogramm sysdump, mit dem Sie herausfinden können, welche Priorität der Aktivitätsmanager Ihrem Serviceprozess zugewiesen hat, und Fälle erkennen können, in denen er eine falsche Priorität zugewiesen hat. Starten Sie Ihren Dienst und geben Sie an einer Eingabeaufforderung auf Ihrem Host-Computer den folgenden Befehl ein:
Aktivitätsprozesse von adb shell dumpsys> tmp.txt
Verwenden Sie den Editor (nicht Wordpad / Write), um den Inhalt zu überprüfen.
Stellen Sie zunächst sicher, dass Sie es erfolgreich geschafft haben, Ihren Dienst im Vordergrund auszuführen. Der erste Abschnitt der dumpsys-Datei enthält eine Beschreibung der ActivityManager-Eigenschaften für jeden Prozess. Suchen Sie im ersten Abschnitt der dumpsys-Datei nach einer Zeile wie der folgenden, die Ihrer Anwendung entspricht:
APP UID 10068 ProcessRecord {41937d40 2205: tunein.service / u0a10068}
Stellen Sie im folgenden Abschnitt sicher, dass foregroundServices = true ist. Mach dir keine Sorgen über die versteckten und leeren Einstellungen; Sie beschreiben den Status der Aktivitäten im Prozess und scheinen für Prozesse mit darin enthaltenen Diensten nicht besonders relevant zu sein. Wenn foregroundService nicht wahr ist, müssen Sie Service.startForeground aufrufen, um es wahr zu machen.
Das nächste, was Sie sich ansehen müssen, ist der Abschnitt am Ende der Datei mit dem Titel "LRU-Liste verarbeiten (sortiert nach oom_adj):". Mit den Einträgen in dieser Liste können Sie feststellen, ob Android Ihre Anwendung tatsächlich als Vordergrunddienst eingestuft hat. Wenn Ihr Prozess am Ende dieser Liste steht, ist er ein Hauptkandidat für die zusammenfassende Vernichtung. Wenn Ihr Prozess ganz oben auf der Liste steht, ist er praktisch unzerstörbar.
Schauen wir uns eine Zeile in dieser Tabelle an:
Proc #31: adj=prcp /FS trm= 0 2205:tunein.service/u0a10068 (fg-service)
Dies ist ein Beispiel für einen Vordergrunddienst, der alles richtig gemacht hat. Das Schlüsselfeld hier ist das Feld "adj =". Dies gibt die Priorität an, die Ihrem Prozess vom ActivityManagerService zugewiesen wurde, nachdem alles erledigt wurde. Sie möchten, dass es "adj = prcp" ist (sichtbarer Vordergrunddienst); oder "adj = vis" (sichtbarer Prozess mit einer Aktivität) oder "fore" (Prozess mit einer Vordergrundaktivität). Wenn es sich um "adj = svc" (Serviceprozess) oder "adj = svcb" (Legacy-Service?) Oder "adj = bak" (leerer Hintergrundprozess) handelt, ist Ihr Prozess wahrscheinlich ein Kandidat für die Beendigung und wird beendet Nicht seltener als alle 30 Minuten, auch wenn kein Druck besteht, Speicher zurückzugewinnen. Die verbleibenden Flags in der Zeile sind hauptsächlich diagnostische Debug-Informationen für Google-Ingenieure. Entscheidungen über die Kündigung werden auf der Grundlage der Felder adj getroffen. Kurz gesagt, / FS zeigt einen Vordergrunddienst an; / FA zeigt einen Vordergrundprozess mit einer Aktivität an. / B zeigt einen Hintergrunddienst an. Das Etikett am Ende gibt die allgemeine Regel an, nach der dem Prozess eine Priorität zugewiesen wurde. Normalerweise sollte es mit dem Feld adj = übereinstimmen. In einigen Fällen kann der Wert adj = jedoch aufgrund von Bindungsflags für aktive Bindungen mit anderen Diensten oder Aktivitäten nach oben oder unten angepasst werden.
Wenn Sie über einen Fehler mit Bindungsflags gestolpert sind, sieht die dumpsys-Zeile folgendermaßen aus:
Proc #31: adj=bak /FS trm= 0 2205:tunein.service/u0a10068 (fg-service)
Beachten Sie, dass der Wert des Felds adj fälschlicherweise auf "adj = bak" (leerer Hintergrundprozess) gesetzt ist. Dies bedeutet ungefähr "Bitte beenden Sie mich jetzt, damit ich diese sinnlose Existenz beenden kann", um den Prozess zu bereinigen. Beachten Sie auch das Flag (fg-service) am Ende der Zeile, das angibt, dass "Forground Service Rules" verwendet wurden, um die Einstellung "adj" zu bestimmen. Trotz der Tatsache, dass fg-service Regeln verwendet wurden, wurde diesem Prozess eine adj-Einstellung zugewiesen "bak", und es wird nicht lange leben. Einfach ausgedrückt, das ist ein Fehler.
Das Ziel ist es also sicherzustellen, dass Ihr Prozess immer "adj = prcp" (oder besser) erhält. Die Methode, um dieses Ziel zu erreichen, besteht darin, die Bindungsflags zu optimieren, bis Sie Fehler in der Prioritätszuweisung vermeiden können.
Hier sind die Fehler, die ich kenne. (1) Wenn ein Dienst oder eine Aktivität jemals mit Context.BIND_ABOVE_CLIENT an den Dienst gebunden wurde, besteht das Risiko, dass die Einstellung adj = auf "bak" herabgestuft wird, auch wenn diese Bindung nicht mehr aktiv ist. Dies gilt insbesondere dann, wenn Sie auch Bindungen zwischen Diensten haben. Ein klarer Fehler in den 4.2.1-Quellen. (2) Verwenden Sie BIND_ABOVE_CLIENT auf keinen Fall für eine Service-to-Service-Bindung. Verwenden Sie es auch nicht für Aktivitäts-Service-Verbindungen. Das Flag, das zum Implementieren des Verhaltens von BIND_ABOVE_CLIENT verwendet wird, scheint pro Prozess und nicht pro Verbindung gesetzt zu sein, sodass es Fehler mit Service-zu-Service-Bindungen auslöst, selbst wenn keine Aktivität-zu-Service-Aktivität vorhanden ist Bindung mit gesetztem Flag. Es scheint auch Probleme bei der Festlegung der Priorität zu geben, wenn mehrere Dienste im Prozess vorhanden sind, mit Service-zu-Service-Bindungen. Die Verwendung von Context.BIND_WAIVE_PRIORITY (API 14) für Service-to-Service-Bindungen scheint zu helfen. Context.BIND_IMPORTANT scheint eine mehr oder weniger gute Idee zu sein, wenn eine Aktivität an einen Dienst gebunden wird. Wenn Sie dies tun, wird Ihre Prozesspriorität um eine Stufe höher, wenn die Aktivität im Vordergrund steht, ohne dass ein offensichtlicher Schaden entsteht, wenn die Aktivität angehalten oder beendet wird.
Insgesamt besteht die Strategie jedoch darin, Ihre bindService-Flags anzupassen, bis sysdump anzeigt, dass Ihr Prozess die richtige Priorität erhalten hat.
Verwenden Sie für meine Zwecke Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT für Activity-to-Service-Bindungen und Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY für Service-to-Service-Bindungen scheint das Richtige zu tun. Ihr Kilometerstand kann abweichen.
Meine App ist ziemlich komplex: zwei Hintergrunddienste, von denen jeder unabhängig voneinander Vordergrunddienstzustände enthalten kann, plus ein dritter, der auch Vordergrunddienstzustände annehmen kann; zwei der Dienste sind bedingt aneinander gebunden; der dritte bindet immer an den ersten. Darüber hinaus werden Aktivitäten in einem separaten Prozess ausgeführt (was die Animation flüssiger macht). Das Ausführen der Aktivitäten und Dienste im selben Prozess schien keinen Unterschied zu machen.
Die Implementierung der Regeln für das Aufräumen von Prozessen (und des Quellcodes, der zum Generieren des Inhalts der Sysdump-Dateien verwendet wird) finden Sie in der Android-Kerndatei
frameworks\base\services\java\com\android\server\am\ActivityManagerService.java.
Viel Glück.
PS: Hier ist die Interpretation der Sysdump-Strings für Android 5.0. Ich habe nicht mit ihnen gearbeitet, also mach aus ihnen, was du willst. Ich glaube, Sie möchten, dass 4 'A' oder 'S' und 5 "IF" oder "IB" und 1 so niedrig wie möglich ist (wahrscheinlich unter 3, da nur 3 drei Vordergrund-Service-Prozesse aktiv bleiben in der Standardkonfiguration).
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 trimmed.
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}: trimMemoryLevel
{8} Process ID.
{9} process name
{10} appUid