wake-up-neo.com

Android 9 (Pie), Context.startForegroundService () hat dann Service.startForeground (): ServiceRecord nicht aufgerufen

Zuerst habe ich mir diese angesehen;

(enter image description here

Ich habe eine Streaming-Anwendung, die von fast einer Million Menschen verwendet wird. Ich verwende den Vordergrunddienst für den Player. Ich habe MediaSession noch nicht implementiert. Ich habe eine sturzfreie Sitzung von 99,95%. Diese App funktioniert also auf allen Versionen, aber ich habe angefangen, Absturzberichte (ANR) mit Android 9. Dieser Absturz tritt nur bei Samsung Telefonen auf, insbesondere bei s9, s9+, s10, s10+, note9 - Modellen.

Ich habe es versucht,

  • Aufruf der Methode startForeground() in onCreate()
  • Aufruf von Service.startForeground() vor Context.stopService()
  • Andere Stackoverflow-Antworten auf ähnliche Fragen

Ich habe einige Kommentare von Entwicklern von Google gelesen, sie sagten, es sei nur Intended Behavior. Ich frage mich, ob es vom Samsung-System oder Android OS) aufgetreten ist. Hat jemand eine Meinung dazu? Wie kann ich das beheben?

16
Beyazid

Ich habe auf meinen Absturzbericht gewartet, um die Lösung zu teilen. Ich habe fast 20 Tage lang keinen Absturz oder ANR bekommen. Ich möchte meine Lösung teilen. Es kann denen helfen, die auf dieses Problem stoßen.

In der Methode onCreate()

  • Zunächst einmal ist meine App eine Medienanwendung. Ich habe die Mediasession noch nicht implementiert. Ich erstelle einen Benachrichtigungskanal oben in onCreate(). offizielles Dokument
  • Ich rufe die Methode Service.startForeground() nach der Methode Context.startForegroundService() auf. In meiner prepareAndStartForeground() Methode.

    Hinweis: Ich weiß nicht warum, aber ContextCompat.startForegroundService () funktioniert nicht richtig.

Aus diesem Grund habe ich meiner Serviceklasse manuell dieselbe Funktion hinzugefügt, anstatt ContextCompat.startForegroundService() aufzurufen

private fun startForegroundService(intent: Intent) {
    if (Build.VERSION.SDK_INT >= 26) {
        context.startForegroundService(intent)
    } else {
        // Pre-O behavior.
        context.startService(intent)
    }
}

prepareAndStartForeground() Methode

private fun prepareAndStartForeground() {
    try {
        val intent = Intent(ctx, MusicService::class.Java)
        startForegroundService(intent)

        val n = mNotificationBuilder.build()
        // do sth
        startForeground(Define.NOTIFICATION_ID, n)
    } catch (e: Exception) {
        Log.e(TAG, "startForegroundNotification: " + e.message)
    }
}

Es ist meine onCreate()

override fun onCreate() {
    super.onCreate()
    createNotificationChannel()
    prepareAndStartForeground()
}

Meine onStartCommand()

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    if (intent == null) {
        return START_STICKY_COMPATIBILITY
    } else {
        //....
        //...
    }
    return START_STICKY
}

onRebind, onBind, onUnbind Methoden wie diese

internal var binder: IBinder? = null

override fun onRebind(intent: Intent) {
    stopForeground(true) // <- remove notification
}

override fun onBind(intent: Intent): IBinder? {
    stopForeground(true) // <- remove notification
    return binder
}

override fun onUnbind(intent: Intent): Boolean {
    prepareAndStartForeground() // <- show notification again
    return true
}

Wir müssen etwas löschen, wenn onDestroy () aufruft

   override fun onDestroy() {
    super.onDestroy()
    releaseService()
   }

private fun releaseService() {
    stopMedia()
    stopTimer()
    // sth like these
    player = null
    mContext = null
    afChangeListener = null
    mAudioBecomingNoisy = null
    handler = null
    mNotificationBuilder = null
    mNotificationManager = null
    mInstance = null
}

Ich hoffe, diese Lösung funktioniert richtig für Sie.

4
Beyazid

Nach zu viel Mühe mit diesem Absturz habe ich diese Ausnahme vollständig behoben und die Lösung gefunden.

Stellen Sie sicher, dass Sie diese Dinge in Ihrem Dienst getan haben. Ich liste sie wie folgt auf: (Einige dieser Dinge wiederholen sich, wie in einer anderen Antwort erwähnt. Ich schreibe sie einfach noch einmal.).

1- Rufen Sie an

startForeground ()

sowohl in onCreate als auch in onStartCommand (es ist in Ordnung, startForeground () aufzurufen. viele Male)

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

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    if (intent == null) {
        return START_NOT_STICKY;
    }
    final int command = intent.getIntExtra(MAIN_SERVICE_COMMAND_KEY, -1);

    if (command == MAIN_SERVICE_START_COMMAND) {
        startCommand();
        return START_STICKY;
    }
    return START_NOT_STICKY;
}

 private void startCommand() {
    createNotificationAndStartForeground();
    runningStatus.set(STARTED);
}

2- Beenden Sie Ihren Dienst mit

context.stopService ()

Es ist nicht erforderlich, stopForeground () oder stopSelf () aufzurufen. .

  try {
        context.stopService(
                new Intent(
                        context,
                        NavigationService.class
                )
        );
    } catch (Exception ex) {
        Crashlytics.logException(ex);
        LogManager.e("Service manager can't stop service ", ex);
    }

3- Starten Sie Ihren Dienst mit

ContextCompat.startForegroundService ()

es werden verschiedene API-Versionen verarbeitet.

   ContextCompat.startForegroundService(
            context,
            NavigationService.getStartIntent(context)
    );

4- Wenn Ihr Dienst über Aktionen verfügt (ausstehende Absichten erforderlich sind), behandeln Sie Ihre ausstehenden Absichten mit einem Rundfunkempfänger Anstelle Ihres aktuellen Dienstes (er ruft Ihren Dienst bei Create () auf und kann gefährlich sein oder PendingIntent.FLAG_NO_CREATE verwenden) empfiehlt es sich, einen bestimmten Broadcast-Empfänger für die Verarbeitung Ihrer Dienstbenachrichtigungsaktionen zu haben Ich meine, erstellen Sie alle ausstehenden Absichten mit PendingIntent.getBroadcast () .

    private PendingIntent getStopActionPendingIntent() {
    final Intent stopNotificationIntent = getBroadCastIntent();

    stopNotificationIntent.setAction(BROADCAST_STOP_SERVICE);

    return getPendingIntent(stopNotificationIntent);
}

private PendingIntent getPendingIntent(final Intent intent) {
    return PendingIntent.getBroadcast(
            this,
            0,
            intent,
            0
    );
}

new NotificationCompat.Builder(this, CHANNEL_ID)
            .addAction(
                    new NotificationCompat.Action(
                            R.drawable.notification,
                            getString(R.string.switch_off),
                            getStopActionPendingIntent()
                    )
            )

5- Immer bevor Ihr Dienst beendet wird , stellen Sie sicher, dass Ihr Dienst erstellt und gestartet wurde (I Erstellen Sie eine globale Klasse mit meinem Dienststatus).

  if (navigationServiceStatus == STARTED) {
            serviceManager.stopNavigationService();
        }

6- Setzen Sie Ihre Benachrichtigungs-ID auf eine lange Zahl wie 121412.

7- Mit NotificationCompat.Builder werden verschiedene API-Versionen verarbeitet. Sie müssen lediglich einen Benachrichtigungskanal für Build-Versionen erstellen> = Build.VERSION_CODES.O. (Diese ist keine Lösung, machen Sie einfach Ihren Code lesbarer)

8- Hinzufügen

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

Erlaubnis zu Ihrem Manifest. (Dieser wird in Android docs) Android Foreground Service erwähnt

ich hoffe es hilft :))

5
Sepehr

Die Komponente Android Service ist etwas schwierig, um ordnungsgemäß zu funktionieren, insbesondere bei späteren Android -Versionen, bei denen das Betriebssystem zusätzliche Einschränkungen hinzufügt. Wie bereits erwähnt Verwenden Sie in anderen Antworten beim Starten von ServiceContextCompat.startForegroundService(). Rufen Sie anschließend in Service.onStartCommand() sofort startForeground() auf. Speichern Sie Notification Sie möchten als Mitgliedsfeld anzeigen und dieses verwenden, es sei denn, es ist null. Beispiel:

private var notification:Notification? = null
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    if (notification == null) {
        notification = createDefaultNotification()
    }
    startForeground(NOTIFICATION_ID, notification)

    // Do any additional setup and work herre

    return START_STICKY
}

Immer zurückgeben START_STICKY in Ihrem Service. Alles andere ist wahrscheinlich das Falsche, besonders wenn Sie einen Audio-Player jeglicher Art verwenden. Wenn Sie einen Audio-Player verwenden, sollten Sie Ihren eigenen Dienst nicht implementieren, sondern stattdessen MediaBrowserServiceCompat (von AndroidX) verwenden.

Ich empfehle auch die Blog-Beiträge, die ich dazu geschrieben habe: https://hellsoft.se/how-to-service-on-Android-part-3-1e24113152cd

1
Erik Hellman

Ich habe das Problem mit startForeground () in MediaSessionCompat.Callback-Methoden wie onPlay (), onPause () fast behoben.

0

Nachdem ich das gleiche Problem mit den gleichen Telefonen hatte, habe ich nur wenige Änderungen vorgenommen und die Abstürze waren verschwunden. Ich bin nicht sicher, was den Trick gemacht hat, aber ich vermute, dass der Aufruf von startForeground sowohl in onCreate als auch in onStartCommand erfolgt. Ich bin nicht sicher, warum dies erforderlich ist, wenn der Dienst bereits gestartet wurde und in onCreate alles ordnungsgemäß aufgerufen wurde.

Weitere Änderungen: - Ändern der serviceId auf eine niedrige Nummer (1-10) - Aufrufen von startFororegroundService seltener über eine Singleton-Synchronklasse (dies wurde zuvor bei den Abstürzen implementiert, um zu verhindern, dass der Dienst vor dem Start mit einem Rückruf von onStartCommand gestoppt wurde, jetzt jedoch auch filtert Anrufe, wenn der Dienst bereits gestartet wurde). - Verwenden von START_REDELIVER_INTENT (sollte nichts beeinflussen)

Das Problem ist auf den genannten Telefonen nur für einige Benutzer aufgetreten, daher vermute ich, dass es mit einem neuen Update von Samsung zusammenhängt und irgendwann behoben wird

0
Michal Polivka

Gemäß dieser Fehlermeldung heißt es, wenn Sie Context.startForegroundService () aufrufen, müssen Sie eine Benachrichtigung mit der Service.startForeground () -Methode ausgeben. Das verstehe ich.

0
user1090751