it-swarm.com.de

Context.startForegroundService () ruft dann nicht Service.startForeground () auf.

Ich verwende Service Class unter Android O OS. 

Ich plane die Service im Hintergrund zu verwenden. 

In der Android-Empfehlung heißt es, dass startService startForegroundService verwenden sollte.

Wenn Sie startForegroundService verwenden, löst Service einen Context.startForegroundService() aus und ruft dann nicht den Service.startForeground()-Fehler auf. 

Was ist daran falsch?

121
NiceGuy

In den Google-Dokumenten auf Verhaltensänderungen in Android 8.0 :

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

Lösung: Rufen Sie startForeground() in onCreate() für die Service auf, die Sie Context.startForegroundService() verwenden.

Siehe auch: Hintergrundausführungsgrenzen für Android 8.0 (Oreo)

63
zhuzhumouse

Ich rief ContextCompat.startForegroundService(this, intent) an, um den Dienst zu starten

In Betrieb 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);
        }
}
45
humazed

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

Dies ist definitiv ein Rahmenproblem, aber nicht alle Entwickler, die mit diesem Problem konfrontiert werden, geben ihr Bestes:

  1. startForeground Eine Benachrichtigung muss sowohl in onCreate als auch in OnStartCommand sein. Wenn Ihr Service bereits erstellt wurde und Ihre Aktivität versucht, ihn erneut zu starten, wird onCreate nicht aufgerufen.

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

  3. stopSelf darf nicht vor startForeground aufgerufen werden.

Bei allen über 3 kann dieses Problem zwar etwas reduziert werden, aber immer noch nicht behoben. Der eigentliche Fix oder eine Problemumgehung besteht darin, die Sdk-Zielversion auf 25 herunterzustufen.

Beachten Sie, dass Android P wahrscheinlich dieses Problem weiterhin hat, da Google sich weigert, zu verstehen, was los ist, und nicht der Meinung ist, dass dies ihre Schuld ist. Lesen Sie hier # 36: https://issuetracker.google.com/issues/76112072 .

19
ZhouX

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

Ich habe eine klare Reproduktion unter: https://github.com/paulpv/ForegroundServiceAPI26/blob/repro/app/src/main/Java/com/github/paulpv/foregroundserviceapi26/MainService.kt#L28

Ich habe einen Fehler in diesem Fall geöffnet: https://issuetracker.google.com/issues/76112072

Einige Fehler in diesem Bereich wurden geöffnet und geschlossen.

Hoffentlich machen meine mit klaren Repro-Schritten den Schnitt.

13
swooby

Ich habe dies ein paar Tage lang recherchiert und die Lösung gefunden ... Jetzt kann in Android O die Hintergrundbeschränkung wie folgt eingestellt werden

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 so 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
    }
}
8
Ahmad Arslan

Nur ein Heads-up, da ich zu viele Stunden damit verschwendet habe. Ich bekam diese Ausnahme immer wieder, obwohl ich startForeground(..) als erstes in onCreate(..) bezeichnet hatte. Am Ende habe ich festgestellt, dass das Problem durch die Verwendung von NOTIFICATION_ID = 0 verursacht wurde. Die Verwendung eines anderen Werts scheint das zu beheben.

6
tobalr

Ich habe ein widget , das relativ häufig Updates durchführt, wenn das Gerät wach ist und ich in nur wenigen Tagen Tausende Abstürze gesehen habe.

Der Problemauslöser

Ich habe das Problem sogar auf meinem Pixel 3 XL bemerkt, als ich gedacht hätte, dass das Gerät überhaupt nicht viel geladen hätte. Alle Codepfade wurden mit startForeground() abgedeckt. 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 angezeigt hat.

Die Problemumgehung/Lösung

Ich konnte alle Abstürze loswerden. Ich habe den Aufruf von stopSelf() aufgehoben.(Ich dachte darüber nach, den Stopp zu verschieben, bis ich ziemlich sicher war, dass die Benachrichtigung angezeigt wurde, aber ich weiß nicht Sie möchten, dass der Benutzer die Benachrichtigung sieht, wenn dies nicht erforderlich ist.) Wenn der Dienst eine Minute lang inaktiv war oder das System ihn normalerweise zerstört, ohne Ausnahmen auszulösen.

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

Dieser Fehler tritt auch bei Android 8+ auf, wenn Service.startForeground (int id, Benachrichtigungsbenachrichtigung) aufgerufen wird, während id auf 0 gesetzt ist.

id int: Der Bezeichner für diese Benachrichtigung gemäß NotificationManager.notify (int, Notification). darf nicht 0 sein.

4
almisoft

Ich habe eine Lösung für dieses Problem. Ich habe diesen Fix in meiner eigenen App (300K + DAU) überprüft, die mindestens 95% dieser Art von Absturz reduzieren kann, aber dieses Problem nicht zu 100% vermeiden kann.

Dieses Problem tritt auch dann auf, wenn Sie den Start von startForeground () direkt nach dem Start des Dienstes als von Google dokumentiert aufrufen. Dies kann darauf zurückzuführen sein, dass der Serviceerstellungs- und -initialisierungsprozess in vielen Szenarien bereits mehr als 5 Sekunden kostet. Unabhängig davon, wann und wo Sie die Methode startForeground () aufrufen, ist dieser Absturz unvermeidlich.

Meine Lösung besteht darin, sicherzustellen, dass startForeground () innerhalb von 5 Sekunden nach der startForegroundService () - Methode 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 gewartet. Hier ist der Code, mein Beispielservice 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 Codefragment für Schritt 1 in einer ausstehenden Absicht ausführen möchten, z. B. einen Vordergrunddienst in einem Widget starten möchten (ein Klick auf die Widget-Schaltfläche), ohne Ihre App zu öffnen, können Sie das Codefragment in einen Broadcast-Empfänger einschließen , und lösen Sie ein Broadcast-Ereignis aus, anstatt den Befehl zum Starten des Dienstes auszuführen.

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

4
Hexise

Problem mit der Android O-API 26

Wenn Sie den Dienst sofort beenden (damit Ihr Dienst nicht wirklich läuft (Wortlaut/Verstehen) und Sie sich weit unter dem ANR-Intervall befinden, müssen Sie noch vor startSelf startForeground aufrufen

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

Versuchte diesen Ansatz, aber es erzeugt immer noch einen Fehler: -

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

Ich verwende dies, bis der Fehler behoben ist 

mContext.startService(playIntent);
3
Andy Cass

So viele Antworten, aber keine funktionierte in meinem Fall.

Ich habe den Dienst so begonnen.

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

Und in meinem Service bei 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);
    }

Vergessen Sie nicht, NOTIFICATION_ID auf Null zu setzen

private static final String Android_CHANNEL_ID = "com.xxxx.Location.Channel"; privates statisches final int NOTIFICATION_ID = 555;

SO war alles perfekt aber stürzte immer noch auf 8.1 ab, also war es wie unten.

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

Ich habe Stoppvordergrund mit der Benachrichtigung "Entfernen" aufgerufen, aber sobald der Benachrichtigungsdienst entfernt wurde, kann der Hintergrunddienst aktiviert werden, und der Hintergrunddienst kann nicht in Android O aus dem Hintergrund ausgeführt werden. start nach Push erhalten.

So magisch ist das Wort

  stopSelf();

Soweit also der Grund, warum Ihr Service abstürzt, befolgen Sie alle oben genannten Schritte und genießen Sie es. 

3
sandy

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

Ähnlich wie startService (Intent), jedoch mit dem impliziten Versprechen, dass die Der Dienst ruft einmal startForeground (int, Android.app.Notification) auf es beginnt zu laufen. Der Service wird mit einer vergleichbaren Zeit versehen auf das ANR-Intervall, um dies zu tun, andernfalls wird das System Beenden Sie den Dienst automatisch und erklären Sie die App als ANR.

Im Gegensatz zum normalen startService (Intent) kann diese Methode unter .__ verwendet werden. zu jeder Zeit, unabhängig davon, ob sich die App, die den Dienst hostet, in einem .__ befindet. Vordergrundzustand.

stellen Sie sicher, dass Sie Service.startForeground(int, Android.app.Notification) auf onCreate () aufrufen, um sicherzustellen, dass es aufgerufen wird. Wenn Sie eine Bedingung haben, die Sie möglicherweise daran hindert, sollten Sie die normale Context.startService(Intent) verwenden und die Service.startForeground(int, Android.app.Notification) selbst aufrufen.

Es scheint, dass die Context.startForegroundService() einen Watchdog hinzufügt, um sicherzustellen, dass Sie die Service.startForeground(int, Android.app.Notification) aufgerufen haben, bevor sie zerstört wurde ...

3

Ich füge einen Code in @humazed answer hinzu. Also gibt es keine Erstmeldung. 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 kleinen Symbolen und Farben bei Benachrichtigung hinzu. Es wird klappen.

2
KHEM RAJ MEENA

Ich bin mit demselben Problem konfrontiert und nachdem ich einige Zeit gefunden habe, können Sie unter dem Code versuchen. Wenn Sie Service verwenden, geben Sie diesen Code in onCreate ein. Andernfalls verwenden Sie Intent Service, und geben Sie diesen Code in onHandleIntent ein.

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);
    }
2
Sambhaji Karad

Selbst nach dem Aufruf von startForeground in Service stürzt es auf einigen Geräten ab, wenn wir stopService aufrufen, kurz bevor onCreate aufgerufen wird ..__ Ich habe dieses Problem behoben, indem ich den Dienst mit einem zusätzlichen Flag startete:

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 er tatsächlich gestoppt wurde:

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

    //call startForeground first
    boolean stopService = false;
    if (intent != null) {
        stopService = intent.getBooleanExtra("request_stop", false);
    }
    if (stopService) {
        stopSelf();
        return START_STICKY;
    }
    //Continue with the background task
    return START_STICKY;
}

P.S. Wenn der Dienst nicht tatsächlich ausgeführt wurde, wird der Dienst zuerst gestartet, was ein Overhead ist.

1
Gaurav Singla

Ich überprüfe nur die Variable PendingIntent oder noch bevor ich die. context.startForegroundService(service_intent) Funktion.

das funktioniert für mich

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

Ich habe dieses Problem recherchiert und das ist, was ich bisher entdeckt habe. Dieser Absturz kann auftreten, wenn der folgende Code ähnlich ist:

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 Codeblock 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 onCreate() von MyForegroundService ausgeführt, da Android die Erstellung des Diensts im Haupt-Thread-Handler plant, jedoch bringDownServiceLocked für eine BinderThread aufgerufen wird, bei der es sich um eine Race-Bedingung handelt. Das bedeutet, dass MyForegroundService keine Chance hatte, startForeground aufzurufen, wodurch der Absturz verursacht wird.

Um dies zu beheben, müssen wir sicherstellen, dass bringDownServiceLocked nicht vor onCreate() von MyForegroundService aufgerufen wird.

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 Sticky Broadcasts stellen wir sicher, dass die Broadcasts nicht verloren gehen und stopReceiver die Stop-Absicht erhält, sobald sie in onCreate() von MyForegroundService registriert wurde. Zu diesem Zeitpunkt haben wir startForeground(...) bereits aufgerufen. Wir müssen auch diese störende Sendung entfernen, um zu verhindern, dass stopReceiver das nächste Mal benachrichtigt wird.

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

1
makovkastar

Rufen Sie bitte keine StartForgroundServices in onCreate () - Methode auf. Sie müssen die StartForground-Dienste in onStartCommand () aufrufen, nachdem der Worker-Thread erstellt wurde. Andernfalls erhalten Sie immer ANR. Schreiben Sie also keine komplexen login im Haupt-Thread 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);

    }
}
1
Shalu Gupta

für mich fügte ich Vordergrunddienst in Manifest in hinzu

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

wenn es funktioniert, Kommentar unten, lass es mich wissen

0
Arun Abimanyu

Ok, etwas, das mir aufgefallen ist und das vielleicht auch ein paar anderen helfen könnte. Dies ist ausschließlich ein Test, um zu sehen, ob ich herausfinden kann, wie die Vorkommnisse, die ich sehe, behoben werden können. Nehmen wir der Einfachheit halber an, ich habe eine Methode, die dies vom Präsentator 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 ist kein onCreate() im Dienst vorhanden.

Selbst wenn Sie die Benutzeroberfläche außerhalb des Hauptthreads aktualisieren, startet sie nicht rechtzeitig und gibt Ihnen den gefürchteten Vordergrundfehler. In meinem Fall haben wir einige Dinge in eine Warteschlange geladen und jedes hat startForegroundService aufgerufen, aber im Hintergrund war mit jedem eine Logik verbunden. Wenn die Logik zu lange gebraucht hat, um diese Methode zu beenden, da sie nacheinander aufgerufen wurden, wird die Absturzzeit überschritten. Das alte startService ignorierte es einfach und machte sich auf den Weg und da wir es jedes Mal anriefen, würde die nächste Runde enden.

Ich fragte mich, ob der Dienst, wenn er von einem Thread im Hintergrund aufgerufen wurde, beim Start nicht vollständig gebunden und sofort ausgeführt werden konnte, und begann zu experimentieren. Auch wenn dies NICHT sofort startet, 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 es zwingt, zu warten, bis der Haupt-Thread es rechtzeitig verarbeiten kann. Ich weiß, dass es nicht ideal ist, es mit dem Haupt-Thread zu verknüpfen, aber da meine Verwendung es im Hintergrund aufruft, bin ich nicht wirklich besorgt, wenn es wartet, bis es abgeschlossen ist, anstatt abzustürzen.

0
a54studio

rufen Sie einfach die Methode startForeground direkt 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());
    }
}
0
Hossein Karami

Ich habe das Problem beim Starten des Dienstes mit startService(intent) anstelle von Context.startForeground() behoben und startForegound() unmittelbar nach super.OnCreate() aufgerufen. Wenn Sie den Dienst beim Booten starten, können Sie außerdem die Aktivität starten, die den Dienst beim Boot-Broadcast startet. Obwohl es keine dauerhafte Lösung ist, funktioniert es.

0
mystogan

Ich weiß, dass es eine späte Antwort ist, aber ich denke, es könnte in Zukunft helfen. Ich habe einfach JobIntentService anstelle von IntentService verwendet, das seinen JobScheduler enthält und alles für mich erledigt. schau dir das an - Beispiel

0
raed

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

Android führt viele Prozesse gleichzeitig aus und es gibt keine Garantie dafür, dass Looper Ihren Zieldienst, der startForeground () aufrufen soll, innerhalb von 5 Sekunden aufruft. Wenn Ihr Zieldienst den Anruf nicht innerhalb von 5 Sekunden erhalten hat, haben Sie Pech und Ihre Benutzer werden unter einer ANR-Situation leiden. In Ihrem Stack-Trace 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 fehlerfrei, Entwickler und Benutzer jedoch nicht. Warum sollten sie sich für die beiden letzteren interessieren, da Googles Verantwortlichkeiten auf das System beschränkt sind? Anscheinend nicht. Könnten sie es besser machen? Natürlich, z.B. Sie hätten auch den Dialog "Anwendung ist beschäftigt" anzeigen können, in dem ein Benutzer aufgefordert wurde, eine Entscheidung über das Warten oder Beenden der App zu treffen. Die Hauptsache ist, dass das System jetzt gesund ist.

Nach meinen Beobachtungen passiert dies relativ selten, 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. Wenn der Dienst dann bereit ist, verarbeiten Sie onServiceConnected, aber auch dies bedeutet, dass überhaupt keine startForegroundService-Aufrufe verwendet werden.

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

Das qs bleibt immer noch: Was soll man stattdessen verwenden? 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 eine Wakelock-Funktion für Ihre App bereit. Aus diesem Grund müssen Sie keine Maßnahmen ergreifen, um zu gewährleisten, 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 sich deshalb nicht von den Vordergrunddiensten unterscheiden. Ab der Implementierung ist PoV JobScheduler nicht Ihr Service, es ist ein System, und vermutlich wird es die Warteschlange richtig behandeln, und Google wird niemals sein eigenes Kind kündigen :)

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 Haupt-Thread gesendet werden, ist dies möglich. Sie sollten sich jedoch daran erinnern, dass der Job auf dem Hauptthread ausgeführt wird und alle wichtigen Dinge auf Threads und asynchrone Aufgaben verlagert werden.

Dieser Dienst führt jeden eingehenden Auftrag 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 verlagern müssen

Die einzige Gefahr beim Wechsel zu JobScheduler/JobService besteht darin, dass Sie alten Code überarbeiten müssen, und das macht keinen Spaß. Ich habe in den letzten zwei Tagen genau das getan, um die SAP-Implementierung des neuen Samsung zu nutzen. Ich schaue mir meine Absturzberichte an und lasse Sie wissen, ob die Abstürze erneut auftreten. Theoretisch sollte es nicht passieren, aber es gibt immer Details, die uns möglicherweise nicht bekannt sind.

0
Oleg Gryb

Da jeder Besucher hier das gleiche leidet, möchte ich meine Lösung mitteilen, die noch niemand zuvor versucht hat (in dieser Frage jedenfalls). Ich kann Ihnen versichern, dass es funktioniert, auch auf einem angehaltenen Haltepunkt was diese Methode bestätigt.

Die Ausgabe ist, Service.startForeground(id, notification) vom Service selbst aufzurufen, recht? Android Framework garantiert leider nicht, Service.startForeground(id, notification) innerhalb von Service.onCreate() in 5 Sekunden aufzurufen, löst aber trotzdem die Ausnahme aus, daher habe ich mir diesen Weg ausgedacht.

  1. Binden Sie den Dienst an einen Kontext mit einem Ordner aus dem Dienst, bevor Sie Context.startForegroundService() aufrufen.
  2. Wenn die Bindung erfolgreich ist, rufen Sie Context.startForegroundService()von der Dienstverbindung auf und rufen Sie sofort Service.startForeground()innerhalb der Dienstverbindung auf
  3. WICHTIGER HINWEIS: Rufen Sie die Context.bindService() -Methode in ein Try-Catch auf, da der Aufruf in einigen Fällen eine Ausnahme auslösen kann. In diesem Fall müssen Sie sich auf den Aufruf verlassen Context.startForegroundService() direkt und hoffe, es wird nicht scheitern. Ein Beispiel kann ein Rundfunkempfängerkontext sein. Das Abrufen des Anwendungskontexts löst in diesem Fall jedoch keine Ausnahme aus, sondern das direkte Verwenden des Kontexts.

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 innerhalb von 5 Sekunden keine zwei Codezeilen ausführen kann, müssen Sie diese in den Papierkorb 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 eine Sammelmappe, die 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 Sie, den Dienst zu binden aus diesem Kontext. Wenn dies erfolgreich ist, wird die Methode ServiceConnection.onServiceConnected() von der von Ihnen verwendeten Dienstverbindung aufgerufen. Behandeln Sie dann die Logik in dem Code, der oben gezeigt wird. Ein Beispielcode würde so 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 von mir entwickelten Anwendungen zu arbeiten, also sollte es auch funktionieren, wenn Sie es versuchen.

0
Furkan Yurdakul

Aktualisieren von Daten in onStartCommand(...)

onBind (...)

onBind(...) ist ein besseres Lebenszyklusereignis zum Initiieren von startForeground im Vergleich zu onCreate(...), da onBind(...) eine Intent übergibt, die möglicherweise wichtige Daten in der Bundle enthält, die zum Initialisieren der Service erforderlich sind. Dies ist jedoch nicht erforderlich, da onStartCommand(...) aufgerufen wird, wenn die Service zum ersten Mal erstellt oder später aufgerufen wird.

onStartCommand (...)

startForeground in onStartCommand(...) ist wichtig, um die Service zu aktualisieren, sobald sie bereits erstellt wurde.

Wenn ContextCompat.startForegroundService(...) aufgerufen wird, nachdem eine Service erstellt wurde, werden onBind(...) und onCreate(...) nicht aufgerufen. Daher können aktualisierte Daten über die IntentBundle an onStartCommand(...) übergeben werden, um Daten in der Service zu aktualisieren.

Probe

Ich verwende dieses Muster, um die PlayerNotificationManager in der Nachrichten-App Coinverse cryptocurrency zu implementieren.

Activity/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)
}
0
Adam Hurwitz