Context.startForegroundService () hat dann Service.startForeground () nicht aufgerufen.


258

Ich verwende ServiceClass unter Android O OS.

Ich habe vor, das Serviceim Hintergrund zu verwenden.

Die Android-Dokumentation besagt dies

Wenn Ihre App auf API-Level 26 oder höher abzielt, legt das System Einschränkungen für die Verwendung oder Erstellung von Hintergrunddiensten fest, es sei denn, die App selbst steht im Vordergrund. Wenn eine App einen Vordergrunddienst erstellen muss, sollte die App aufrufen startForegroundService().

Wenn Sie verwenden startForegroundService(), wird Serviceder folgende Fehler ausgegeben.

Context.startForegroundService() did not then call
Service.startForeground() 

Was ist daran falsch?


IOW, bitte geben Sie ein minimal reproduzierbares Beispiel an . Dies würde den gesamten Java-Stack-Trace und den Code einschließen, der den Absturz auslöst.
CommonsWare

3
Der Fehler noch hier in API 26 und 27 (27.0.3). Betroffene Android-Versionen sind 8.0 und 8.1. Sie können die Anzahl der Abstürze verringern, indem Sie startForeground () sowohl zu onCreate () als auch zu onStartCommand () hinzufügen. Die Abstürze treten jedoch bei einigen Benutzern weiterhin auf. Die einzige Möglichkeit, das Problem zu beheben, ist targetSdkVersion 25 in Ihrem build.gradle.
Alex


1
Ich benutze diese Problemumgehung jetzt. funktioniert wie Charme theandroiddeveloper.com/blog/…
Prasad Pawar

1
Ich habe das gleiche Problem. Ich behebe dieses Problem. Ich teilte meine Implementierung in diesem Thema stackoverflow.com/questions/55894636/…
Beyazid

Antworten:


99

In den Google-Dokumenten zu Android 8.0 werden Verhaltensänderungen vorgenommen :

Das System ermöglicht es Apps, Context.startForegroundService () aufzurufen, auch wenn sich die App im Hintergrund befindet. Die App muss jedoch die startForeground () -Methode dieses Dienstes innerhalb von fünf Sekunden nach dem Erstellen des Dienstes aufrufen.

Lösung: Rufen Sie startForeground()in onCreate()für die , Servicedie Sie verwendenContext.startForegroundService()

Siehe auch: Hintergrundausführungsbeschränkungen für Android 8.0 (Oreo)


19
Ich habe dies in der onStartCommandMethode getan , aber ich erhalte immer noch diesen Fehler. Ich rief startForegroundService(intent)in meinem MainActivity. Möglicherweise wird der Dienst zu langsam gestartet. Ich denke, das Fünf-Sekunden-Limit sollte nicht existieren, bevor sie versprechen können, dass der Dienst sofort gestartet wird.
Kimi Chiu

29
5 Sekunden sind definitiv nicht genug, diese Ausnahme tritt sehr oft in Debug-Sitzungen auf. Ich vermute, dass es auch von Zeit zu Zeit im Release-Modus passieren würde. Und als FATAL EXCEPTION stürzt die App einfach ab! Ich habe versucht, es mit Thread.setDefaultUncaughtExceptionHandler () zu fangen, aber selbst wenn ich es dort bekomme und Android ignoriere, friert die Anwendung ein. Dies liegt daran, dass diese Ausnahme von handleMessage () ausgelöst wird und die Hauptschleife effektiv endet ... auf der Suche nach einer Problemumgehung.
Southton

Ich habe die Context.startForegroundService () -Methode im Broadcast-Empfänger aufgerufen. Wie soll man in dieser Situation damit umgehen? weil onCreate () nicht im Rundfunkempfänger verfügbar ist
Anand Savjani

6
@southerton Ich denke, Sie sollten es onStartCommand()anstelle von aufrufen onCreate, denn wenn Sie den Dienst schließen und erneut starten, kann es gehen, onStartCommand()ohne anzurufen onCreate...
Android-Entwickler

1
Wir können die Antwort des Google-Teams hier überprüfen issuetracker.google.com/issues/76112072#comment56
Prags

82

Ich habe dann angerufen ContextCompat.startForegroundService(this, intent), um den Dienst zu starten

Im Dienst onCreate

 @Override
 public void onCreate() {
        super.onCreate();

        if (Build.VERSION.SDK_INT >= 26) {
            String CHANNEL_ID = "my_channel_01";
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                    "Channel human readable title",
                    NotificationManager.IMPORTANCE_DEFAULT);

            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);

            Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                    .setContentTitle("")
                    .setContentText("").build();

            startForeground(1, notification);
        }
}

47
Ich auch. Aber ich bekomme immer noch gelegentlich diesen Fehler. Vielleicht kann das Android nicht garantieren, dass es das onCreatein 5 Sekunden aufruft. Deshalb sollten sie es neu gestalten, bevor sie uns zwingen, die Regeln zu befolgen.
Kimi Chiu

5
Ich habe startForeground in onStartCommand () aufgerufen und gelegentlich diesen Fehler erhalten. Ich habe es auf onCreate verschoben und seitdem nicht mehr gesehen (Daumen drücken).
Tyler

4
Dadurch wird in Oreo eine lokale Benachrichtigung mit der Meldung "APP_NAME wird ausgeführt. Tippen Sie hier, um zu schließen oder Informationen anzuzeigen" erstellt. Wie kann ich aufhören, diese Benachrichtigung anzuzeigen?
Artist404

6
Nein, das Android-Team hat behauptet, dies sei ein beabsichtigtes Verhalten. Also habe ich gerade meine App neu gestaltet. Das ist lächerlich. Aufgrund dieses "beabsichtigten Verhaltens" müssen Apps immer neu gestaltet werden. Es ist das dritte Mal.
Kimi Chiu

12
Die Hauptursache für dieses Problem ist, dass der Dienst gestoppt wurde, bevor er in den Vordergrund gestellt wurde. Aber die Behauptung hörte nicht auf, nachdem der Dienst zerstört wurde. Sie können versuchen, dies zu reproduzieren, indem Sie es StopServicenach dem Aufruf hinzufügen startForegroundService.
Kimi Chiu

55

Dieses Problem tritt auf, weil das Android-Framework nicht garantieren kann, dass Ihr Dienst innerhalb von 5 Sekunden gestartet wird. Andererseits muss die Vordergrundbenachrichtigung innerhalb von 5 Sekunden ausgelöst werden, ohne zu überprüfen, ob das Framework versucht hat, den Dienst zu starten .

Dies ist definitiv ein Framework-Problem, aber nicht alle Entwickler, die mit diesem Problem konfrontiert sind, geben ihr Bestes:

  1. startForegroundEine Benachrichtigung muss in beiden enthalten sein onCreateund wird nicht aufgerufen onStartCommand, wenn Ihr Dienst bereits erstellt wurde und Ihre Aktivität irgendwie versucht, ihn erneut zu starten onCreate.

  2. Die Benachrichtigungs-ID darf nicht 0 sein, da sonst derselbe Absturz auftritt, auch wenn dies nicht der gleiche Grund ist.

  3. stopSelfdarf vorher nicht aufgerufen werden startForeground.

Mit allen oben genannten 3 kann dieses Problem ein wenig reduziert werden, aber immer noch kein Fix. Der eigentliche Fix oder die Problemumgehung besteht darin, die SDK-Zielversion auf 25 herunterzustufen.

Beachten Sie auch, dass Android P dieses Problem höchstwahrscheinlich weiterhin aufweist, da Google nicht einmal versteht, was vor sich geht, und nicht glaubt, dass dies ihre Schuld ist. Weitere Informationen finden Sie in den Abschnitten 36 und 56


9
Warum sowohl onCreate als auch onStartCommand? Können Sie es einfach in onStartCommand einfügen?
rcell

stopSelf darf nicht vor startForeground => oder direkt danach aufgerufen werden, da es den "startForeground" zu löschen scheint, wenn Sie dies tun.
Frank

1
Ich habe einige Tests ausgeführt, und selbst wenn onCreate nicht aufgerufen wird, wenn der Dienst bereits erstellt wurde, müssen Sie startForeground nicht erneut aufrufen. Daher können Sie es nur für die onCreate-Methode und nicht für den onStartCommand aufrufen. Wahrscheinlich betrachten sie die einzelne Instanz des Dienstes nach dem ersten Aufruf bis zu seinem Ende als im Vordergrund.
Lxu

Ich verwende 0 als Benachrichtigungs-ID für den ersten Anruf für startForeground. Das Ändern auf 1 behebt das Problem für mich (hoffentlich)
mr5

Was ist die Lösung?
IgorGanapolsky

35

Ich weiß, es wurden bereits zu viele Antworten veröffentlicht, aber die Wahrheit ist - startForegroundService kann nicht auf App-Ebene repariert werden und Sie sollten es nicht mehr verwenden. Die Empfehlung von Google, die API Service # startForeground () innerhalb von 5 Sekunden nach dem Aufruf von Context # startForegroundService () zu verwenden, kann eine App nicht immer ausführen.

Android führt viele Prozesse gleichzeitig aus und es gibt keine Garantie dafür, dass Looper Ihren Zieldienst aufruft, der startForeground () innerhalb von 5 Sekunden aufrufen soll. Wenn Ihr Zieldienst den Anruf nicht innerhalb von 5 Sekunden erhalten hat, haben Sie kein Glück und Ihre Benutzer werden eine ANR-Situation erleben. In Ihrer Stapelverfolgung sehen Sie ungefähr Folgendes:

Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{1946947 u0 ...MessageService}

main" prio=5 tid=1 Native
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x763e01d8 self=0x7d77814c00
  | sysTid=11171 nice=-10 cgrp=default sched=0/0 handle=0x7dfe411560
  | state=S schedstat=( 1337466614 103021380 2047 ) utm=106 stm=27 core=0 HZ=100
  | stack=0x7fd522f000-0x7fd5231000 stackSize=8MB
  | held mutexes=
  #00  pc 00000000000712e0  /system/lib64/libc.so (__epoll_pwait+8)
  #01  pc 00000000000141c0  /system/lib64/libutils.so (android::Looper::pollInner(int)+144)
  #02  pc 000000000001408c  /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+60)
  #03  pc 000000000012c0d4  /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+44)
  at android.os.MessageQueue.nativePollOnce (MessageQueue.java)
  at android.os.MessageQueue.next (MessageQueue.java:326)
  at android.os.Looper.loop (Looper.java:181)
  at android.app.ActivityThread.main (ActivityThread.java:6981)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1445)

Soweit ich weiß, hat Looper die Warteschlange hier analysiert, einen "Missbraucher" gefunden und sie einfach getötet. Das System ist jetzt glücklich und gesund, Entwickler und Nutzer jedoch nicht. Da Google jedoch seine Verantwortlichkeiten auf das System beschränkt, warum sollten sie sich für die beiden letzteren interessieren? Anscheinend nicht. Könnten sie es besser machen? Natürlich hätten sie beispielsweise den Dialog "Anwendung ist beschäftigt" bedienen und einen Benutzer bitten können, eine Entscheidung über das Warten oder Beenden der App zu treffen, aber warum sich die Mühe machen, liegt nicht in ihrer Verantwortung. Die Hauptsache ist, dass das System jetzt gesund ist.

Nach meinen Beobachtungen kommt dies relativ selten vor, in meinem Fall ungefähr 1 Absturz pro Monat für 1K-Benutzer. Es ist unmöglich, es zu reproduzieren, und selbst wenn es reproduziert wird, können Sie nichts tun, um es dauerhaft zu reparieren.

In diesem Thread gab es einen guten Vorschlag, "bind" anstelle von "start" zu verwenden und dann, wenn der Dienst bereit ist, onServiceConnected zu verarbeiten. Dies bedeutet jedoch wiederum, dass startForegroundService-Aufrufe überhaupt nicht verwendet werden.

Ich denke, die richtige und ehrliche Aktion von Google-Seite wäre, allen zu sagen, dass startForegourndServcie einen Mangel hat und nicht verwendet werden sollte.

Die Frage bleibt weiterhin: Was soll stattdessen verwendet werden? Zum Glück gibt es jetzt JobScheduler und JobService, die eine bessere Alternative für Vordergrunddienste darstellen. Aus diesem Grund ist es eine bessere Option:

Während ein Job ausgeführt wird, hält das System im Namen Ihrer App ein Wakelock. Aus diesem Grund müssen Sie keine Maßnahmen ergreifen, um sicherzustellen, dass das Gerät für die Dauer des Auftrags wach bleibt.

Es bedeutet, dass Sie sich nicht mehr um den Umgang mit Wakelocks kümmern müssen, und deshalb unterscheidet es sich nicht von Vordergrunddiensten. Aus Sicht der Implementierung ist JobScheduler nicht Ihr Dienst, sondern ein System, vermutlich wird es die Warteschlange richtig behandeln und Google wird niemals sein eigenes Kind beenden :)

Samsung hat in seinem Samsung Accessory Protocol (SAP) von startForegroundService auf JobScheduler und JobService umgestellt. Dies ist sehr hilfreich, wenn Geräte wie Smartwatches mit Hosts wie Telefonen kommunizieren müssen, bei denen der Job über den Hauptthread einer App mit einem Benutzer interagieren muss. Da die Jobs vom Scheduler an den Hauptthread gesendet werden, wird dies möglich. Sie sollten sich jedoch daran erinnern, dass der Job auf dem Hauptthread ausgeführt wird, und alle schweren Inhalte auf andere Threads und asynchrone Aufgaben auslagern.

Dieser Dienst führt jeden eingehenden Job auf einem Handler aus, der im Hauptthread Ihrer Anwendung ausgeführt wird. Dies bedeutet, dass Sie Ihre Ausführungslogik auf einen anderen Thread / Handler / AsyncTask Ihrer Wahl auslagern müssen

Die einzige Gefahr beim Wechsel zu JobScheduler / JobService besteht darin, dass Sie alten Code umgestalten müssen, und es macht keinen Spaß. Ich habe die letzten zwei Tage damit verbracht, die SAP-Implementierung des neuen Samsung zu nutzen. Ich werde meine Absturzberichte ansehen und Sie wissen lassen, ob die Abstürze erneut auftreten. Theoretisch sollte es nicht passieren, aber es gibt immer Details, die uns möglicherweise nicht bekannt sind.

UPDATE Keine Abstürze mehr vom Play Store gemeldet. Dies bedeutet, dass JobScheduler / JobService kein solches Problem haben und der Wechsel zu diesem Modell der richtige Ansatz ist, um das Problem startForegroundService ein für alle Mal zu beseitigen. Ich hoffe, Google / Android liest es und wird irgendwann eine offizielle Anleitung für alle kommentieren / beraten / bereitstellen.

UPDATE 2

Für diejenigen, die SAP verwenden und fragen, wie SAP V2 JobService verwendet, finden Sie unten eine Erklärung.

In Ihrem benutzerdefinierten Code müssen Sie SAP initialisieren (es ist Kotlin):

SAAgentV2.requestAgent(App.app?.applicationContext, 
   MessageJobs::class.java!!.getName(), mAgentCallback)

Jetzt müssen Sie den Samsung-Code dekompilieren, um zu sehen, was im Inneren vor sich geht. Sehen Sie sich in SAAgentV2 die Implementierung von requestAgent und die folgende Zeile an:

SAAgentV2.d var3 = new SAAgentV2.d(var0, var1, var2);

where d defined as below

private SAAdapter d;

Wechseln Sie jetzt zur SAAdapter-Klasse und suchen Sie die Funktion onServiceConnectionRequested, mit der ein Job mithilfe des folgenden Aufrufs geplant wird:

SAJobService.scheduleSCJob(SAAdapter.this.d, var11, var14, var3, var12); 

SAJobService ist nur eine Implementierung von Android'd JobService und dies ist diejenige, die eine Jobplanung durchführt:

private static void a(Context var0, String var1, String var2, long var3, String var5, SAPeerAgent var6) {
    ComponentName var7 = new ComponentName(var0, SAJobService.class);
    Builder var10;
    (var10 = new Builder(a++, var7)).setOverrideDeadline(3000L);
    PersistableBundle var8;
    (var8 = new PersistableBundle()).putString("action", var1);
    var8.putString("agentImplclass", var2);
    var8.putLong("transactionId", var3);
    var8.putString("agentId", var5);
    if (var6 == null) {
        var8.putStringArray("peerAgent", (String[])null);
    } else {
        List var9;
        String[] var11 = new String[(var9 = var6.d()).size()];
        var11 = (String[])var9.toArray(var11);
        var8.putStringArray("peerAgent", var11);
    }

    var10.setExtras(var8);
    ((JobScheduler)var0.getSystemService("jobscheduler")).schedule(var10.build());
}

Wie Sie sehen, verwendet die letzte Zeile hier Android'd JobScheduler, um diesen Systemdienst abzurufen und einen Job zu planen.

Im requestAgent-Aufruf haben wir mAgentCallback übergeben, eine Rückruffunktion, die die Kontrolle erhält, wenn ein wichtiges Ereignis eintritt. So wird der Rückruf in meiner App definiert:

private val mAgentCallback = object : SAAgentV2.RequestAgentCallback {
    override fun onAgentAvailable(agent: SAAgentV2) {
        mMessageService = agent as? MessageJobs
        App.d(Accounts.TAG, "Agent " + agent)
    }

    override fun onError(errorCode: Int, message: String) {
        App.d(Accounts.TAG, "Agent initialization error: $errorCode. ErrorMsg: $message")
    }
}

MessageJobs hier ist eine Klasse, die ich implementiert habe, um alle Anfragen von einer Samsung-Smartwatch zu verarbeiten. Es ist nicht der vollständige Code, nur ein Skelett:

class MessageJobs (context:Context) : SAAgentV2(SERVICETAG, context, MessageSocket::class.java) {


    public fun release () {

    }


    override fun onServiceConnectionResponse(p0: SAPeerAgent?, p1: SASocket?, p2: Int) {
        super.onServiceConnectionResponse(p0, p1, p2)
        App.d(TAG, "conn resp " + p1?.javaClass?.name + p2)


    }

    override fun onAuthenticationResponse(p0: SAPeerAgent?, p1: SAAuthenticationToken?, p2: Int) {
        super.onAuthenticationResponse(p0, p1, p2)
        App.d(TAG, "Auth " + p1.toString())

    }


    override protected fun onServiceConnectionRequested(agent: SAPeerAgent) {


        }
    }

    override fun onFindPeerAgentsResponse(peerAgents: Array<SAPeerAgent>?, result: Int) {
    }

    override fun onError(peerAgent: SAPeerAgent?, errorMessage: String?, errorCode: Int) {
        super.onError(peerAgent, errorMessage, errorCode)
    }

    override fun onPeerAgentsUpdated(peerAgents: Array<SAPeerAgent>?, result: Int) {

    }

}

Wie Sie sehen, erfordert MessageJobs auch die MessageSocket-Klasse, die Sie implementieren müssten, und die alle von Ihrem Gerät kommenden Nachrichten verarbeitet.

Unterm Strich ist es nicht so einfach und erfordert einige Eingriffe in Interna und Codierung, aber es funktioniert und vor allem - es stürzt nicht ab.


Gute Antwort, aber es gibt ein großes Problem, JobIntentServicedas sofort IntentServiceunter Oreo ausgeführt wird, aber einen Job auf und über Oreo plant, also JobIntentServicenicht sofort startet. Weitere Informationen
CopsOnRoad

1
@CopsOnRoad Es ist sehr nützlich und startet in meinem Fall sofort. Wie ich schrieb, wird es für Echtzeit-Interaktionen zwischen Telefon und Smartwatch verwendet. Auf keinen Fall würde ein Benutzer 15 Minuten warten, um Daten zwischen diesen beiden zu senden. Funktioniert sehr gut und stürzt nie ab.
Oleg Gryb

1
Klingt cool, würde Ihre Antwort sehr wert machen, wenn Sie einen Beispielcode teilen können. Ich bin neu in Android und habe festgestellt, dass Job...der Start einige Zeit in Anspruch nimmt und es sich um eine Vorabversion von handelt AlarmManager.
CopsOnRoad

2
Wahrscheinlich werde ich es tun, wenn es die Zeit erlaubt: Ich muss es von benutzerdefiniertem Code
entkoppeln

1
@batmaci - JobService wird von SAP intern verwendet. Weitere Informationen finden Sie unter Update 2 in OP.
Oleg Gryb

32

Ihre App stürzt ab, wenn Sie anrufen Context.startForegroundService(...)und dann anrufen, Context.stopService(...)bevor Service.startForeground(...)aufgerufen wird.

Ich habe hier einen klaren Repro ForegroundServiceAPI26

Ich habe einen Fehler in diesem Bereich geöffnet: Google Issue Tracker

Mehrere Fehler wurden geöffnet und geschlossen. Wird nicht behoben.

Hoffentlich schaffen meine mit klaren Repro-Schritten den Schnitt.

Informationen vom Google-Team

Google Issue Tracker Kommentar 36

Dies ist kein Framework-Fehler. es ist beabsichtigt. Wenn die App eine Dienstinstanz mit startet startForegroundService(), muss sie diese Dienstinstanz in den Vordergrundzustand versetzen und die Benachrichtigung anzeigen. Wenn die Dienstinstanz gestoppt wird, bevor sie startForeground()aufgerufen wird, wird dieses Versprechen nicht erfüllt: Dies ist ein Fehler in der App.

Zu # 31 ist die Veröffentlichung eines Dienstes, den andere Apps direkt starten können, grundsätzlich unsicher. Sie können dies ein wenig abmildern, indem Sie alle Startaktionen dieses Dienstes als erforderlich behandeln startForeground(), obwohl dies offensichtlich möglicherweise nicht das ist, was Sie sich vorgestellt haben.

Google Issue Tracker Kommentar 56

Es gibt verschiedene Szenarien, die hier zum gleichen Ergebnis führen.

Das geradezu semantische Problem, dass es einfach ein Fehler ist, etwas anzufangen, startForegroundService()aber es zu vernachlässigen, es tatsächlich über in den Vordergrund zu stellen startForeground(), ist genau das: ein semantisches Problem. Das wird absichtlich als App-Fehler behandelt. Das Beenden des Dienstes vor dem Übergang in den Vordergrund ist ein App-Fehler. Das war der Kern des OP, und deshalb wurde dieses Problem als "wie beabsichtigt funktionieren" gekennzeichnet.

Es gibt jedoch auch Fragen zur falschen Erkennung dieses Problems. Das ist , wird wird als ein echtes Problem behandelt, obwohl es getrennt von dieser Bug - Tracker Ausgabe verfolgt. Wir sind nicht taub für die Beschwerde.


2
Das beste Update, das ich gesehen habe, ist issuetracker.google.com/issues/76112072#comment56 . Ich habe meinen Code neu geschrieben, um Context.bindService zu verwenden, wodurch der problematische Aufruf von Context.startForegroundService vollständig vermieden wird. Sie können meinen Beispielcode unter github.com/paulpv/ForegroundServiceAPI26/tree/bound/app/src/…
swooby

Ja, wie ich schrieb, sollte Binden funktionieren, aber ich habe bereits zu JobServive gewechselt und werde nichts ändern, es sei denn, auch dieses wird kaputt gehen ':)
Oleg Gryb

20

Ich habe ein paar Tage lang darüber recherchiert und die Lösung gefunden. Jetzt können Sie in Android O die Hintergrundbeschränkung wie folgt einstellen

Der Dienst, der eine Dienstklasse aufruft

Intent serviceIntent = new Intent(SettingActivity.this,DetectedService.class);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
    SettingActivity.this.startForegroundService(serviceIntent);
} else {
    startService(serviceIntent);
}

und die Serviceklasse sollte wie sein

public class DetectedService extends Service { 
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        int NOTIFICATION_ID = (int) (System.currentTimeMillis()%10000);
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForeground(NOTIFICATION_ID, new Notification.Builder(this).build());
        }


        // Do whatever you want to do here
    }
}

3
Haben Sie es in einer App versucht, die mindestens mehrere tausend Benutzer hat? Ich habe versucht, einige Benutzer haben immer noch die Absturzprobleme.
Alex

Nein, nein, ich verwende den genauen Code und sehe keinen Absturz der Clients.
Ahmad Arslan

Wie viele Benutzer hast du? Ich habe täglich ~ 10-30 + Abstürze (mit dem Fehler) in einer App gesehen, die 10.000 Benutzer pro Tag hatte. Natürlich passiert es nur für Benutzer mit Android 8.0 und Android 8.1, falls targetSdkVersion 27 ist. Alle Probleme verschwinden auf 0 Abstürze, gleich nachdem ich targetSdkVersion auf 25 gesetzt habe.
Alex

nur 2200 Benutzer: - / aber keinen Absturz bekommen
Ahmad Arslan

1
@ Jeeva, nicht wirklich. Atm, ich benutze targetSdkVersion 25 mit compileSdkVersion 27. Es sieht nach vielen Experimenten am besten aus .... Ich hoffe, sie werden developer.android.com/topic/libraries/architecture/… vor August 2018 fertigstellen , weil android-developer.googleblog .com / 2017/12 /…
Alex

16

Ich habe ein Widget, das relativ häufig aktualisiert, wenn das Gerät wach ist, und ich habe in nur wenigen Tagen Tausende von Abstürzen gesehen.

Der Problemauslöser

Ich habe das Problem sogar bei meinem Pixel 3 XL bemerkt, als ich nicht gedacht hätte, dass das Gerät überhaupt viel Last hat. Und alle Codepfade wurden mit abgedeckt startForeground(). Aber dann wurde mir klar, dass mein Service in vielen Fällen die Arbeit sehr schnell erledigt. Ich glaube, der Auslöser für meine App war, dass der Dienst beendet wurde, bevor das System tatsächlich eine Benachrichtigung zeigte.

Die Problemumgehung / Lösung

Ich konnte alle Abstürze loswerden. Was ich getan habe, war den Anruf zu entfernen stopSelf(). (Ich habe darüber nachgedacht, den Stopp zu verzögern, bis ich ziemlich sicher war, dass die Benachrichtigung angezeigt wurde, aber ich möchte nicht, dass der Benutzer die Benachrichtigung sieht, wenn dies nicht erforderlich ist.) Wenn der Dienst eine Minute lang inaktiv war oder das System zerstört es normalerweise ohne Ausnahmen.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    stopForeground(true);
} else {
    stopSelf();
}

11

Nur ein Kopf hoch, da ich viel zu viele Stunden damit verschwendet habe. Ich bekam diese Ausnahme immer wieder, obwohl ich startForeground(..)als erstes anrief onCreate(..). Am Ende stellte ich fest, dass das Problem durch die Verwendung verursacht wurde NOTIFICATION_ID = 0. Die Verwendung eines anderen Werts scheint dies zu beheben.


In meinem Fall habe ich ID 1 verwendet, danke, das Problem wurde behoben
Amin Pinjari

4
Ich verwende die ID 101, erhalte diesen Fehler jedoch manchmal.
CopsOnRoad

9

Ich habe eine Lösung für dieses Problem. Ich habe dieses Update in meiner eigenen App (300K + DAU) überprüft, wodurch mindestens 95% dieser Art von Absturz reduziert werden können, dieses Problem jedoch immer noch nicht zu 100% vermieden werden kann.

Dieses Problem tritt auch dann auf, wenn Sie sicherstellen, dass startForeground () direkt nach dem Start des Dienstes aufgerufen wird, wie von Google dokumentiert. Dies kann daran liegen, dass der Prozess zur Erstellung und Initialisierung von Diensten in vielen Szenarien bereits mehr als 5 Sekunden dauert. Unabhängig davon, wann und wo Sie die Methode startForeground () aufrufen, ist dieser Absturz unvermeidbar.

Meine Lösung besteht darin, sicherzustellen, dass startForeground () innerhalb von 5 Sekunden nach der Methode startForegroundService () ausgeführt wird, unabhängig davon, wie lange Ihr Dienst erstellt und initialisiert werden muss. Hier ist die detaillierte Lösung.

  1. Verwenden Sie startForegroundService nicht an erster Stelle, sondern bindService () mit dem Flag auto_create. Es wird auf die Dienstinitialisierung warten. Hier ist der Code, mein Beispieldienst ist MusicService:

    final Context applicationContext = context.getApplicationContext();
    Intent intent = new Intent(context, MusicService.class);
    applicationContext.bindService(intent, new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            if (binder instanceof MusicBinder) {
                MusicBinder musicBinder = (MusicBinder) binder;
                MusicService service = musicBinder.getService();
                if (service != null) {
                    // start a command such as music play or pause.
                    service.startCommand(command);
                    // force the service to run in foreground here.
                    // the service is already initialized when bind and auto_create.
                    service.forceForeground();
                }
            }
            applicationContext.unbindService(this);
        }
    
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    }, Context.BIND_AUTO_CREATE);
  2. Dann ist hier die MusicBinder-Implementierung:

    /**
     * Use weak reference to avoid binder service leak.
     */
     public class MusicBinder extends Binder {
    
         private WeakReference<MusicService> weakService;
    
         /**
          * Inject service instance to weak reference.
          */
         public void onBind(MusicService service) {
             this.weakService = new WeakReference<>(service);
         }
    
         public MusicService getService() {
             return weakService == null ? null : weakService.get();
         }
     }
  3. Der wichtigste Teil, die MusicService-Implementierung, die forceForeground () -Methode, stellt sicher, dass die startForeground () -Methode direkt nach startForegroundService () aufgerufen wird:

    public class MusicService extends MediaBrowserServiceCompat {
    ...
        private final MusicBinder musicBind = new MusicBinder();
    ...
        @Override
        public IBinder onBind(Intent intent) {
            musicBind.onBind(this);
            return musicBind;
        }
    ...
        public void forceForeground() {
            // API lower than 26 do not need this work around.
            if (Build.VERSION.SDK_INT >= 26) {
                Intent intent = new Intent(this, MusicService.class);
                // service has already been initialized.
                // startForeground method should be called within 5 seconds.
                ContextCompat.startForegroundService(this, intent);
                Notification notification = mNotificationHandler.createNotification(this);
                // call startForeground just after startForegroundService.
                startForeground(Constants.NOTIFICATION_ID, notification);
            }
        }
    }
  4. Wenn Sie das Code-Snippet von Schritt 1 in einer ausstehenden Absicht ausführen möchten, z. B. wenn Sie einen Vordergrunddienst in einem Widget starten möchten (Klicken auf die Widget-Schaltfläche), ohne Ihre App zu öffnen, können Sie das Code-Snippet in einen Broadcast-Empfänger einbinden und ein Broadcast-Ereignis auslösen, anstatt den Dienstbefehl zu starten.

Das ist alles. Ich hoffe es hilft. Viel Glück.



7

Da jeder, der hierher kommt, das gleiche leidet, möchte ich meine Lösung teilen, die noch niemand zuvor versucht hat (in dieser Frage jedenfalls). Ich kann Ihnen versichern, dass es funktioniert, auch an einem gestoppten Haltepunkt, der diese Methode bestätigt.

Das Problem ist, Service.startForeground(id, notification)vom Dienst selbst anzurufen , oder? Android Framework garantiert leider nicht, Service.startForeground(id, notification)innerhalb Service.onCreate()von 5 Sekunden anzurufen, löst aber trotzdem die Ausnahme aus, also habe ich mir diesen Weg ausgedacht.

  1. Binden Sie den Dienst vor dem Aufruf an einen Kontext mit einem Ordner aus dem Dienst Context.startForegroundService()
  2. Wenn die Bindung erfolgreich ist, rufen Sie Context.startForegroundService() über die Dienstverbindung an und rufen Sie sofort Service.startForeground() innerhalb der Dienstverbindung an.
  3. WICHTIGER HINWEIS: Rufen Sie die Context.bindService()Methode in einem Try-Catch auf, da der Aufruf in einigen Fällen eine Ausnahme auslösen kann. In diesem Fall müssen Sie sich auf den Context.startForegroundService()direkten Aufruf verlassen und hoffen, dass er nicht fehlschlägt. Ein Beispiel kann ein Broadcast-Empfängerkontext sein. Das Abrufen des Anwendungskontexts löst in diesem Fall jedoch keine Ausnahme aus, die direkte Verwendung des Kontexts jedoch.

Dies funktioniert sogar, wenn ich nach dem Binden des Dienstes und vor dem Auslösen des Aufrufs "startForeground" auf einen Haltepunkt warte. Warten zwischen 3-4 Sekunden löst die Ausnahme nicht aus, während sie nach 5 Sekunden die Ausnahme auslöst. (Wenn das Gerät nicht in 5 Sekunden zwei Codezeilen ausführen kann, ist es Zeit, diese in den Papierkorb zu werfen.)

Beginnen Sie also mit dem Erstellen einer Dienstverbindung.

// Create the service connection.
ServiceConnection connection = new ServiceConnection()
{
    @Override
    public void onServiceConnected(ComponentName name, IBinder service)
    {
        // The binder of the service that returns the instance that is created.
        MyService.LocalBinder binder = (MyService.LocalBinder) service;

        // The getter method to acquire the service.
        MyService myService = binder.getService();

        // getServiceIntent(context) returns the relative service intent 
        context.startForegroundService(getServiceIntent(context));

        // This is the key: Without waiting Android Framework to call this method
        // inside Service.onCreate(), immediately call here to post the notification.
        myService.startForeground(myNotificationId, MyService.getNotification());

        // Release the connection to prevent leaks.
        context.unbindService(this);
    }

    @Override
    public void onBindingDied(ComponentName name)
    {
        Log.w(TAG, "Binding has dead.");
    }

    @Override
    public void onNullBinding(ComponentName name)
    {
        Log.w(TAG, "Bind was null.");
    }

    @Override
    public void onServiceDisconnected(ComponentName name)
    {
        Log.w(TAG, "Service is disconnected..");
    }
};

Erstellen Sie in Ihrem Dienst einen Ordner, der die Instanz Ihres Dienstes zurückgibt.

public class MyService extends Service
{
    public class LocalBinder extends Binder
    {
        public MyService getService()
        {
            return MyService.this;
        }
    }

    // Create the instance on the service.
    private final LocalBinder binder = new LocalBinder();

    // Return this instance from onBind method.
    // You may also return new LocalBinder() which is
    // basically the same thing.
    @Nullable
    @Override
    public IBinder onBind(Intent intent)
    {
        return binder;
    }
}

Dann versuchen , den Dienst zu binden , aus diesem Zusammenhang. Wenn dies erfolgreich ist, wird die ServiceConnection.onServiceConnected()Methode von der von Ihnen verwendeten Dienstverbindung aufgerufen . Behandeln Sie dann die Logik in dem oben gezeigten Code. Ein Beispielcode würde folgendermaßen aussehen:

// Try to bind the service
try
{
     context.bindService(getServiceIntent(context), connection,
                    Context.BIND_AUTO_CREATE);
}
catch (RuntimeException ignored)
{
     // This is probably a broadcast receiver context even though we are calling getApplicationContext().
     // Just call startForegroundService instead since we cannot bind a service to a
     // broadcast receiver context. The service also have to call startForeground in
     // this case.
     context.startForegroundService(getServiceIntent(context));
}

Es scheint an den Anwendungen zu arbeiten, die ich entwickle, also sollte es auch funktionieren, wenn Sie es versuchen.


5

Problem mit Android O API 26

Wenn Sie den Dienst sofort beenden (damit Ihr Dienst nicht wirklich ausgeführt wird (Wortlaut / Verständnis) und Sie sich weit unter dem ANR-Intervall befinden, müssen Sie vor stopSelf immer noch startForeground aufrufen

https://plus.google.com/116630648530850689477/posts/L2rn4T6SAJ5

Versuchte diesen Ansatz, aber es entsteht immer noch ein Fehler: -

if (Util.SDK_INT > 26) {
    mContext.startForegroundService(playIntent);
} else {
    mContext.startService(playIntent);
}

Ich benutze dies, bis der Fehler behoben ist

mContext.startService(playIntent);

3
Sollte Util.SDK_INT> = 26 sein, nicht nur größer
Andris

4

Ich stehe vor dem gleichen Problem und nachdem ich Zeit damit verbracht habe, eine Lösung zu finden, können Sie den folgenden Code ausprobieren. Wenn Sie verwenden Service, geben Sie Intent Servicediesen Code in onCreate ein. Andernfalls verwenden Sie diesen Code in onHandleIntent.

if (Build.VERSION.SDK_INT >= 26) {
        String CHANNEL_ID = "my_app";
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                "MyApp", NotificationManager.IMPORTANCE_DEFAULT);
        ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
        Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle("")
                .setContentText("").build();
        startForeground(1, notification);
    }

4

Ich habe dieses Problem untersucht und dies ist, was ich bisher entdeckt habe. Dieser Absturz kann passieren, wenn wir einen ähnlichen Code haben:

MyForegroundService.java

public class MyForegroundService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
        startForeground(...);
    }
}

MainActivity.java

Intent serviceIntent = new Intent(this, MyForegroundService.class);
startForegroundService(serviceIntent);
...
stopService(serviceIntent);

Die Ausnahme wird im folgenden Block des Codes ausgelöst:

ActiveServices.java

private final void bringDownServiceLocked(ServiceRecord r) {
    ...
    if (r.fgRequired) {
        Slog.w(TAG_SERVICE, "Bringing down service while still waiting for start foreground: "
                  + r);
        r.fgRequired = false;
        r.fgWaiting = false;
        mAm.mAppOpsService.finishOperation(AppOpsManager.getToken(mAm.mAppOpsService),
                    AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
        mAm.mHandler.removeMessages(
                    ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
        if (r.app != null) {
            Message msg = mAm.mHandler.obtainMessage(
                ActivityManagerService.SERVICE_FOREGROUND_CRASH_MSG);
            msg.obj = r.app;
            msg.getData().putCharSequence(
                ActivityManagerService.SERVICE_RECORD_KEY, r.toString());
            mAm.mHandler.sendMessage(msg);
         }
    }
    ...
}

Diese Methode wird vor der Ausführung onCreate()von MyForegroundServiceda Android Plänen der Erstellung des Dienstes auf den Haupt - Thread - Handler , sondern bringDownServiceLockedauf einem genannt BinderThread, Weicht eine Race - Bedingung ist. Es bedeutet, dass MyForegroundServicewir keine Chance hatten anzurufen, startForegroundwas den Absturz verursachen würde.

Um dies zu beheben, müssen wir sicherstellen, dass dies bringDownServiceLockednicht vorher onCreate()von aufgerufen wird MyForegroundService.

public class MyForegroundService extends Service {

    private static final String ACTION_STOP = "com.example.MyForegroundService.ACTION_STOP";

    private final BroadcastReceiver stopReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            context.removeStickyBroadcast(intent);
            stopForeground(true);
            stopSelf();
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        startForeground(...);
        registerReceiver(
            stopReceiver, new IntentFilter(ACTION_STOP));
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(stopReceiver);
    }

    public static void stop(Context context) {
        context.sendStickyBroadcast(new Intent(ACTION_STOP));
    }
}

Durch die Verwendung von klebrigen Sendungen stellen wir sicher , dass die Sendung erhält nicht verloren und stopReceivererhält den Anschlag Absicht, sobald sie registriert wurde onCreate()von MyForegroundService. Zu diesem Zeitpunkt haben wir bereits angerufen startForeground(...). Wir müssen diese klebrige Sendung auch entfernen, um zu verhindern, dass stopReceiver das nächste Mal benachrichtigt wird.

Bitte beachten Sie, dass die Methode sendStickyBroadcastveraltet ist und ich sie nur als vorübergehende Problemumgehung verwende, um dieses Problem zu beheben.


Sie sollten es anstelle von context.stopService (serviceIntent) aufrufen, wenn Sie den Dienst beenden möchten.
Makovkastar

4

So viele Antworten, aber keine haben in meinem Fall funktioniert.

Ich habe den Dienst so begonnen.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    startForegroundService(intent);
} else {
    startService(intent);
}

Und in meinem Dienst in onStartCommand

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID)
                .setContentTitle(getString(R.string.app_name))
                .setContentText("SmartTracker Running")
                .setAutoCancel(true);
        Notification notification = builder.build();
        startForeground(NOTIFICATION_ID, notification);
    } else {
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                .setContentTitle(getString(R.string.app_name))
                .setContentText("SmartTracker is Running...")
                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                .setAutoCancel(true);
        Notification notification = builder.build();
        startForeground(NOTIFICATION_ID, notification);
    }

Und vergessen Sie nicht, NOTIFICATION_ID ungleich Null zu setzen

private static final String ANDROID_CHANNEL_ID = "com.xxxx.Location.Channel";
private static final int NOTIFICATION_ID = 555;

Also war alles perfekt, stürzte aber immer noch auf 8.1 ab, also war die Ursache wie unten.

     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            stopForeground(true);
        } else {
            stopForeground(true);
        }

Ich habe Stop Vordergrund mit Benachrichtigung entfernen aufgerufen, aber sobald die Benachrichtigung entfernt wurde, wird der Dienst zum Hintergrund und der Hintergrunddienst kann nicht in Android O aus dem Hintergrund ausgeführt werden. gestartet nach Push erhalten.

So magisch ist das Wort

   stopSelf();

Befolgen Sie alle oben genannten Schritte und genießen Sie jeden Grund, warum Ihr Dienst abstürzt.


if (Build.VERSION.SDK_INT> = Build.VERSION_CODES.O) {stopForeground (true); } else {stopForeground (true); } Was ist das, wenn sonst? In beiden Fällen haben Sie dasselbe getan: stopForeground (true);
user3135923

Ich glaube, du wolltest stopSelf () setzen; innerhalb des else-Blocks anstelle von stopForeground (true);
Justin Stanley

1
Ja, @JustinStanley verwendet stopSelf ();
Sand

Sollte nicht startForegroundangerufen werden onCreate?
Aba

Ich verwende NotificationManager und nicht die Notification-Klasse. Wie kann ich das umsetzen?
Prajwal W

3

https://developer.android.com/reference/android/content/Context.html#startForegroundService(android.content.Intent)

Ähnlich wie startService (Intent), jedoch mit dem impliziten Versprechen, dass der Service startForeground (int, android.app.Notification) aufruft, sobald er ausgeführt wird. Der Dienst erhält dafür eine Zeit, die mit dem ANR-Intervall vergleichbar ist. Andernfalls stoppt das System den Dienst automatisch und deklariert die App-ANR.

Im Gegensatz zum normalen startService (Intent) kann diese Methode jederzeit verwendet werden, unabhängig davon, ob sich die App, die den Dienst hostet, im Vordergrund befindet.

Service.startForeground(int, android.app.Notification)Stellen Sie sicher, dass Sie onCreate () aufrufen, damit es aufgerufen wird. Wenn Sie eine Bedingung haben, die Sie möglicherweise daran hindert, sollten Sie das normale verwenden Context.startService(Intent)und sich Service.startForeground(int, android.app.Notification)selbst aufrufen .

Es scheint, dass der Context.startForegroundService()einen Wachhund hinzufügt, um sicherzustellen, dass Sie den angerufen haben, Service.startForeground(int, android.app.Notification)bevor er zerstört wurde ...


3

Bitte rufen Sie keine StartForgroundServices innerhalb der onCreate () -Methode auf. Sie müssen die StartForground-Dienste in onStartCommand () aufrufen, nachdem Sie den Arbeitsthread erstellt haben. Andernfalls erhalten Sie immer ANR. Schreiben Sie daher keine komplexe Anmeldung in den Hauptthread von onStartCommand (). ;;

public class Services extends Service {

    private static final String ANDROID_CHANNEL_ID = "com.xxxx.Location.Channel";
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID)
                    .setContentTitle(getString(R.string.app_name))
                    .setContentText("SmartTracker Running")
                    .setAutoCancel(true);
            Notification notification = builder.build();
            startForeground(0, notification);
            Log.e("home_button","home button");
        } else {
            NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                    .setContentTitle(getString(R.string.app_name))
                    .setContentText("SmartTracker is Running...")
                    .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                    .setAutoCancel(true);
            Notification notification = builder.build();
            startForeground(0, notification);
            Log.e("home_button_value","home_button_value");

        }
        return super.onStartCommand(intent, flags, startId);

    }
}

EDIT: Achtung! Die Funktion startForeground kann nicht 0 als erstes Argument verwenden, sondern löst eine Ausnahme aus! Dieses Beispiel enthält einen falschen Funktionsaufruf. Ändern Sie 0 in Ihre eigene Konstante, die nicht 0 oder größer als Max (Int32) sein kann.


2

Auch nach dem Aufruf des startForegroundIn Servicestürzt es auf einigen Geräten ab, wenn wir stopServicekurz vor dem Aufruf anrufen onCreate. Daher habe ich dieses Problem behoben, indem ich den Dienst mit einem zusätzlichen Flag gestartet habe:

Intent intent = new Intent(context, YourService.class);
intent.putExtra("request_stop", true);
context.startService(intent);

und fügte einen Check in onStartCommand hinzu, um zu sehen, ob es gestoppt wurde:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    //call startForeground first
    if (intent != null) {
        boolean stopService = intent.getBooleanExtra("request_stop", false);
        if (stopService) {
            stopSelf();
        }
    }

    //Continue with the background task
    return START_STICKY;
}

PS Wenn der Dienst nicht ausgeführt würde, würde er zuerst den Dienst starten, was ein Overhead ist.


2

Sie müssen eine Berechtigung als Balg für Android 9-Gerät hinzufügen, wenn Sie Ziel SDK 28 oder höher verwenden. Andernfalls tritt immer die Ausnahme auf:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

1

Ich überprüfe nur die PendingIntentNull oder nicht, bevor ich die anrufe context.startForegroundService(service_intent) Funktion aufrufe.

das funktioniert bei mir

PendingIntent pendingIntent=PendingIntent.getBroadcast(context,0,intent,PendingIntent.FLAG_NO_CREATE);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && pendingIntent==null){
            context.startForegroundService(service_intent);
        }
        else
        {
            context.startService(service_intent);
        }
}

Dadurch wird der Dienst nur an startService und nicht an startForegroundService gesendet, wenn null. Die if-Anweisung müsste verschachtelt sein, sonst stürzt die App ab und versucht irgendwann, Dienste in einer späteren Version zu verwenden.
a54studio

1

Rufen Sie einfach die startForeground-Methode unmittelbar nach dem Erstellen von Service oder IntentService auf. so was:

import android.app.Notification;
public class AuthenticationService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        startForeground(1,new Notification());
    }
}

1
FATAL EXCEPTION: main android.app.RemoteServiceException: Ungültige Benachrichtigung für startForeground: java.lang.RuntimeException: Ungültiger Kanal für Servicebenachrichtigung: Benachrichtigung (channel = null pri = 0 contentView = null vibrate = null sound = null defaults = 0x0 flags = 0x40 color = 0x00000000 vis = PRIVATE) bei android.app.ActivityThread $ H.handleMessage (ActivityThread.java:1821) bei android.os.Handler.dispatchMessage (Handler.java:106) bei android.os.Looper.loop (Looper. Java: 164) bei android.app.ActivityThread.main (ActivityThread.java:6626)
Gerry

1

Ok, etwas, das mir aufgefallen ist und das vielleicht auch einigen anderen helfen könnte. Dies ist ausschließlich ein Test, um festzustellen, ob ich herausfinden kann, wie die aufgetretenen Vorkommnisse behoben werden können. Nehmen wir der Einfachheit halber an, ich habe eine Methode, die dies vom Moderator aufruft.

context.startForegroundService(new Intent(context, TaskQueueExecutorService.class));

try {
    Thread.sleep(10000);
} catch (InterruptedException e) {
    e.printStackTrace();
}       

Dies wird mit dem gleichen Fehler abstürzen. Der Dienst wird NICHT gestartet, bis die Methode abgeschlossen ist, daher nicht onCreate()im Dienst.

Selbst wenn Sie die Benutzeroberfläche außerhalb des Hauptthreads aktualisieren, startet sie nicht rechtzeitig, wenn Sie etwas haben, das diese Methode möglicherweise aufhält, und gibt Ihnen den gefürchteten Vordergrundfehler. In meinem Fall haben wir einige Dinge in eine Warteschlange geladen und jeweils angerufen startForegroundService, aber im Hintergrund war jeweils eine Logik enthalten. Wenn die Logik also zu lange gedauert hat, um diese Methode zu beenden, da sie hintereinander aufgerufen wurden, Absturzzeit. Das altestartService ignorierte es einfach und machte sich auf den Weg und da wir es jedes Mal anriefen, endete die nächste Runde.

Ich fragte mich daher, ob ich den Dienst von einem Thread im Hintergrund aus aufrufen konnte, ob er beim Start nicht vollständig gebunden und sofort ausgeführt werden konnte, und begann zu experimentieren. Obwohl dies NICHT sofort gestartet wird, stürzt es nicht ab.

new Handler(Looper.getMainLooper()).post(new Runnable() {
        public void run() {
               context.startForegroundService(new Intent(context, 
           TaskQueueExecutorService.class));
               try {
                   Thread.sleep(10000);
               } catch (InterruptedException e) {
                  e.printStackTrace();
              }       
        }
});

Ich werde nicht vorgeben zu wissen, warum es nicht abstürzt, obwohl ich vermute, dass dies dazu zwingt, zu warten, bis der Haupt-Thread es rechtzeitig handhaben kann. Ich weiß, dass es nicht ideal ist, es an den Haupt-Thread zu binden, aber da meine Verwendung es im Hintergrund aufruft, bin ich nicht wirklich besorgt, wenn es wartet, bis es abgeschlossen sein kann, anstatt abzustürzen.


Sie starten Ihren Dienst im Hintergrund oder in der Aktivität?
Mateen Chaudhry

Hintergrund. Das Problem ist, dass Android nicht garantieren kann, dass der Dienst in der vorgegebenen Zeit gestartet wird. Man würde denken, sie würden nur einen Fehler auslösen, um ihn zu ignorieren, wenn Sie wollen, aber leider nicht Android. Es muss tödlich sein. Dies hat in unserer App geholfen, aber nicht vollständig behoben.
a54studio

1

Ich füge Code in @humazed Antwort hinzu. Es gibt also keine erste Benachrichtigung. Es könnte eine Problemumgehung sein, aber es funktioniert für mich.

@Override
 public void onCreate() {
        super.onCreate();

        if (Build.VERSION.SDK_INT >= 26) {
            String CHANNEL_ID = "my_channel_01";
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                    "Channel human readable title",
                    NotificationManager.IMPORTANCE_DEFAULT);

            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);

            Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                    .setContentTitle("")
                    .setContentText("")
                    .setColor(ContextCompat.getColor(this, R.color.transparentColor))
                    .setSmallIcon(ContextCompat.getColor(this, R.color.transparentColor)).build();

            startForeground(1, notification);
        }
}

Ich füge transparentColor in kleinem Symbol und Farbe bei Benachrichtigung hinzu. Es wird klappen.


0

Ich habe das Problem behoben, dass der Dienst mit startService(intent)anstatt Context.startForeground()gestartet und startForegound()sofort danach angerufen wurde super.OnCreate(). Wenn Sie den Dienst beim Booten starten, können Sie außerdem die Aktivität starten, mit der der Dienst beim Booten gestartet wird. Obwohl es keine dauerhafte Lösung ist, funktioniert es.


Bei einigen Xiaomi-Geräten können Aktivitäten nicht über den Hintergrund gestartet werden
Mateen Chaudhry,

0

Aktualisieren von Daten in onStartCommand(...)

onBind (...)

onBind(...)Veranstaltung ist eine bessere Lebenszyklus zu initiieren startForegroundvs. onCreate(...)weil onBind(...)Pässe in einem Intentder wichtigen Daten in denen enthalten Bundlebenötigten die initialisieren Service. Es ist jedoch nicht erforderlich, wie onStartCommand(...)es aufgerufen wird, wenn das Servicezum ersten Mal erstellt wird oder nachfolgende Male aufgerufen wird.

onStartCommand (...)

startForegroundin onStartCommand(...)ist wichtig, um das zu aktualisieren, Servicesobald es bereits erstellt wurde.

Wann ContextCompat.startForegroundService(...)wird aufgerufen, nachdem ein Serviceerstellt wurde onBind(...)und onCreate(...)nicht aufgerufen wird. Daher können aktualisierte Daten onStartCommand(...)über die Intent Bundlezu aktualisierenden Daten in die übergeben werden Service.

Stichprobe

Ich verwende dieses Muster, um die PlayerNotificationManagerin der Coinverse Cryptocurrency News App zu implementieren .

Aktivität / Fragment.kt

context?.bindService(
        Intent(context, AudioService::class.java),
        serviceConnection, Context.BIND_AUTO_CREATE)
ContextCompat.startForegroundService(
        context!!,
        Intent(context, AudioService::class.java).apply {
            action = CONTENT_SELECTED_ACTION
            putExtra(CONTENT_SELECTED_KEY, contentToPlay.content.apply {
                audioUrl = uri.toString()
            })
        })

AudioService.kt

private var uri: Uri = Uri.parse("")

override fun onBind(intent: Intent?) =
        AudioServiceBinder().apply {
            player = ExoPlayerFactory.newSimpleInstance(
                    applicationContext,
                    AudioOnlyRenderersFactory(applicationContext),
                    DefaultTrackSelector())
        }

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    intent?.let {
        when (intent.action) {
            CONTENT_SELECTED_ACTION -> it.getParcelableExtra<Content>(CONTENT_SELECTED_KEY).also { content ->
                val intentUri = Uri.parse(content.audioUrl)
                // Checks whether to update Uri passed in Intent Bundle.
                if (!intentUri.equals(uri)) {
                    uri = intentUri
                    player?.prepare(ProgressiveMediaSource.Factory(
                            DefaultDataSourceFactory(
                                    this,
                                    Util.getUserAgent(this, getString(app_name))))
                            .createMediaSource(uri))
                    player?.playWhenReady = true
                    // Calling 'startForeground' in 'buildNotification(...)'.          
                    buildNotification(intent.getParcelableExtra(CONTENT_SELECTED_KEY))
                }
            }
        }
    }
    return super.onStartCommand(intent, flags, startId)
}

// Calling 'startForeground' in 'onNotificationStarted(...)'.
private fun buildNotification(content: Content): Unit? {
    playerNotificationManager = PlayerNotificationManager.createWithNotificationChannel(
            this,
            content.title,
            app_name,
            if (!content.audioUrl.isNullOrEmpty()) 1 else -1,
            object : PlayerNotificationManager.MediaDescriptionAdapter {
                override fun createCurrentContentIntent(player: Player?) = ...
                override fun getCurrentContentText(player: Player?) = ...
                override fun getCurrentContentTitle(player: Player?) = ...
                override fun getCurrentLargeIcon(player: Player?,
                                                 callback: PlayerNotificationManager.BitmapCallback?) = ...
            },
            object : PlayerNotificationManager.NotificationListener {
                override fun onNotificationStarted(notificationId: Int, notification: Notification) {
                    startForeground(notificationId, notification)
                }
                override fun onNotificationCancelled(notificationId: Int) {
                    stopForeground(true)
                    stopSelf()
                }
            })
    return playerNotificationManager.setPlayer(player)
}

-1

Bedienung

class TestService : Service() {

    override fun onCreate() {
        super.onCreate()
        Log.d(TAG, "onCreate")

        val nBuilder = NotificationCompat.Builder(this, "all")
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            .setContentTitle("TestService")
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        startForeground(1337, nBuilder.build())
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val rtn = super.onStartCommand(intent, flags, startId)

        if (intent?.action == STOP_ACTION) {
            Log.d(TAG, "onStartCommand -> STOP")
            stopForeground(true)
            stopSelf()
        } else {
            Log.d(TAG, "onStartCommand -> START")
        }

        return rtn
    }

    override fun onDestroy() {
        Log.d(TAG, "onDestroy")
        super.onDestroy()
    }

    override fun onBind(intent: Intent?): IBinder? = null

    companion object {

        private val TAG = "TestService"
        private val STOP_ACTION = "ly.zen.test.TestService.ACTION_STOP"

        fun start(context: Context) {
            ContextCompat.startForegroundService(context, Intent(context, TestService::class.java))
        }

        fun stop(context: Context) {
            val intent = Intent(context, TestService::class.java)
            intent.action = STOP_ACTION
            ContextCompat.startForegroundService(context, intent)
        }

    }

}

Prüfer

val nChannel = NotificationChannel("all", "All", NotificationManager.IMPORTANCE_NONE)
val nManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
nManager.createNotificationChannel(nChannel)

start_test_service.setOnClickListener {
    TestService.start(this@MainActivity)
    TestService.stop(this@MainActivity)
}

Ergebnis

D/TestService: onCreate
D/TestService: onStartCommand -> START
D/TestService: onStartCommand -> STOP
D/TestService: onDestroy
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.