Context.startForegroundService() nu a apelat apoi Service.startForeground() (Programare, Android, Service, Sistem De Operare, Foreground, Android 8.0 Oreo)

NiceGuy a intrebat.

Eu folosesc Service Class pe sistemul de operare Android O.

Intenționez să utilizez Service în fundal.

Site-ul documentația Android precizează că

Dacă aplicația dvs. vizează nivelul API 26 sau mai mare, sistemul impune restricții în ceea ce privește utilizarea sau crearea de servicii în fundal, cu excepția cazului în care aplicația însăși se află în prim-plan. Dacă o aplicație trebuie să creeze un serviciu în prim-plan, aceasta trebuie să apeleze la startForegroundService().

Dacă utilizați startForegroundService(), , aplicația Service aruncă următoarea eroare.

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

Ce este în neregulă cu aceasta?

Comentarii

  • Adică, vă rog să oferiți un exemplu minim reproductibil. Acesta ar trebui să includă întregul stack trace Java și codul care declanșează accidentul. –  > Por CommonsWare.
  • Bug-ul este încă prezent în API 26 și 27 (27.0.3). Versiunile Android afectate sunt 8.0 și 8.1. Ați putea reduce numărul de accidentări prin adăugarea funcției startForeground() atât la onCreate(), cât și la onStartCommand(), dar unii utilizatori vor avea în continuare accidentări. Singura modalitate de a remedia acest lucru este targetSdkVersion 25 în build.gradle. –  > Por Alex.
  • FYI issuetracker.google.com/issues/76112072 –  > Por v1k.
  • Eu folosesc această soluție de rezolvare acum. funcționează ca un farmec theandroiddeveloper.com/blog/… –  > Por Prasad Pawar.
  • Am aceeași problemă. Am rezolvat această problemă. Am împărtășit implementarea mea în acest subiect stackoverflow.com/questions/55894636/… –  > Por Beyazid.
30 răspunsuri
zhuzhumouse

Din documentele Google pe Modificări de comportament Android 8.0:

Sistemul permite aplicațiilor să apeleze Context.startForegroundService() chiar și atunci când aplicația se află în fundal. Cu toate acestea, aplicația trebuie să apeleze metoda startForeground() a serviciului respectiv în termen de cinci secunde de la crearea serviciului.

Soluție: apelați startForeground() în onCreate() pentru Service pe care o utilizați Context.startForegroundService()

Vedeți și: „A se vedea și Limite de execuție în fundal pentru Android 8.0 (Oreo)

Comentarii

    20

  • Am făcut acest lucru în onStartCommand dar tot primesc această eroare. Am apelat startForegroundService(intent) în metoda MainActivity. Poate că serviciul este pornit prea lent. Cred că limita de cinci secunde nu ar trebui să existe înainte de a putea promite că serviciul este pornit imediat. –  > Por Kimi Chiu.
  • 32

  • Perioada de 5 secunde nu este cu siguranță suficientă, această excepție se întâmplă foarte des în sesiunile de depanare. Bănuiesc că s-ar întâmpla din când în când și în modul de lansare. Și fiind EXCEPȚIE FATALĂ pur și simplu se blochează aplicația! Am încercat să o prind cu Thread.setDefaultUncaughtExceptionHandler(), dar chiar și la obținerea ei acolo și ignorarea androidului îngheață aplicația. Asta pentru că această excepție este declanșată de la handleMessage(), iar bucla principală se termină efectiv… caut o soluție de rezolvare. –  > Por southerton.
  • @southerton Cred că ar trebui să o apelați pe onStartCommand() în loc de onCreate, deoarece, dacă închideți serviciul și îl porniți din nou, s-ar putea ca acesta să treacă la onStartCommand() fără a apela onCreate … –  > Por dezvoltator android.
  • Putem verifica răspunsul de la echipa Google aici issuetracker.google.com/issues/76112072#comment56 –  > Por Prags.
  • Am o aplicație care are 30k utilizatori activi pe Play Store. Și am aceeași problemă: ~30 de blocări zilnice pe Android 8+ din cauza limitei de timp de 5 sec pentru a porni în prim-plan. Blocajele vin de la diverse dispozitive, chiar și de la lansări de telefoane de anul acesta(S20Ultra, Motorola…)Designul Android nu garantează că apelul va fi declanșat în 5 sec și serviciul este omorât de sistemul de operare. Google are o listă lungă de feedback cu privire la această problemă, dar nu recunoaște că este un defect de design. Ar trebui să-l reproiecteze mai bine înainte de a ne forța să renunțăm la serviciile de fundal. –  > Por Duna.
humazed

Am sunat ContextCompat.startForegroundService(this, intent) pentru a porni serviciul, apoi

În serviciu 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);
        }
}

Comentarii

    59

  • Și eu. Dar tot mai primesc această eroare din când în când. Poate că Android nu poate garanta că va apela serviciul onCreate în 5 secunde. Așa că ar trebui să-l reproiecteze înainte de a ne obliga să respectăm regulile. –  > Por Kimi Chiu.
  • Am apelat startForeground în onStartCommand() și primeam ocazional această eroare. Am mutat-o în onCreate și de atunci nu am mai văzut-o (încrucișez degetele). –  > Por Tyler.
  • Acest lucru creează o notificare locală în Oreo care spune „APP_NAME is running. Atingeți pentru a închide sau pentru a vedea informații”. Cum să nu mai afișați această notificare? –  > Por Artist404.
  • Nu, echipa Android a susținut că acesta este un comportament intenționat. Așa că tocmai mi-am reproiectat aplicația. Acest lucru este ridicol. Întotdeauna trebuie să reproiectați aplicațiile din cauza acestor „comportamente intenționate”. Este a treia oară. –  > Por Kimi Chiu.
  • 15

  • Cauza principală a acestei probleme este că serviciul a fost oprit înainte de a fi promovat în prim-plan. Dar afirmația nu s-a oprit după ce serviciul a fost distrus. Puteți încerca să reproduceți acest lucru adăugând StopService după apelarea startForegroundService. –  > Por Kimi Chiu.
ZhouX

Motivul pentru care se întâmplă această problemă este că cadrul Android nu poate garanta că serviciul dvs. este pornit în 5 secunde, dar, pe de altă parte, cadrul are o limită strictă privind notificarea în prim-plan trebuie să fie lansată în 5 secunde, fără a verifica dacă cadrul a încercat să pornească serviciul.

Aceasta este cu siguranță o problemă de cadru, dar nu toți dezvoltatorii care se confruntă cu această problemă fac tot ce pot:

  1. startForeground o notificare trebuie să fie atât în onCreate și onStartCommand, , deoarece dacă serviciul dvs. este deja creat și cumva activitatea dvs. încearcă să îl pornească din nou, onCreate nu va fi apelată.

  2. ID-ul notificării nu trebuie să fie 0, altfel se va întâmpla aceeași eroare, chiar dacă nu este același motiv.

  3. stopSelf nu trebuie să fie apelată înainte startForeground.

Cu toate cele 3 de mai sus, această problemă poate fi redusă un pic, dar încă nu este o soluție, adevărata soluție sau, să spunem, o soluție de rezolvare este de a reduce versiunea sdk-ului țintă la 25.

Și rețineți că, cel mai probabil, Android P va purta în continuare această problemă, deoarece Google refuză să înțeleagă măcar ce se întâmplă și nu crede că este vina lor, citiți #36 și #56 pentru mai multe informații

Comentarii

  • De ce atât onCreate, cât și onStartCommand? Poți să o pui doar în onStartCommand? –  > Por rcell.
  • stopSelf nu trebuie apelat înainte de startForeground => sau direct după, deoarece pare să anuleze „startForeground” atunci când o faci. –  > Por Frank.
  • Am executat câteva teste și, chiar dacă onCreate nu este apelat dacă serviciul este deja creat, nu este nevoie să apelați din nou startForeground. Prin urmare, ați putea să o apelați doar la metoda onCreate și nu în onStartCommand. Probabil că se consideră că o singură instanță a serviciului este în prim-plan după primul apel și până la sfârșitul acestuia. –  > Por Lxu.
  • Eu folosesc 0 ca ID de notificare a apelului inițial pentru startForeground. Schimbarea la 1 rezolvă problema pentru mine (sper) –  > Por mr5.
  • Deci, care este soluția? –  > Por IgorGanapolsky.
Oleg Gryb

Știu, prea multe răspunsuri au fost publicate deja, totuși adevărul este – startForegroundService nu poate fi reparat la nivelul aplicației și ar trebui să nu-l mai folosiți. Acea recomandare Google de a utiliza API Service#startForeground() în termen de 5 secunde după ce Context#startForegroundService() a fost apelat nu este ceva ce o aplicație poate face întotdeauna.

Android rulează o mulțime de procese simultan și nu există nicio garanție că Looper va apela serviciul țintă care ar trebui să apeleze startForeground() în termen de 5 secunde. Dacă serviciul dvs. țintă nu a primit apelul în 5 secunde, nu aveți noroc, iar utilizatorii dvs. vor experimenta situația ANR. În stack trace veți vedea ceva de genul următor:

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)

Din câte am înțeles, Looper a analizat coada de așteptare aici, a găsit un „abuzator” și l-a ucis pur și simplu. Sistemul este fericit și sănătos acum, în timp ce dezvoltatorii și utilizatorii nu sunt, dar din moment ce Google își limitează responsabilitățile față de sistem, de ce ar trebui să le pese de ultimii doi? Se pare că nu le pasă. Ar putea să-l facă mai bun? Bineînțeles, de exemplu, ar fi putut servi dialogul „Aplicația este ocupată”, cerând utilizatorului să ia o decizie privind așteptarea sau distrugerea aplicației, dar de ce să se deranjeze, nu este responsabilitatea lor. Principalul lucru este că sistemul este sănătos acum.

Din observațiile mele, acest lucru se întâmplă relativ rar, în cazul meu aproximativ 1 accident într-o lună pentru 1 000 de utilizatori. Reproducerea este imposibilă și chiar dacă este reprodusă, nu poți face nimic pentru a o repara definitiv.

A existat o sugestie bună în acest fir de discuție de a folosi „bind” în loc de „start” și apoi, atunci când serviciul este gata, procesați onServiceConnected, dar, din nou, înseamnă să nu folosiți deloc apelurile startForegroundService.

Cred că acțiunea corectă și onestă din partea Google ar fi să spună tuturor că startForegourndServcie are o deficiență și nu ar trebui să fie utilizat.

Întrebarea rămâne în continuare: ce să folosim în schimb? Din fericire pentru noi, există acum JobScheduler și JobService, care reprezintă o alternativă mai bună pentru serviciile de prim-plan. Este o opțiune mai bună, din acest motiv:

În timp ce un job rulează, sistemul deține un wakelock în numele aplicației dvs. Din acest motiv, nu trebuie să întreprindeți nicio acțiune pentru a garanta că dispozitivul rămâne treaz pe durata lucrării.

Aceasta înseamnă că nu trebuie să vă mai preocupați de gestionarea wakelock-urilor și de aceea nu diferă de serviciile de prim-plan. Din punctul de vedere al implementării, JobScheduler nu este serviciul dumneavoastră, ci al sistemului, care probabil că va gestiona corect coada de așteptare, iar Google nu își va termina niciodată propriul copil 🙂

Samsung a trecut de la startForegroundService la JobScheduler și JobService în protocolul lor de accesoriu Samsung (SAP). Este foarte util atunci când dispozitive precum ceasurile inteligente trebuie să vorbească cu gazde precum telefoanele, în cazul în care jobul trebuie să interacționeze cu un utilizator prin intermediul firului principal al unei aplicații. Deoarece lucrările sunt postate de către planificator pe firul principal, acest lucru devine posibil. Totuși, trebuie să vă amintiți că lucrarea se execută pe firul principal și să descărcați toate lucrurile grele pe alte fire și sarcini asincrone.

Acest serviciu execută fiecare sarcină de lucru primită pe un Handler care rulează pe firul principal al aplicației dumneavoastră. Acest lucru înseamnă că trebuie să descărcați logica de execuție pe un alt fir/handler/AsyncTask la alegere.

Singura capcană a trecerii la JobScheduler/JobService este că va trebui să refactorizați codul vechi, iar acest lucru nu este distractiv. Mi-am petrecut ultimele două zile făcând exact acest lucru pentru a utiliza noua implementare SAP a Samsung. Voi urmări rapoartele mele de accident și vă voi anunța dacă voi vedea din nou accidentări. Teoretic nu ar trebui să se întâmple, dar întotdeauna există detalii de care s-ar putea să nu fim conștienți.

UPDATENu mai sunt raportate crash-uri de către Play Store. Înseamnă că JobScheduler/JobService nu au o astfel de problemă și trecerea la acest model este abordarea corectă pentru a scăpa de problema startForegroundService o dată pentru totdeauna. Sper că, Google/Android citește acest lucru și va comenta/aviza/furniza în cele din urmă o îndrumare oficială pentru toată lumea.

UPDATE 2

Pentru cei care utilizează SAP și care întreabă cum utilizează SAP V2 JobService explicația este mai jos.

În codul dvs. personalizat va trebui să inițializați SAP (este Kotlin) :

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

Acum trebuie să decompilați codul Samsung pentru a vedea ce se întâmplă în interior. În SAAgentV2 aruncați o privire la implementarea requestAgent și la următoarea linie:

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

where d defined as below

private SAAdapter d;

Mergeți acum la clasa SAAdapter și găsiți funcția onServiceConnectionRequested care programează un job folosind următorul apel:

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

SAJobService este doar o implementare a JobService din Android și aceasta este cea care programează o sarcină:

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());
}

După cum vedeți, ultima linie de aici folosește Android’d JobScheduler pentru a obține acest serviciu de sistem și pentru a programa o lucrare.

În apelul requestAgent am trecut mAgentCallback, care este o funcție de apelare care va primi controlul atunci când are loc un eveniment important. Acesta este modul în care este definit callback-ul în aplicația mea:

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 aici este o clasă pe care am implementat-o pentru a procesa toate cererile venite de la un smartwatch Samsung. Nu este codul complet, ci doar un schelet:

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) {

    }

}

După cum vedeți, MessageJobs necesită și clasa MessageSocket pe care va trebui să o implementați și care procesează toate mesajele venite de la dispozitiv.

În concluzie, nu este atât de simplu și necesită ceva săpături în interior și codare, dar funcționează și, cel mai important – nu se blochează.

Comentarii

  • Un răspuns bun, dar există o problemă majoră, JobIntentService se execută imediat ca un IntentService sub Oreo, dar programează un Job pe și peste Oreo, așa că JobIntentService nu pornește imediat. Mai multe informații –  > Por CopsOnRoad.
  • @CopsOnRoad Este foarte util și pornește imediat în cazul meu, După cum am scris, este folosit pentru interacțiuni în timp real între telefon și un smartwatch. În niciun caz, un utilizator nu ar aștepta 15 minute pentru a trimite date între acestea două. Funcționează foarte bine și nu se blochează niciodată. –  > Por Oleg Gryb.
  • Sună grozav, ar face răspunsul dvs. mult mai valoros dacă puteți împărtăși câteva exemple de cod, sunt nou în Android și am constatat că Job... durează ceva timp pentru a porni și este o versiune avansată a AlarmManager. –  > Por CopsOnRoad.
  • Probabil că o voi face când timpul îmi va permite: Va trebui să îl decuplez de codul specific personalizat și să de-compilez unele librării Samsung proprietare –  > Por Oleg Gryb.
  • @batmaci – JobService este utilizat de SAP în mod intern. Consultați Actualizarea 2 din OP pentru detalii. –  > Por Oleg Gryb.
swooby

Aplicația dvs. se va bloca dacă apelați Context.startForegroundService(...) și apoi apelați Context.stopService(...) înainte de Service.startForeground(...) este apelat.

Am un repro clar aici ForegroundServiceAPI26

Am deschis un bug pe această temă la : Google issue tracker

Au fost deschise și închise mai multe bug-uri pe această temă Won’t Fix.

Să sperăm că a mea cu pași de repro clar va face tăietura.

Informații furnizate de echipa Google

Google issue tracker Comentariu 36

Aceasta nu este o eroare de cadru; este intenționată. Dacă aplicația pornește o instanță de serviciu cu startForegroundService(), , aceasta trebuie să să treacă acea instanță de serviciu în starea de prim-plan și să afișeze notificarea. Dacă instanța de serviciu este oprită înainte ca startForeground() să fie apelată, promisiunea respectivă nu este îndeplinită: aceasta este o eroare a aplicației.

Referință nr. 31, , publicarea unui serviciu pe care alte aplicații îl pot lansa direct este fundamental nesigură. Puteți atenua puțin acest lucru prin tratarea toate acțiunile de lansare ale serviciului respectiv ca necesitând startForeground(), , deși, evident, s-ar putea să nu fie ceea ce v-ați gândit.

Google issue tracker Comentariu 56

Există câteva scenarii diferite care duc la același rezultat aici.

Problema semantică directă, că este pur și simplu o eroare să pornești ceva cu startForegroundService() dar să neglijezi trecerea efectivă în prim-plan prin startForeground(), , este doar atât: o problemă semantică. Aceasta este tratată ca un bug al aplicației, în mod intenționat. Oprirea serviciului înainte de a-l trece în prim-plan este o eroare de aplicație. Acesta a fost punctul central al OP și este motivul pentru care această problemă a fost marcată ca „funcționând conform destinației”.

Cu toate acestea, există, de asemenea, întrebări cu privire la spusele false de detectare a acestei probleme. Aceasta este este tratată ca o problemă reală, deși este urmărită separat de această problemă specială din bug tracker. Nu suntem surzi la această plângere.

Comentarii

Ahmad Arslan

Am cercetat pe această temă timp de câteva zile și am găsit soluția. acum în Android O puteți seta limitarea fundalului ca mai jos

Serviciul care apelează o clasă de servicii

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);
}

iar clasa de servicii ar trebui să fie de tipul

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
    }
}

Comentarii

  • Ați încercat în vreo aplicație care are cel puțin câteva mii de utilizatori? Am încercat, unii utilizatori încă mai au probleme de blocare. –  > Por Alex.
  • Nu, nu, nu, folosesc exact codul și nu văd clienți care să se blocheze. –  > Por Ahmad Arslan.
  • Câți utilizatori ai? Am văzut ~10-30+ crash-uri zilnice (cu eroarea) în aplicația care avea 10k utilizatori pe zi. Bineînțeles, se întâmplă doar pentru utilizatorii cu android 8.0 și android 8.1, în cazul în care targetSdkVersion este 27. Toate problemele dispar la 0 crash-uri imediat după ce am setat targetSdkVersion la 25. –  > Por Alex.
  • doar 2200 de utilizatori :-/ dar nu am avut parte de nici un crash –  > Por Ahmad Arslan.
  • @Jeeva, nu chiar. În prezent, folosesc targetSdkVersion 25 cu compileSdkVersion 27. Se pare că este cea mai bună cale după multe experimente…. Sper că vor termina developer.android.com/topic/libraries/architecture/… înainte de august 2018, deoarece android-developers.googleblog.com/2017/12/12/… –  > Por Alex.
Roy Solberg

Am un widget care face actualizări relativ frecvente atunci când dispozitivul este treaz și vedeam mii de blocaje în doar câteva zile.

Problema declanșează

Am observat problema chiar și pe Pixel 3 XL, când nu aș fi crezut că dispozitivul are prea multă încărcare. Și toate și toate căile de cod au fost acoperite cu startForeground(). Dar apoi mi-am dat seama că, în multe cazuri, serviciul meu își face treaba foarte repede. Cred că declanșatorul aplicației mele a fost faptul că serviciul se termina înainte ca sistemul să apuce să afișeze o notificare.

Rezolvarea/soluția

Am reușit să scap de toate blocajele. Ceea ce am făcut a fost să elimin apelul către stopSelf(). (Mă gândeam să întârzii oprirea până când eram destul de sigur că notificarea a fost afișată, dar nu vreau ca utilizatorul să vadă notificarea dacă nu este necesar). Când serviciul a fost inactiv timp de un minut sau sistemul îl distruge în mod normal, fără să arunce nicio excepție.

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

Comentarii

  • Dar, conform documentelor, stopForeground ...does not stop the service from running (for that you use stopSelf() or related methods) –  > Por Dima Rostopira.
  • @DimaRostopira Da, în practică, văd că sistemul se ocupă de oprirea serviciului din funcționare după ce a fost inactiv pentru o perioadă de timp (poate un minut). Nu este ideal, dar împiedică sistemul să se plângă. –  > Por Roy Solberg.
  • Am avut, de asemenea, problema că notificarea din prim-plan nu a fost eliminată, atunci când serviciul meu a fost foarte puțin timp în execuție. Adăugarea unei întârzieri de 100ms înainte de apelul la stopForeground(true); stopSelf();; a ajutat. Soluția dvs. nu a ajutat. (Pixel 3, Android 11) –  > Por Malachiasz.
Furkan Yurdakul

Din moment ce toată lumea care vizitează aici suferă același lucru, vreau să împărtășesc soluția mea pe care nimeni altcineva nu a încercat-o înainte (în această întrebare, oricum). Vă pot asigura că funcționează, chiar și pe un punct de întrerupere oprit ceea ce confirmă această metodă.

Problema este de a apela Service.startForeground(id, notification) din serviciul însuși, nu-i așa? Android Framework, din păcate, nu garantează apelarea Service.startForeground(id, notification) în cadrul Service.onCreate() în 5 secunde, dar aruncă oricum excepția, așa că am venit cu această metodă.

  1. Legați serviciul de un context cu un binder din serviciu înainte de a apela Context.startForegroundService()
  2. În cazul în care bind-ul are succes, apelați Context.startForegroundService() din conexiunea serviciului și se apelează imediat Service.startForeground() în interiorul conexiunii serviciului.
  3. NOTĂ IMPORTANTĂ: Apelați la Context.bindService() în interiorul un try-catch deoarece, în unele ocazii, apelul poate arunca o excepție, caz în care trebuie să vă bazați pe apelarea metodei Context.startForegroundService() direct și să sperați că nu va da greș. Un exemplu poate fi contextul unui receptor de difuzare, însă obținerea contextului aplicației nu aruncă o excepție în acest caz, dar utilizarea directă a contextului o face.

Acest lucru funcționează chiar și atunci când aștept un punct de întrerupere după legarea serviciului și înainte de a declanșa apelul „startForeground”. Așteptarea între 3-4 secunde nu declanșează excepția, în timp ce după 5 secunde se aruncă excepția. (Dacă dispozitivul nu poate executa două rânduri de cod în 5 secunde, atunci este timpul să aruncați acel dispozitiv la gunoi).

Așadar, începeți cu crearea unei conexiuni de serviciu.

// 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..");
    }
};

În interiorul serviciului, creați un liant care returnează instanța serviciului dvs.

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;
    }
}

Apoi, încercați să legați serviciul din acel context. Dacă reușește, va apela ServiceConnection.onServiceConnected() din conexiunea serviciului pe care o utilizați. Apoi, gestionați logica din codul prezentat mai sus. Un exemplu de cod ar arăta astfel:

// 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));
}

Se pare că funcționează în aplicațiile pe care le dezvolt, așa că ar trebui să funcționeze și atunci când încercați și dumneavoastră.

Comentarii

  • Mulțumesc, tocmai am implementat soluția dvs. într-o aplicație cu mult peste 5M de instalări, să vedem. Sper să fie bine. –  > Por gregko.
  • Funcționează pentru mine până în prezent – aproape o lună mai târziu și nu mai există accidentări sau ANR-uri legate de startForegroundService() – Mulțumesc! –  > Por gregko.
tobalr

Doar o avertizare, deoarece am pierdut prea multe ore pe acest lucru. Am continuat să primesc această excepție chiar dacă am apelat startForeground(..) ca primul lucru din onCreate(..). În cele din urmă am descoperit că problema a fost cauzată de utilizarea NOTIFICATION_ID = 0. Folosirea oricărei alte valori pare să rezolve problema.

Comentarii

  • În cazul meu am folosit ID 1, mulțumesc, problema a fost rezolvată –  > Por Amin Pinjari.
  • Eu folosesc ID 101, dar tot primesc această eroare uneori. –  > Por CopsOnRoad.
Hexise

Am o soluție pentru această problemă. Am verificat această soluție în propria mea aplicație (300K+ DAU), care poate reduce cel puțin 95% din acest tip de accident, dar tot nu poate evita 100% această problemă.

Această problemă se întâmplă chiar și atunci când vă asigurați că apelați startForeground() imediat după ce serviciul a început, așa cum a documentat Google. Se poate întâmpla pentru că procesul de creare și inițializare a serviciului costă deja mai mult de 5 secunde în multe scenarii, atunci indiferent când și unde apelați metoda startForeground(), acest accident este inevitabil.

Soluția mea este să mă asigur că startForeground() va fi executată în termen de 5 secunde după metoda startForegroundService(), indiferent cât timp trebuie să fie creat și inițializat serviciul dumneavoastră. Iată soluția detaliată.

  1. Nu folosiți startForegroundService în primul rând, utilizați bindService() cu flag-ul auto_create. Acesta va aștepta inițializarea serviciului. Iată codul, serviciul meu de probă este 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. Apoi, iată implementarea MusicBinder:

    /**
     * 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. Cea mai importantă parte, implementarea MusicService, metoda forceForeground() va asigura că metoda startForeground() este apelată imediat după startForegroundService():

    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. Dacă doriți să rulați fragmentul de cod de la pasul 1 într-o intenție în așteptare, de exemplu, dacă doriți să porniți un serviciu de prim-plan într-un widget (un clic pe butonul widget) fără a deschide aplicația, puteți include fragmentul de cod într-un receptor de difuzare și puteți lansa un eveniment de difuzare în locul comenzii de pornire a serviciului.

Asta este tot. Sper că vă ajută. Mult noroc.

almisoft

Această eroare apare și pe Android 8+ atunci când Service.startForeground(int id, Notification notification) este apelat în timp ce id este setat la 0.

id int: Identificatorul pentru această notificare conform NotificationManager.notify(int, Notification); nu trebuie să fie 0.

Comentarii

  • Nu folosiți numere mai mari și pentru ID-ul notificării. Încercați să folosiți ID-uri cu o singură cifră. stackoverflow.com/a/12228555/2527204 –  > Por Marlon.
Shrdi

Trebuie să adăugați o permisiune ca mai jos pentru dispozitivul android 9 atunci când utilizați sdk-ul țintă 28 sau mai târziu sau excepția se va întâmpla întotdeauna:

<uses-permission android_name="android.permission.FOREGROUND_SERVICE" />

Comentarii

  • Aplicația mea are această permisiune, dar încă se confruntă cu problema descrisă în întrebare. –  > Por JohnnyLambada.
  • @JohnnyLambada se pare că numai o activitate poate începeForgroundService, încercați să începeți serviciul în fundal în schimb. –  > Por Shrdi.
makovkastar

Am cercetat această problemă și iată ce am descoperit până acum. Acest accident s-ar putea întâmpla dacă avem un cod asemănător cu acesta:

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);

Excepția este aruncată în următorul bloc de cod:

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);
         }
    }
    ...
}

Această metodă este executată înainte de onCreate() din MyForegroundService deoarece Android programează crearea serviciului pe gestionarul firului principal, însă bringDownServiceLocked este apelată pe un BinderThread, , ceea ce reprezintă o condiție de rasă. Aceasta înseamnă că MyForegroundService nu a avut șansa de a apela startForeground ceea ce va cauza blocarea.

Pentru a remedia acest lucru trebuie să ne asigurăm că bringDownServiceLocked nu este apelat înainte de onCreate() din 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));
    }
}

Prin utilizarea transmisiunilor lipicioase ne asigurăm că transmisiunile nu se pierd, iar stopReceiver primește intenția de oprire de îndată ce a fost înregistrată în onCreate() din MyForegroundService. Până în acest moment, am apelat deja startForeground(...). De asemenea, trebuie să eliminăm acea difuzare lipicioasă pentru a împiedica notificarea lui stopReceiver data viitoare.

Vă rugăm să rețineți că că metoda sendStickyBroadcast este depreciată și o folosesc doar ca o soluție temporară pentru a rezolva această problemă.

Comentarii

  • Ar trebui să o apelați în loc de context.stopService(serviceIntent) atunci când doriți să opriți serviciul. –  > Por makovkastar.
sandy

Atât de multe răspunsuri, dar niciunul nu a funcționat în cazul meu.

Am pornit serviciul în felul următor.

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

Și în serviciul meu în 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);
    }

Și nu uitați să setați NOTIFICATION_ID non zero

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

Deci, totul a fost perfect, dar tot se blochează pe 8.1, deci cauza a fost cea de mai jos.

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

Am apelat la stop foreground cu remove notificaton dar odată ce notificarea a fost eliminată serviciul a devenit fundal și serviciul de fundal nu poate rula în android O din fundal. a început după ce a primit push.

Deci, cuvântul magic este

   stopSelf();

Până acum, deci orice motiv pentru care serviciul dvs. se blochează urmați toți pașii de mai sus și bucurați-vă.

Comentarii

  • if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { stopForeground(true); } else { stopForeground(true); } pentru ce este acest if else? în cazul în care făceați același lucru, stopForeground(true); –  > Por user3135923.
  • Cred că ați vrut să puneți stopSelf(); în interiorul blocului else în loc de stopForeground(true); –  > Por Justin Stanley.
  • Da, @JustinStanley folosiți stopSelf(); –  > Por sandy.
  • Nu ar trebui startForeground fi apelat în onCreate? –  > Por Aba.
  • Eu folosesc NotificationManager și nu clasa Notification. Cum pot implementa acest lucru? –  > Por Prajwal Waingankar.
Sambhaji Karad

Mă confrunt cu aceeași problemă și, după ce am petrecut timp, am găsit o soluție, puteți încerca codul de mai jos. Dacă utilizați Service atunci puneți acest cod în onCreate, altfel folosiți Intent Service atunci puneți acest cod în 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);
    }

Andy Cass

Probleme cu Android O API 26

Dacă opriți serviciul imediat (astfel încât serviciul dvs. nu rulează cu adevărat (formulare / înțelegere) și sunteți mult sub intervalul ANR, trebuie să apelați în continuare startForeground înainte de stopSelf

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

Ați încercat această abordare, dar tot se creează o eroare:-

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

Folosesc această abordare până când eroarea este rezolvată

mContext.startService(playIntent);

Comentarii

  • Ar trebui să fie Util.SDK_INT >= 26, nu doar mai mare –  > Por Andris.
Shalu Gupta

Vă rugăm să nu apelați nici un StartForgroundServices în interiorul onCreate() trebuie să apelați serviciile StartForground în onStartCommand() după ce ați creat firul de lucru, altfel veți obține ANR întotdeauna, deci vă rugăm să nu scrieți o autentificare complexă în firul principal al 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: Atenție! funcția startForeground nu poate lua 0 ca prim argument, va ridica o excepție! acest exemplu conține un apel de funcție greșit, schimbați 0 cu propria const care nu poate fi 0 sau mai mare decât Max(Int32).

Alécio Carvalho

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

Similar cu startService(Intent), dar cu o promisiune implicită că serviciul va apela startForeground(int, android.app.Notification) odată ce începe să funcționeze. Serviciului i se acordă o perioadă de timp comparabilă cu intervalul ANR pentru a face acest lucru, în caz contrar sistemul va opri automat serviciul și va declara aplicația ANR.

Spre deosebire de startService(Intent) obișnuită, această metodă poate fi utilizată în orice moment, indiferent dacă aplicația care găzduiește serviciul se află în stare de prim-plan.

Asigurați-vă că apelați metoda Service.startForeground(int, android.app.Notification) la onCreate() astfel încât să vă asigurați că va fi apelată..dacă aveți vreo condiție care vă poate împiedica să faceți acest lucru, atunci ar fi mai bine să folosiți metoda normală Context.startService(Intent) și să apelați la Service.startForeground(int, android.app.Notification) tu însuți.

Se pare că Context.startForegroundService() adaugă un câine de pază pentru a se asigura că ați apelat Service.startForeground(int, android.app.Notification) înainte ca acesta să fie distrus…

a54studio

Ok, ceva ce am observat în acest sens și care ar putea ajuta și pe alții. Acest lucru este strict din teste pentru a vedea dacă aș putea să-mi dau seama cum să rezolv incidentele pe care le văd. De dragul simplității, să spunem că am o metodă care apelează acest lucru din prezentator.

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

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

Aceasta se va bloca cu aceeași eroare. Serviciul NU va porni până când metoda nu este finalizată, prin urmare, nu se va aplica niciun onCreate() în serviciu.

Deci, chiar dacă actualizați interfața de utilizare în afara firului principal, DACĂ aveți ceva care ar putea reține acea metodă după ea, nu va porni la timp și vă va da temuta eroare Foreground Error. În cazul meu, încărcam niște lucruri într-o coadă și fiecare dintre ele era apelată startForegroundService, dar era implicată și o anumită logică în fundal. Deci, dacă logica a durat prea mult timp pentru a termina acea metodă, deoarece au fost apelate una după alta, era timpul să se prăbușească. Vechiul startService pur și simplu o ignora și își continua drumul și, din moment ce o apelam de fiecare dată, următoarea rundă se termina.

Acest lucru m-a făcut să mă întreb dacă, dacă aș fi apelat serviciul dintr-un fir în fundal, nu ar fi putut fi complet legat la început și să ruleze imediat, așa că am început să experimentez. Chiar dacă acest lucru NU îl pornește imediat, nu se blochează.

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();
              }       
        }
});

Nu voi pretinde că știu de ce nu se blochează, deși bănuiesc că acest lucru îl forțează să aștepte până când firul principal îl poate gestiona în timp util. Știu că nu este ideal să îl legăm de firul principal, dar din moment ce utilizarea mea îl apelează în fundal, nu sunt foarte îngrijorat dacă așteaptă până când se poate finaliza în loc să se prăbușească.

Comentarii

  • vă porniți serviciul din fundal sau în activitate? –  > Por Mateen Chaudhry.
  • În fundal. Problema este că Android nu poate garanta că serviciul va porni în timpul alocat. S-ar putea crede că ar arunca o eroare pentru a o ignora dacă doriți, dar, din păcate, nu și Android. Trebuie să fie fatală. Acest lucru a ajutat în aplicația noastră, dar nu a rezolvat problema în totalitate. –  > Por a54studio.
Gaurav Singla

Chiar și după ce am apelat la startForeground în Service, , se blochează pe unele dispozitive dacă apelăm stopService chiar înainte de onCreate este apelat.Deci, am rezolvat această problemă prin pornirea serviciului cu un indicator suplimentar:

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

și am adăugat o verificare în onStartCommand pentru a vedea dacă a fost pornit pentru a se opri:

@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;
}

P.S. În cazul în care serviciul nu era în curs de execuție, ar fi pornit mai întâi serviciul, ceea ce reprezintă un cost suplimentar.

Comentarii

  • nu este nevoie să creați un serviciu pentru a-l opri, ci doar să folosiți managerul local de difuzare )) -) –  > Por user924.
  • Nu este un serviciu diferit. Este același serviciu pe care vreau să îl opresc! –  > Por Gaurav Singla.
  • Nu am spus nimic despre un serviciu diferit, adică de ce încerci să te oprești în timp ce este pornit? –  > Por user924.
  • utilizați mesaje locale de difuzare –  > Por user924.
  • Dragă user924, Dacă trebuie să opresc serviciul, nu este nevoie să folosesc și BroadCast, pot apela direct context.stopService(). Răspunsul de mai sus este un truc pentru a rezolva problema care se întâmplă pe dispozitivele Samsung în cazul în care opriți serviciul chiar înainte ca onStartCommand-ul său să fie apelat efectiv. –  > Por Gaurav Singla.
SaimumIslam27

Tocmai am verificat PendingIntent null or nor not înainte de a apela comanda context.startForegroundService(service_intent) funcția.

acest lucru funcționează pentru mine

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);
        }
}

Comentarii

  • De fapt, acest lucru doar lovește serviciul la startService mai degrabă decât la startForegroundService dacă este nul. Instrucțiunea if ar trebui să fie imbricata, altfel aplicația s-ar bloca la un moment dat dacă ar încerca să utilizeze serviciile pe o versiune ulterioară. –  > Por a54studio.
Hossein Karami

apelați doar metoda startForeground imediat după ce Serviciul sau IntentService este creat. astfel:

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

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

Comentarii

  • FATAL EXCEPTION: main android.app.RemoteServiceException: Bad notification for startForeground: java.lang.RuntimeException: canal invalid pentru notificarea serviciului: Notification(channel=null pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x40 color=0x000000000000 vis=PRIVATE) at android.app.ActivityThread$H.handleMessage(ActivityThread. java:1821) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:164) at android.app.ActivityThread.main(ActivityThread.java:6626)  > Por Gerry.
Adam Hurwitz

Actualizarea datelor în onStartCommand(...)

onBind(…)

onBind(...) este un eveniment de ciclu de viață mai bun pentru a iniția un eveniment de ciclu de viață startForeground vs. onCreate(...) deoarece onBind(...) trece într-un Intent care poate conține date importante în Bundle necesare pentru a inițializa Service. Cu toate acestea, nu este necesar ca onStartCommand(...) este apelat atunci când Service este creată pentru prima dată sau este apelată ulterior.

onStartCommand(….)

startForeground în onStartCommand(...) este importantă pentru a actualiza Service odată ce a fost deja creat.

Atunci când ContextCompat.startForegroundService(...) este apelat după o comandă Service a fost creat onBind(...) și onCreate(...) nu sunt apelate. Prin urmare, datele actualizate pot fi trecute în onStartCommand(...) prin intermediul Intent Bundle pentru a actualiza datele din Service.

Exemplu

Folosesc acest tipar pentru a implementa aplicația PlayerNotificationManager în Coinverse aplicația de știri despre criptomonede.

Activitate / 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)
}

KHEM RAJ MEENA

Adaug un cod în răspunsul lui @humazed. Deci, nu există în nici o notificare inițială. S-ar putea fi o soluție de lucru, dar funcționează pentru mine.

@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);
        }
}

Adăugam transparentColor în pictograma mică și culoarea pe notificare. va funcționa.

Pankaj Kant Patel

O problemă ar putea fi Clasa de servicii nu este activată în fișierul AndroidManifest. vă rugăm să verificați și acest lucru.

<service
        android_name=".AudioRecorderService"
        android_enabled="true"
        android_exported="false"
        android_foregroundServiceType="microphone" />

melianor

Am rezolvat problema cu pornirea serviciului cu startService(intent) în loc de Context.startForeground() și apelând startForegound() imediat după super.OnCreate(). În plus, dacă porniți serviciul la pornire, puteți porni Activity care să pornească serviciul la transmisia de pornire. Deși nu este o soluție permanentă, funcționează.

Comentarii

  • în unele dispozitive Xiaomi activitățile nu pot fi pornite din fundal –  > Por Mateen Chaudhry.
Shailesh

Tocmai am împărtășit recenzia mea despre acest lucru. Nu sunt sigur(100% spunând) că codul de mai sus nu funcționează pentru mine și pentru alți băieți, de asemenea, dar uneori am primit această problemă. Să presupunem că am rulat aplicația 10 timp, atunci ar putea fi primit această problemă 2 la 3 trei trei ori.

Am încercat mai sus toate răspunsurile, dar încă nu am rezolvat problema. Am implementat mai sus toate codurile și am testat în diferite niveluri API (API level 26, 28, 29) și în diferite dispozitive mobile (Samsung, Xiaomi, MIUI, Vivo, Moto, One Plus, Huawei, etc.) și am primit aceeași problemă de mai jos.

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

Am citit service pe site-ul web al dezvoltatorilor Google, pe alte bloguri și pe unele întrebări din stack overflow și am înțeles că această problemă se va întâmpla atunci când apelăm startForgroundSerivce() metoda, dar în acel moment serviciul nu a fost pornit.

În cazul meu, am oprit serviciul și după aceea am pornit imediat serviciul. Mai jos este indicatorul.

....//some other code
...// API level and other device auto star service condition is already set
stopService();
startService();
.....//some other code

În acest caz, serviciul nu este pornit din cauza vitezei de procesare și a memoriei reduse din RAM, dar startForegroundService() metoda este apelată și declanșează excepția.

Work for me:

new Handler().postDelayed(()->ContextCompat.startForegroundService(activity, new Intent(activity, ChatService.class)), 500);

Am modificat codul și am setat o întârziere de 500 de milisecunde pentru a apela metoda startService(), iar problema este rezolvată. Aceasta nu este o soluție perfectă, deoarece în acest fel performanța aplicației scade.

Note:Acest lucru este valabil numai pentru Prim-plan și fundal numai pentru serviciile de fundal și de fundal. Nu se testează atunci când se utilizează serviciul Bind.Vă împărtășesc acest lucru pentru că numai în acest mod am rezolvat această problemă.

Comentarii

  • Am încercat acest lucru pe aplicația mea (200k MAU) și încă primesc această eroare. Mai ales Samsung se pare că din anumite motive (poate statistice, dar idk). –  > Por MikkelT.
Malachiasz

Am avut o problemă în Pixel 3, Android 11, că atunci când serviciul meu funcționa foarte scurt, atunci notificarea din prim-plan nu era respinsă.

Adăugarea unei întârzieri de 100ms înainte de stopForeground() stopSelf() pare să ajute.

Oamenii scriu aici că stopForeground() ar trebui să fie apelat înainte de stopSelf(). Nu pot confirma, dar cred că nu se deranjează să facă asta.

public class AService extends Service {

@Override
public void onCreate() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        startForeground(
            getForegroundNotificationId(),
            channelManager.buildBackgroundInfoNotification(getNotificationTitle(), getNotificationText()),
            ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC);
    } else {
        startForeground(getForegroundNotificationId(),
            channelManager.buildBackgroundInfoNotification(getNotificationTitle(), getNotificationText())
        );
    }

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

    if (hasQueueMoreItems()) {
        startWorkerThreads();
    } else {
        stopForeground(true);
        stopSelf();
    }
    return START_STICKY;
}

private class WorkerRunnable implements Runnable {

    @Override
    public void run() {

        while (getItem() != null && !isLoopInterrupted) {
            doSomething(getItem())   
        }

        waitALittle();
        stopForeground(true);
        stopSelf();
    }

    private void waitALittle() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
}

Duna

Din documentele Google privind Android 12 modificări de comportament:

To provide a streamlined experience for short-running foreground services on Android 12, the system can delay the display of foreground service notifications by 10 seconds for certain foreground services. This change gives short-lived tasks a chance to complete before their notifications appear.

Soluție: Apelați startForeground() în onCreate() pentru serviciul pe care îl utilizați Context.startForegroundService().

JeanK

Serviciu

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)
        }

    }

}

Tester

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([email protected])
    TestService.stop([email protected])
}

Rezultat

D/TestService: onCreate
D/TestService: onStartCommand -> START
D/TestService: onStartCommand -> STOP
D/TestService: onDestroy