Zuerst habe ich mir diese angesehen;
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,
startForeground()
in onCreate()
Service.startForeground()
vor Context.stopService()
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?
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()
onCreate()
. offizielles DokumentService.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.
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 :))
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 Service
ContextCompat.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
Ich habe das Problem mit startForeground () in MediaSessionCompat.Callback-Methoden wie onPlay (), onPause () fast behoben.
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
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.